Commit | Line | Data |
---|---|---|
f8f8039f RK |
1 | /* |
2 | * This file is part of DisOrder | |
3 | * Copyright (C) 2007 Richard Kettlewell | |
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 | /** @file server/decode.c | |
21 | * @brief General-purpose decoder for use by speaker process | |
22 | */ | |
23 | ||
24 | #include <config.h> | |
25 | #include "types.h" | |
26 | ||
27 | #include <getopt.h> | |
28 | #include <unistd.h> | |
29 | #include <fcntl.h> | |
30 | #include <errno.h> | |
31 | #include <stdlib.h> | |
32 | #include <locale.h> | |
33 | #include <assert.h> | |
34 | #include <fnmatch.h> | |
35 | #include <mad.h> | |
36 | #include <ao/ao.h> | |
37 | ||
38 | #include "log.h" | |
39 | #include "syscalls.h" | |
40 | #include "defs.h" | |
41 | ||
42 | /** @brief Encoding lookup table type */ | |
43 | struct decoder { | |
44 | /** @brief Glob pattern matching file */ | |
45 | const char *pattern; | |
46 | /** @brief Decoder function */ | |
47 | void (*decode)(void); | |
48 | }; | |
49 | ||
50 | /** @brief Input file */ | |
51 | static int inputfd; | |
52 | ||
53 | /** @brief Output file */ | |
54 | static FILE *outputfp; | |
55 | ||
56 | /** @brief Filename */ | |
57 | static const char *path; | |
58 | ||
59 | /** @brief Input buffer */ | |
60 | static unsigned char buffer[1048576]; | |
61 | ||
62 | /** @brief Open the input file */ | |
63 | static void open_input(void) { | |
64 | if((inputfd = open(path, O_RDONLY)) < 0) | |
65 | fatal(errno, "opening %s", path); | |
66 | } | |
67 | ||
68 | /** @brief Fill the buffer | |
69 | * @return Number of bytes read | |
70 | */ | |
71 | static size_t fill(void) { | |
72 | int n = read(inputfd, buffer, sizeof buffer); | |
73 | ||
74 | if(n < 0) | |
75 | fatal(errno, "reading from %s", path); | |
76 | return n; | |
77 | } | |
78 | ||
79 | /** @brief Write a 16-bit word in bigendian format */ | |
80 | static inline void output_16(uint16_t n) { | |
81 | if(putc(n >> 8, outputfp) < 0 | |
82 | || putc(n & 0xFF, outputfp) < 0) | |
83 | fatal(errno, "decoding %s: output error", path); | |
84 | } | |
85 | ||
86 | /** @brief Write the header | |
87 | * If called more than once, either does nothing (if you kept the same | |
88 | * output encoding) or fails (if you changed it). | |
89 | */ | |
90 | static void output_header(int rate, | |
91 | int channels, | |
92 | int bits) { | |
93 | static int already_written_header; | |
94 | struct ao_sample_format format; | |
95 | ||
96 | if(!already_written_header) { | |
97 | format.rate = rate; | |
98 | format.bits = bits; | |
99 | format.channels = channels; | |
100 | format.byte_format = AO_FMT_BIG; | |
101 | if(fwrite(&format, sizeof format, 1, outputfp) < 1) | |
102 | fatal(errno, "decoding %s: writing format header", path); | |
103 | already_written_header = 1; | |
104 | } | |
105 | } | |
106 | ||
107 | /** @brief Dithering state | |
108 | * Filched from mpg321, which credits it to Robert Leslie */ | |
109 | struct audio_dither { | |
110 | mad_fixed_t error[3]; | |
111 | mad_fixed_t random; | |
112 | }; | |
113 | ||
114 | /** @brief 32-bit PRNG | |
115 | * Filched from mpg321, which credits it to Robert Leslie */ | |
116 | static inline unsigned long prng(unsigned long state) | |
117 | { | |
118 | return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; | |
119 | } | |
120 | ||
121 | /** @brief Generic linear sample quantize and dither routine | |
122 | * Filched from mpg321, which credits it to Robert Leslie */ | |
123 | #define bits 16 | |
124 | static long audio_linear_dither(mad_fixed_t sample, | |
125 | struct audio_dither *dither) { | |
126 | unsigned int scalebits; | |
127 | mad_fixed_t output, mask, rnd; | |
128 | ||
129 | enum { | |
130 | MIN = -MAD_F_ONE, | |
131 | MAX = MAD_F_ONE - 1 | |
132 | }; | |
133 | ||
134 | /* noise shape */ | |
135 | sample += dither->error[0] - dither->error[1] + dither->error[2]; | |
136 | ||
137 | dither->error[2] = dither->error[1]; | |
138 | dither->error[1] = dither->error[0] / 2; | |
139 | ||
140 | /* bias */ | |
141 | output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1)); | |
142 | ||
143 | scalebits = MAD_F_FRACBITS + 1 - bits; | |
144 | mask = (1L << scalebits) - 1; | |
145 | ||
146 | /* dither */ | |
147 | rnd = prng(dither->random); | |
148 | output += (rnd & mask) - (dither->random & mask); | |
149 | ||
150 | dither->random = rnd; | |
151 | ||
152 | /* clip */ | |
153 | if (output > MAX) { | |
154 | output = MAX; | |
155 | ||
156 | if (sample > MAX) | |
157 | sample = MAX; | |
158 | } | |
159 | else if (output < MIN) { | |
160 | output = MIN; | |
161 | ||
162 | if (sample < MIN) | |
163 | sample = MIN; | |
164 | } | |
165 | ||
166 | /* quantize */ | |
167 | output &= ~mask; | |
168 | ||
169 | /* error feedback */ | |
170 | dither->error[0] = sample - output; | |
171 | ||
172 | /* scale */ | |
173 | return output >> scalebits; | |
174 | } | |
175 | #undef bits | |
176 | ||
177 | /** @brief MP3 output callback */ | |
178 | static enum mad_flow mp3_output(void attribute((unused)) *data, | |
179 | struct mad_header const *header, | |
180 | struct mad_pcm *pcm) { | |
181 | size_t n = pcm->length; | |
182 | const mad_fixed_t *l = pcm->samples[0], *r = pcm->samples[1]; | |
183 | static struct audio_dither ld[1], rd[1]; | |
184 | ||
185 | output_header(header->samplerate, | |
186 | pcm->channels, | |
187 | 16); | |
188 | switch(pcm->channels) { | |
189 | case 1: | |
190 | while(n--) | |
191 | output_16(audio_linear_dither(*l++, ld)); | |
192 | break; | |
193 | case 2: | |
194 | while(n--) { | |
195 | output_16(audio_linear_dither(*l++, ld)); | |
196 | output_16(audio_linear_dither(*r++, rd)); | |
197 | } | |
198 | break; | |
199 | default: | |
200 | fatal(0, "decoding %s: unsupported channel count %d", path, pcm->channels); | |
201 | } | |
202 | return MAD_FLOW_CONTINUE; | |
203 | } | |
204 | ||
205 | /** @brief MP3 input callback */ | |
206 | static enum mad_flow mp3_input(void attribute((unused)) *data, | |
207 | struct mad_stream *stream) { | |
208 | const size_t n = fill(); | |
209 | fprintf(stderr, "n=%zu\n", n); | |
210 | if(!n) | |
211 | return MAD_FLOW_STOP; | |
212 | mad_stream_buffer(stream, buffer, n); | |
213 | return MAD_FLOW_CONTINUE; | |
214 | } | |
215 | ||
216 | ||
217 | /** @brief MP3 error callback */ | |
218 | static enum mad_flow mp3_error(void attribute((unused)) *data, | |
219 | struct mad_stream *stream, | |
220 | struct mad_frame attribute((unused)) *frame) { | |
221 | error(0, "decoding %s: %s (%#04x)", | |
222 | path, mad_stream_errorstr(stream), stream->error); | |
223 | return MAD_FLOW_CONTINUE; | |
224 | } | |
225 | ||
226 | /** @brief MP3 header callback */ | |
227 | static enum mad_flow mp3_header(void attribute((unused)) *data, | |
228 | struct mad_header const *header) { | |
229 | output_header(header->samplerate, | |
230 | MAD_NCHANNELS(header), | |
231 | 16); | |
232 | return MAD_FLOW_CONTINUE; | |
233 | } | |
234 | ||
235 | /** @brief MP3 decoder */ | |
236 | static void decode_mp3(void) { | |
237 | struct mad_decoder mad[1]; | |
238 | ||
239 | open_input(); | |
240 | mad_decoder_init(mad, 0/*data*/, mp3_input, mp3_header, 0/*filter*/, | |
241 | mp3_output, mp3_error, 0/*message*/); | |
242 | if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC)) | |
243 | exit(1); | |
244 | mad_decoder_finish(mad); | |
245 | } | |
246 | ||
247 | /** @brief Lookup table of decoders */ | |
248 | static const struct decoder decoders[] = { | |
249 | { "*.mp3", decode_mp3 }, | |
250 | { "*.MP3", decode_mp3 }, | |
251 | { 0, 0 } | |
252 | }; | |
253 | ||
254 | static const struct option options[] = { | |
255 | { "help", no_argument, 0, 'h' }, | |
256 | { "version", no_argument, 0, 'V' }, | |
257 | { 0, 0, 0, 0 } | |
258 | }; | |
259 | ||
260 | /* Display usage message and terminate. */ | |
261 | static void help(void) { | |
262 | xprintf("Usage:\n" | |
263 | " disorder-decode [OPTIONS] PATH\n" | |
264 | "Options:\n" | |
265 | " --help, -h Display usage message\n" | |
266 | " --version, -V Display version number\n" | |
267 | "\n" | |
268 | "Audio decoder for DisOrder. Only intended to be used by speaker\n" | |
269 | "process, not for normal users.\n"); | |
270 | xfclose(stdout); | |
271 | exit(0); | |
272 | } | |
273 | ||
274 | /* Display version number and terminate. */ | |
275 | static void version(void) { | |
276 | xprintf("disorder-decode version %s\n", disorder_version_string); | |
277 | xfclose(stdout); | |
278 | exit(0); | |
279 | } | |
280 | ||
281 | int main(int argc, char **argv) { | |
282 | int n; | |
283 | const char *e; | |
284 | ||
285 | set_progname(argv); | |
286 | if(!setlocale(LC_CTYPE, "")) fatal(errno, "calling setlocale"); | |
287 | while((n = getopt_long(argc, argv, "hV", options, 0)) >= 0) { | |
288 | switch(n) { | |
289 | case 'h': help(); | |
290 | case 'V': version(); | |
291 | default: fatal(0, "invalid option"); | |
292 | } | |
293 | } | |
294 | if(optind >= argc) | |
295 | fatal(0, "missing filename"); | |
296 | if(optind + 1 < argc) | |
297 | fatal(0, "excess arguments"); | |
298 | if((e = getenv("DISORDER_RAW_FD"))) { | |
299 | if(!(outputfp = fdopen(atoi(e), "wb"))) | |
300 | fatal(errno, "fdopen"); | |
301 | } else | |
302 | outputfp = stdout; | |
303 | path = argv[optind]; | |
304 | for(n = 0; | |
305 | decoders[n].pattern | |
306 | && fnmatch(decoders[n].pattern, path, 0) != 0; | |
307 | ++n) | |
308 | ; | |
309 | if(!decoders[n].pattern) | |
310 | fatal(0, "cannot determine file type for %s", path); | |
311 | decoders[n].decode(); | |
312 | xfclose(outputfp); | |
313 | return 0; | |
314 | } | |
315 | ||
316 | /* | |
317 | Local Variables: | |
318 | c-basic-offset:2 | |
319 | comment-column:40 | |
320 | fill-column:79 | |
321 | indent-tabs-mode:nil | |
322 | End: | |
323 | */ |