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> | |
572c2899 | 36 | #include <vorbis/vorbisfile.h> |
87b5259b | 37 | #include <string.h> |
762806f1 RK |
38 | |
39 | /* libFLAC has had an API change and stupidly taken away the old API */ | |
40 | #if HAVE_FLAC_FILE_DECODER_H | |
41 | # include <FLAC/file_decoder.h> | |
42 | #else | |
43 | # include <FLAC/stream_decoder.h> | |
44 | #define FLAC__FileDecoder FLAC__StreamDecoder | |
45 | #define FLAC__FileDecoderState FLAC__StreamDecoderState | |
46 | #endif | |
f8f8039f RK |
47 | |
48 | #include "log.h" | |
49 | #include "syscalls.h" | |
50 | #include "defs.h" | |
ce6c36be | 51 | #include "wav.h" |
39492555 | 52 | #include "speaker-protocol.h" |
3fbdc96d | 53 | #include "version.h" |
f8f8039f RK |
54 | |
55 | /** @brief Encoding lookup table type */ | |
56 | struct decoder { | |
57 | /** @brief Glob pattern matching file */ | |
58 | const char *pattern; | |
59 | /** @brief Decoder function */ | |
60 | void (*decode)(void); | |
61 | }; | |
62 | ||
63 | /** @brief Input file */ | |
64 | static int inputfd; | |
65 | ||
66 | /** @brief Output file */ | |
67 | static FILE *outputfp; | |
68 | ||
69 | /** @brief Filename */ | |
70 | static const char *path; | |
71 | ||
72 | /** @brief Input buffer */ | |
75db8354 | 73 | static char input_buffer[1048576]; |
f8f8039f | 74 | |
87b5259b | 75 | /** @brief Number of bytes read into buffer */ |
76 | static int input_count; | |
f8f8039f | 77 | |
75db8354 | 78 | /** @brief Write an 8-bit word */ |
79 | static inline void output_8(int n) { | |
80 | if(putc(n, outputfp) < 0) | |
81 | fatal(errno, "decoding %s: output error", path); | |
82 | } | |
83 | ||
f8f8039f RK |
84 | /** @brief Write a 16-bit word in bigendian format */ |
85 | static inline void output_16(uint16_t n) { | |
86 | if(putc(n >> 8, outputfp) < 0 | |
75db8354 | 87 | || putc(n, outputfp) < 0) |
88 | fatal(errno, "decoding %s: output error", path); | |
89 | } | |
90 | ||
91 | /** @brief Write a 24-bit word in bigendian format */ | |
92 | static inline void output_24(uint32_t n) { | |
93 | if(putc(n >> 16, outputfp) < 0 | |
94 | || putc(n >> 8, outputfp) < 0 | |
95 | || putc(n, outputfp) < 0) | |
f8f8039f RK |
96 | fatal(errno, "decoding %s: output error", path); |
97 | } | |
98 | ||
75db8354 | 99 | /** @brief Write a 32-bit word in bigendian format */ |
100 | static inline void output_32(uint32_t n) { | |
101 | if(putc(n >> 24, outputfp) < 0 | |
102 | || putc(n >> 16, outputfp) < 0 | |
103 | || putc(n >> 8, outputfp) < 0 | |
104 | || putc(n, outputfp) < 0) | |
105 | fatal(errno, "decoding %s: output error", path); | |
106 | } | |
107 | ||
108 | /** @brief Write a block header | |
109 | * @param rate Sample rate in Hz | |
110 | * @param channels Channel count (currently only 1 or 2 supported) | |
111 | * @param bits Bits per sample (must be a multiple of 8, no more than 64) | |
112 | * @param nbytes Total number of data bytes | |
113 | * @param endian @ref ENDIAN_BIG or @ref ENDIAN_LITTLE | |
114 | * | |
115 | * Checks that the sample format is a supported one (so other calls do not have | |
116 | * to) and calls fatal() on error. | |
f8f8039f RK |
117 | */ |
118 | static void output_header(int rate, | |
119 | int channels, | |
39492555 | 120 | int bits, |
ce6c36be | 121 | int nbytes, |
122 | int endian) { | |
39492555 | 123 | struct stream_header header; |
124 | ||
75db8354 | 125 | if(bits <= 0 || bits % 8 || bits > 64) |
126 | fatal(0, "decoding %s: unsupported sample size %d bits", path, bits); | |
127 | if(channels <= 0 || channels > 2) | |
128 | fatal(0, "decoding %s: unsupported channel count %d", path, channels); | |
129 | if(rate <= 0) | |
130 | fatal(0, "decoding %s: nonsensical sample rate %dHz", path, rate); | |
39492555 | 131 | header.rate = rate; |
132 | header.bits = bits; | |
133 | header.channels = channels; | |
ce6c36be | 134 | header.endian = endian; |
39492555 | 135 | header.nbytes = nbytes; |
136 | if(fwrite(&header, sizeof header, 1, outputfp) < 1) | |
137 | fatal(errno, "decoding %s: writing format header", path); | |
f8f8039f RK |
138 | } |
139 | ||
140 | /** @brief Dithering state | |
141 | * Filched from mpg321, which credits it to Robert Leslie */ | |
142 | struct audio_dither { | |
143 | mad_fixed_t error[3]; | |
144 | mad_fixed_t random; | |
145 | }; | |
146 | ||
147 | /** @brief 32-bit PRNG | |
148 | * Filched from mpg321, which credits it to Robert Leslie */ | |
149 | static inline unsigned long prng(unsigned long state) | |
150 | { | |
151 | return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; | |
152 | } | |
153 | ||
154 | /** @brief Generic linear sample quantize and dither routine | |
155 | * Filched from mpg321, which credits it to Robert Leslie */ | |
f8f8039f RK |
156 | static long audio_linear_dither(mad_fixed_t sample, |
157 | struct audio_dither *dither) { | |
158 | unsigned int scalebits; | |
159 | mad_fixed_t output, mask, rnd; | |
87b5259b | 160 | const int bits = 16; |
f8f8039f RK |
161 | |
162 | enum { | |
163 | MIN = -MAD_F_ONE, | |
164 | MAX = MAD_F_ONE - 1 | |
165 | }; | |
166 | ||
167 | /* noise shape */ | |
168 | sample += dither->error[0] - dither->error[1] + dither->error[2]; | |
169 | ||
170 | dither->error[2] = dither->error[1]; | |
171 | dither->error[1] = dither->error[0] / 2; | |
172 | ||
173 | /* bias */ | |
174 | output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1)); | |
175 | ||
176 | scalebits = MAD_F_FRACBITS + 1 - bits; | |
177 | mask = (1L << scalebits) - 1; | |
178 | ||
179 | /* dither */ | |
180 | rnd = prng(dither->random); | |
181 | output += (rnd & mask) - (dither->random & mask); | |
182 | ||
183 | dither->random = rnd; | |
184 | ||
185 | /* clip */ | |
186 | if (output > MAX) { | |
187 | output = MAX; | |
188 | ||
189 | if (sample > MAX) | |
190 | sample = MAX; | |
191 | } | |
192 | else if (output < MIN) { | |
193 | output = MIN; | |
194 | ||
195 | if (sample < MIN) | |
196 | sample = MIN; | |
197 | } | |
198 | ||
199 | /* quantize */ | |
200 | output &= ~mask; | |
201 | ||
202 | /* error feedback */ | |
203 | dither->error[0] = sample - output; | |
204 | ||
205 | /* scale */ | |
206 | return output >> scalebits; | |
207 | } | |
f8f8039f RK |
208 | |
209 | /** @brief MP3 output callback */ | |
210 | static enum mad_flow mp3_output(void attribute((unused)) *data, | |
211 | struct mad_header const *header, | |
212 | struct mad_pcm *pcm) { | |
213 | size_t n = pcm->length; | |
214 | const mad_fixed_t *l = pcm->samples[0], *r = pcm->samples[1]; | |
215 | static struct audio_dither ld[1], rd[1]; | |
216 | ||
217 | output_header(header->samplerate, | |
218 | pcm->channels, | |
39492555 | 219 | 16, |
ce6c36be | 220 | 2 * pcm->channels * pcm->length, |
221 | ENDIAN_BIG); | |
f8f8039f RK |
222 | switch(pcm->channels) { |
223 | case 1: | |
224 | while(n--) | |
225 | output_16(audio_linear_dither(*l++, ld)); | |
226 | break; | |
227 | case 2: | |
228 | while(n--) { | |
229 | output_16(audio_linear_dither(*l++, ld)); | |
230 | output_16(audio_linear_dither(*r++, rd)); | |
231 | } | |
232 | break; | |
f8f8039f RK |
233 | } |
234 | return MAD_FLOW_CONTINUE; | |
235 | } | |
236 | ||
237 | /** @brief MP3 input callback */ | |
238 | static enum mad_flow mp3_input(void attribute((unused)) *data, | |
239 | struct mad_stream *stream) { | |
87b5259b | 240 | int used, remain, n; |
241 | ||
242 | /* libmad requires its caller to do ALL the buffering work, including coping | |
243 | * with partial frames. Given that it appears to be completely undocumented | |
244 | * you could perhaps be forgiven for not discovering this... */ | |
245 | if(input_count) { | |
246 | /* Compute total number of bytes consumed */ | |
247 | used = (char *)stream->next_frame - input_buffer; | |
248 | /* Compute number of bytes left to consume */ | |
249 | remain = input_count - used; | |
250 | memmove(input_buffer, input_buffer + used, remain); | |
251 | } else { | |
252 | remain = 0; | |
253 | } | |
254 | /* Read new data */ | |
255 | n = read(inputfd, input_buffer + remain, (sizeof input_buffer) - remain); | |
256 | if(n < 0) | |
257 | fatal(errno, "reading from %s", path); | |
258 | /* Compute total number of bytes available */ | |
259 | input_count = remain + n; | |
260 | if(input_count) | |
261 | mad_stream_buffer(stream, (unsigned char *)input_buffer, input_count); | |
262 | if(n) | |
263 | return MAD_FLOW_CONTINUE; | |
264 | else | |
f8f8039f | 265 | return MAD_FLOW_STOP; |
f8f8039f RK |
266 | } |
267 | ||
f8f8039f RK |
268 | /** @brief MP3 error callback */ |
269 | static enum mad_flow mp3_error(void attribute((unused)) *data, | |
270 | struct mad_stream *stream, | |
271 | struct mad_frame attribute((unused)) *frame) { | |
39492555 | 272 | if(0) |
273 | /* Just generates pointless verbosity l-( */ | |
274 | error(0, "decoding %s: %s (%#04x)", | |
275 | path, mad_stream_errorstr(stream), stream->error); | |
f8f8039f RK |
276 | return MAD_FLOW_CONTINUE; |
277 | } | |
278 | ||
279 | /** @brief MP3 decoder */ | |
280 | static void decode_mp3(void) { | |
281 | struct mad_decoder mad[1]; | |
282 | ||
87b5259b | 283 | if((inputfd = open(path, O_RDONLY)) < 0) |
284 | fatal(errno, "opening %s", path); | |
39492555 | 285 | mad_decoder_init(mad, 0/*data*/, mp3_input, 0/*header*/, 0/*filter*/, |
f8f8039f RK |
286 | mp3_output, mp3_error, 0/*message*/); |
287 | if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC)) | |
288 | exit(1); | |
289 | mad_decoder_finish(mad); | |
290 | } | |
291 | ||
572c2899 | 292 | /** @brief OGG decoder */ |
293 | static void decode_ogg(void) { | |
294 | FILE *fp; | |
295 | OggVorbis_File vf[1]; | |
296 | int err; | |
297 | long n; | |
298 | int bitstream; | |
299 | vorbis_info *vi; | |
300 | ||
301 | if(!(fp = fopen(path, "rb"))) | |
302 | fatal(errno, "cannot open %s", path); | |
303 | /* There doesn't seem to be any standard function for mapping the error codes | |
304 | * to strings l-( */ | |
305 | if((err = ov_open(fp, vf, 0/*initial*/, 0/*ibytes*/))) | |
306 | fatal(0, "ov_fopen %s: %d", path, err); | |
307 | if(!(vi = ov_info(vf, 0/*link*/))) | |
308 | fatal(0, "ov_info %s: failed", path); | |
75db8354 | 309 | while((n = ov_read(vf, input_buffer, sizeof input_buffer, 1/*bigendianp*/, |
572c2899 | 310 | 2/*bytes/word*/, 1/*signed*/, &bitstream))) { |
311 | if(n < 0) | |
312 | fatal(0, "ov_read %s: %ld", path, n); | |
313 | if(bitstream > 0) | |
314 | fatal(0, "only single-bitstream ogg files are supported"); | |
ce6c36be | 315 | output_header(vi->rate, vi->channels, 16/*bits*/, n, ENDIAN_BIG); |
75db8354 | 316 | if(fwrite(input_buffer, 1, n, outputfp) < (size_t)n) |
572c2899 | 317 | fatal(errno, "decoding %s: writing sample data", path); |
318 | } | |
319 | } | |
320 | ||
ce6c36be | 321 | /** @brief Sample data callback used by decode_wav() */ |
322 | static int wav_write(struct wavfile attribute((unused)) *f, | |
323 | const char *data, | |
324 | size_t nbytes, | |
325 | void attribute((unused)) *u) { | |
326 | if(fwrite(data, 1, nbytes, outputfp) < nbytes) | |
327 | fatal(errno, "decoding %s: writing sample data", path); | |
328 | return 0; | |
329 | } | |
330 | ||
331 | /** @brief WAV file decoder */ | |
332 | static void decode_wav(void) { | |
333 | struct wavfile f[1]; | |
334 | int err; | |
335 | ||
336 | if((err = wav_init(f, path))) | |
337 | fatal(err, "opening %s", path); | |
ce6c36be | 338 | output_header(f->rate, f->channels, f->bits, f->datasize, ENDIAN_LITTLE); |
339 | if((err = wav_data(f, wav_write, 0))) | |
340 | fatal(err, "error decoding %s", path); | |
341 | } | |
342 | ||
75db8354 | 343 | /** @brief Metadata callback for FLAC decoder |
344 | * | |
345 | * This is a no-op here. | |
346 | */ | |
347 | static void flac_metadata(const FLAC__FileDecoder attribute((unused)) *decoder, | |
348 | const FLAC__StreamMetadata attribute((unused)) *metadata, | |
349 | void attribute((unused)) *client_data) { | |
350 | } | |
351 | ||
352 | /** @brief Error callback for FLAC decoder */ | |
353 | static void flac_error(const FLAC__FileDecoder attribute((unused)) *decoder, | |
354 | FLAC__StreamDecoderErrorStatus status, | |
355 | void attribute((unused)) *client_data) { | |
356 | fatal(0, "error decoding %s: %s", path, | |
357 | FLAC__StreamDecoderErrorStatusString[status]); | |
358 | } | |
359 | ||
360 | /** @brief Write callback for FLAC decoder */ | |
361 | static FLAC__StreamDecoderWriteStatus flac_write | |
362 | (const FLAC__FileDecoder attribute((unused)) *decoder, | |
363 | const FLAC__Frame *frame, | |
364 | const FLAC__int32 *const buffer[], | |
365 | void attribute((unused)) *client_data) { | |
366 | size_t n, c; | |
367 | ||
368 | output_header(frame->header.sample_rate, | |
369 | frame->header.channels, | |
370 | frame->header.bits_per_sample, | |
371 | (frame->header.channels * frame->header.blocksize | |
372 | * frame->header.bits_per_sample) / 8, | |
373 | ENDIAN_BIG); | |
374 | for(n = 0; n < frame->header.blocksize; ++n) { | |
375 | for(c = 0; c < frame->header.channels; ++c) { | |
376 | switch(frame->header.bits_per_sample) { | |
377 | case 8: output_8(buffer[c][n]); break; | |
378 | case 16: output_16(buffer[c][n]); break; | |
379 | case 24: output_24(buffer[c][n]); break; | |
380 | case 32: output_32(buffer[c][n]); break; | |
381 | } | |
382 | } | |
383 | } | |
384 | return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | |
385 | } | |
386 | ||
387 | ||
388 | /** @brief FLAC file decoder */ | |
389 | static void decode_flac(void) { | |
762806f1 | 390 | #if HAVE_FLAC_FILE_DECODER_H |
75db8354 | 391 | FLAC__FileDecoder *fd = 0; |
392 | FLAC__FileDecoderState fs; | |
393 | ||
394 | if(!(fd = FLAC__file_decoder_new())) | |
395 | fatal(0, "FLAC__file_decoder_new failed"); | |
396 | if(!(FLAC__file_decoder_set_filename(fd, path))) | |
397 | fatal(0, "FLAC__file_set_filename failed"); | |
398 | FLAC__file_decoder_set_metadata_callback(fd, flac_metadata); | |
399 | FLAC__file_decoder_set_error_callback(fd, flac_error); | |
400 | FLAC__file_decoder_set_write_callback(fd, flac_write); | |
401 | if((fs = FLAC__file_decoder_init(fd))) | |
402 | fatal(0, "FLAC__file_decoder_init: %s", FLAC__FileDecoderStateString[fs]); | |
403 | FLAC__file_decoder_process_until_end_of_file(fd); | |
762806f1 RK |
404 | #else |
405 | FLAC__StreamDecoder *sd = 0; | |
406 | FLAC__StreamDecoderInitStatus is; | |
407 | ||
408 | if((is = FLAC__stream_decoder_init_file(sd, path, flac_write, flac_metadata, | |
409 | flac_error, 0))) | |
410 | fatal(0, "FLAC__stream_decoder_init_file %s: %s", | |
411 | path, FLAC__StreamDecoderInitStatusString[is]); | |
412 | #endif | |
75db8354 | 413 | } |
414 | ||
f8f8039f RK |
415 | /** @brief Lookup table of decoders */ |
416 | static const struct decoder decoders[] = { | |
417 | { "*.mp3", decode_mp3 }, | |
418 | { "*.MP3", decode_mp3 }, | |
572c2899 | 419 | { "*.ogg", decode_ogg }, |
420 | { "*.OGG", decode_ogg }, | |
75db8354 | 421 | { "*.flac", decode_flac }, |
422 | { "*.FLAC", decode_flac }, | |
ce6c36be | 423 | { "*.wav", decode_wav }, |
424 | { "*.WAV", decode_wav }, | |
f8f8039f RK |
425 | { 0, 0 } |
426 | }; | |
427 | ||
428 | static const struct option options[] = { | |
429 | { "help", no_argument, 0, 'h' }, | |
430 | { "version", no_argument, 0, 'V' }, | |
431 | { 0, 0, 0, 0 } | |
432 | }; | |
433 | ||
434 | /* Display usage message and terminate. */ | |
435 | static void help(void) { | |
436 | xprintf("Usage:\n" | |
437 | " disorder-decode [OPTIONS] PATH\n" | |
438 | "Options:\n" | |
439 | " --help, -h Display usage message\n" | |
440 | " --version, -V Display version number\n" | |
441 | "\n" | |
442 | "Audio decoder for DisOrder. Only intended to be used by speaker\n" | |
443 | "process, not for normal users.\n"); | |
444 | xfclose(stdout); | |
445 | exit(0); | |
446 | } | |
447 | ||
f8f8039f RK |
448 | int main(int argc, char **argv) { |
449 | int n; | |
450 | const char *e; | |
451 | ||
452 | set_progname(argv); | |
453 | if(!setlocale(LC_CTYPE, "")) fatal(errno, "calling setlocale"); | |
454 | while((n = getopt_long(argc, argv, "hV", options, 0)) >= 0) { | |
455 | switch(n) { | |
456 | case 'h': help(); | |
3fbdc96d | 457 | case 'V': version("disorder-decode"); |
f8f8039f RK |
458 | default: fatal(0, "invalid option"); |
459 | } | |
460 | } | |
461 | if(optind >= argc) | |
462 | fatal(0, "missing filename"); | |
463 | if(optind + 1 < argc) | |
464 | fatal(0, "excess arguments"); | |
465 | if((e = getenv("DISORDER_RAW_FD"))) { | |
466 | if(!(outputfp = fdopen(atoi(e), "wb"))) | |
467 | fatal(errno, "fdopen"); | |
468 | } else | |
469 | outputfp = stdout; | |
470 | path = argv[optind]; | |
471 | for(n = 0; | |
472 | decoders[n].pattern | |
473 | && fnmatch(decoders[n].pattern, path, 0) != 0; | |
474 | ++n) | |
475 | ; | |
476 | if(!decoders[n].pattern) | |
477 | fatal(0, "cannot determine file type for %s", path); | |
478 | decoders[n].decode(); | |
479 | xfclose(outputfp); | |
480 | return 0; | |
481 | } | |
482 | ||
483 | /* | |
484 | Local Variables: | |
485 | c-basic-offset:2 | |
486 | comment-column:40 | |
487 | fill-column:79 | |
488 | indent-tabs-mode:nil | |
489 | End: | |
490 | */ |