460b9539 |
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" |
ce6c36be |
39 | #include "wav.h" |
460b9539 |
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) { |
ce6c36be |
99 | struct wavfile f[1]; |
100 | int err, sample_frame_size; |
101 | long duration; |
460b9539 |
102 | |
ce6c36be |
103 | if((err = wav_init(f, path))) { |
104 | disorder_error(err, "error opening %s", path); |
105 | return -1; |
460b9539 |
106 | } |
ce6c36be |
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); |
460b9539 |
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 | */ |