chiark / gitweb /
darwin libiconv
[disorder] / plugins / tracklength.c
1 /*
2  * This file is part of DisOrder.
3  * Portions copyright (C) 2004, 2005 Richard Kettlewell (see also below)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18  * USA
19  */
20
21 #include <config.h>
22
23 #include <string.h>
24 #include <stdio.h>
25 #include <math.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/mman.h>
31 #include <errno.h>
32
33 #include <vorbis/vorbisfile.h>
34 #include <mad.h>
35
36 #include <disorder.h>
37
38 #include "madshim.h"
39 #include "wav.h"
40
41 static void *mmap_file(const char *path, size_t *lengthp) {
42   int fd;
43   void *base;
44   struct stat sb;
45   
46   if((fd = open(path, O_RDONLY)) < 0) {
47     disorder_error(errno, "error opening %s", path);
48     return 0;
49   }
50   if(fstat(fd, &sb) < 0) {
51     disorder_error(errno, "error calling stat on %s", path);
52     goto error;
53   }
54   if(sb.st_size == 0)                   /* can't map 0-length files */
55     goto error;
56   if((base = mmap(0, sb.st_size, PROT_READ,
57                   MAP_SHARED, fd, 0)) == (void *)-1) {
58     disorder_error(errno, "error calling mmap on %s", path);
59     goto error;
60   }
61   *lengthp = sb.st_size;
62   close(fd);
63   return base;
64 error:
65   close(fd);
66   return 0;
67 }
68
69 static long tl_mp3(const char *path) {
70   size_t length;
71   void *base;
72   buffer b;
73
74   if(!(base = mmap_file(path, &length))) return -1;
75   b.duration = mad_timer_zero;
76   scan_mp3(base, length, &b);
77   munmap(base, length);
78   return b.duration.seconds + !!b.duration.fraction;
79 }
80
81 static long tl_ogg(const char *path) {
82   OggVorbis_File vf;
83   FILE *fp = 0;
84   double length;
85
86   if(!path) goto error;
87   if(!(fp = fopen(path, "rb"))) goto error;
88   if(ov_open(fp, &vf, 0, 0)) goto error;
89   fp = 0;
90   length = ov_time_total(&vf, -1);
91   ov_clear(&vf);
92   return ceil(length);
93 error:
94   if(fp) fclose(fp);
95   return -1;
96 }
97
98 static long tl_wav(const char *path) {
99   struct wavfile f[1];
100   int err, sample_frame_size;
101   long duration;
102
103   if((err = wav_init(f, path))) {
104     disorder_error(err, "error opening %s", path); 
105     return -1;
106   }
107   sample_frame_size = (f->bits + 7) / 8 * f->channels;
108   if(sample_frame_size) {
109     const long long n_samples = f->datasize / sample_frame_size;
110     duration = (n_samples + f->rate - 1) / f->rate;
111   } else
112     duration = -1;
113   wav_destroy(f);
114   return duration;
115 }
116
117 static const struct {
118   const char *ext;
119   long (*fn)(const char *path);
120 } file_formats[] = {
121   { ".MP3", tl_mp3 },
122   { ".OGG", tl_ogg },
123   { ".WAV", tl_wav },
124   { ".mp3", tl_mp3 },
125   { ".ogg", tl_ogg },
126   { ".wav", tl_wav }
127 };
128 #define N_FILE_FORMATS (int)(sizeof file_formats / sizeof *file_formats)
129
130 long disorder_tracklength(const char attribute((unused)) *track,
131                           const char *path) {
132   const char *ext = strrchr(path, '.');
133   int l, r, m = 0, c = 0;               /* quieten compiler */
134
135   if(ext) {
136     l = 0;
137     r = N_FILE_FORMATS - 1;
138     while(l <= r && (c = strcmp(ext, file_formats[m = (l + r) / 2].ext)))
139       if(c < 0)
140         r = m - 1;
141       else
142         l = m + 1;
143     if(!c)
144       return file_formats[m].fn(path);
145   }
146   return 0;
147 }
148
149 /*
150 Local Variables:
151 c-basic-offset:2
152 comment-column:40
153 fill-column:79
154 End:
155 */