- fatal(errno, "decoding %s: writing format header", path);
-}
-
-/** @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,
- 2 * pcm->channels * pcm->length);
- 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) {
- if(0)
- /* Just generates pointless verbosity l-( */
- error(0, "decoding %s: %s (%#04x)",
- path, mad_stream_errorstr(stream), stream->error);
- 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, 0/*header*/, 0/*filter*/,
- mp3_output, mp3_error, 0/*message*/);
- if(mad_decoder_run(mad, MAD_DECODER_MODE_SYNC))
- exit(1);
- mad_decoder_finish(mad);