-static void output_header(int rate,
- int channels,
- int bits) {
- static int already_written_header;
- struct ao_sample_format format;
-
- if(!already_written_header) {
- format.rate = rate;
- format.bits = bits;
- format.channels = channels;
- format.byte_format = AO_FMT_BIG;
- if(fwrite(&format, sizeof format, 1, outputfp) < 1)
- fatal(errno, "decoding %s: writing format header", path);
- already_written_header = 1;
- }
-}
-
-/** @brief Dithering state
- * Filched from mpg321, which credits it to Robert Leslie */
-struct audio_dither {
- mad_fixed_t error[3];
- mad_fixed_t random;
-};
-
-/** @brief 32-bit PRNG
- * Filched from mpg321, which credits it to Robert Leslie */
-static inline unsigned long prng(unsigned long state)
-{
- return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
-}
-
-/** @brief Generic linear sample quantize and dither routine
- * Filched from mpg321, which credits it to Robert Leslie */
-#define bits 16
-static long audio_linear_dither(mad_fixed_t sample,
- struct audio_dither *dither) {
- unsigned int scalebits;
- mad_fixed_t output, mask, rnd;
-
- enum {
- MIN = -MAD_F_ONE,
- MAX = MAD_F_ONE - 1
- };
-
- /* noise shape */
- sample += dither->error[0] - dither->error[1] + dither->error[2];
-
- dither->error[2] = dither->error[1];
- dither->error[1] = dither->error[0] / 2;
-
- /* bias */
- output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));
-
- scalebits = MAD_F_FRACBITS + 1 - bits;
- mask = (1L << scalebits) - 1;
-
- /* dither */
- rnd = prng(dither->random);
- output += (rnd & mask) - (dither->random & mask);
-
- dither->random = rnd;
-
- /* clip */
- if (output > MAX) {
- output = MAX;
-
- if (sample > MAX)
- sample = MAX;
- }
- else if (output < MIN) {
- output = MIN;
-
- if (sample < MIN)
- sample = MIN;
- }
-
- /* quantize */
- output &= ~mask;
-
- /* error feedback */
- dither->error[0] = sample - output;
-
- /* scale */
- return output >> scalebits;
-}
-#undef bits
-
-/** @brief MP3 output callback */
-static enum mad_flow mp3_output(void attribute((unused)) *data,
- struct mad_header const *header,
- struct mad_pcm *pcm) {
- size_t n = pcm->length;
- const mad_fixed_t *l = pcm->samples[0], *r = pcm->samples[1];
- static struct audio_dither ld[1], rd[1];
-
- output_header(header->samplerate,
- pcm->channels,
- 16);
- switch(pcm->channels) {
- case 1:
- while(n--)
- output_16(audio_linear_dither(*l++, ld));
- break;
- case 2:
- while(n--) {
- output_16(audio_linear_dither(*l++, ld));
- output_16(audio_linear_dither(*r++, rd));
- }
- break;
- default:
- fatal(0, "decoding %s: unsupported channel count %d", path, pcm->channels);
- }
- return MAD_FLOW_CONTINUE;
-}
-
-/** @brief MP3 input callback */
-static enum mad_flow mp3_input(void attribute((unused)) *data,
- struct mad_stream *stream) {
- const size_t n = fill();
- fprintf(stderr, "n=%zu\n", n);
- if(!n)
- return MAD_FLOW_STOP;
- mad_stream_buffer(stream, buffer, n);
- return MAD_FLOW_CONTINUE;
-}
-
-
-/** @brief MP3 error callback */
-static enum mad_flow mp3_error(void attribute((unused)) *data,
- struct mad_stream *stream,
- struct mad_frame attribute((unused)) *frame) {
- error(0, "decoding %s: %s (%#04x)",
- path, mad_stream_errorstr(stream), stream->error);
- return MAD_FLOW_CONTINUE;
-}
-
-/** @brief MP3 header callback */
-static enum mad_flow mp3_header(void attribute((unused)) *data,
- struct mad_header const *header) {
- output_header(header->samplerate,
- MAD_NCHANNELS(header),
- 16);
- return MAD_FLOW_CONTINUE;
-}
-
-/** @brief MP3 decoder */
-static void decode_mp3(void) {
- struct mad_decoder mad[1];
-
- open_input();
- mad_decoder_init(mad, 0/*data*/, mp3_input, mp3_header, 0/*filter*/,
- mp3_output, mp3_error, 0/*message*/);
- if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC))
- exit(1);
- mad_decoder_finish(mad);
+void output_header(int rate,
+ int channels,
+ int bits,
+ int nbytes,
+ int endian) {
+ struct stream_header header;
+
+ if(bits <= 0 || bits % 8 || bits > 64)
+ disorder_fatal(0, "decoding %s: unsupported sample size %d bits",
+ path, bits);
+ if(channels <= 0 || channels > 2)
+ disorder_fatal(0, "decoding %s: unsupported channel count %d",
+ path, channels);
+ if(rate <= 0)
+ disorder_fatal(0, "decoding %s: nonsensical sample rate %dHz", path, rate);
+ header.rate = rate;
+ header.bits = bits;
+ header.channels = channels;
+ header.endian = endian;
+ header.nbytes = nbytes;
+ if(fwrite(&header, sizeof header, 1, outputfp) < 1)
+ disorder_fatal(errno, "decoding %s: writing format header", path);