From f1b965bf12a5cc19832ce3e27271bd30a5bb50dd Mon Sep 17 00:00:00 2001 From: vspader Date: Mon, 30 Nov 2009 20:18:47 -0800 Subject: [PATCH] Patch from T. Million for 24-bit mad output. --- Plugins/MAD/MADDecoder.h | 14 +++++ Plugins/MAD/MADDecoder.m | 128 ++++++++++++++++++++++++++++++++++----- 2 files changed, 126 insertions(+), 16 deletions(-) diff --git a/Plugins/MAD/MADDecoder.h b/Plugins/MAD/MADDecoder.h index af41d8952..aeb67ec41 100644 --- a/Plugins/MAD/MADDecoder.h +++ b/Plugins/MAD/MADDecoder.h @@ -14,12 +14,26 @@ #define INPUT_BUFFER_SIZE 5*8192 +struct audio_dither { + mad_fixed_t error[3]; + mad_fixed_t random; +}; + +struct audio_stats { + unsigned long clipped_samples; + mad_fixed_t peak_clipping; + mad_fixed_t peak_sample; +}; + @interface MADDecoder : NSObject { struct mad_stream _stream; struct mad_frame _frame; struct mad_synth _synth; + struct audio_dither channel_dither[2]; + struct audio_stats stats; + unsigned char _inputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD]; unsigned char *_outputBuffer; int _outputFrames; diff --git a/Plugins/MAD/MADDecoder.m b/Plugins/MAD/MADDecoder.m index 5261227a8..c33b6222f 100644 --- a/Plugins/MAD/MADDecoder.m +++ b/Plugins/MAD/MADDecoder.m @@ -277,7 +277,7 @@ mad_frame_finish (&frame); mad_stream_finish (&stream); - bitsPerSample = 16; + bitsPerSample = 24; bytesPerFrame = (bitsPerSample/8) * channels; @@ -321,6 +321,89 @@ return [self scanFile]; } +/* + * NAME: prng() + * DESCRIPTION: 32-bit pseudo-random number generator + */ +static inline +unsigned long prng(unsigned long state) +{ + return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; +} + + +// Clipping and rounding code from madplay(audio.c): +/* + * madplay - MPEG audio decoder and player + * Copyright (C) 2000-2004 Robert Leslie + */ +static inline signed long audio_linear_dither(unsigned int bits, mad_fixed_t sample, + struct audio_dither *dither, + struct audio_stats *stats) +{ + unsigned int scalebits; + mad_fixed_t output, mask, random; + + 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 */ + random = prng(dither->random); + output += (random & mask) - (dither->random & mask); + + dither->random = random; + + /* clip */ + if (output >= stats->peak_sample) { + if (output > MAX) { + ++stats->clipped_samples; + if (output - MAX > stats->peak_clipping) + stats->peak_clipping = output - MAX; + + output = MAX; + + if (sample > MAX) + sample = MAX; + } + stats->peak_sample = output; + } + else if (output < -stats->peak_sample) { + if (output < MIN) { + ++stats->clipped_samples; + if (MIN - output > stats->peak_clipping) + stats->peak_clipping = MIN - output; + + output = MIN; + + if (sample < MIN) + sample = MIN; + } + stats->peak_sample = -output; + } + + /* quantize */ + output &= ~mask; + + /* error feedback */ + dither->error[0] = sample - output; + + /* scale */ + return output >> scalebits; +} // Clipping and rounding code from madplay(audio.c): /* @@ -402,23 +485,36 @@ audio_linear_round(unsigned int bits, _outputBuffer = (unsigned char *) malloc (_outputFrames * bytesPerFrame * sizeof (char)); - unsigned int i, j; - unsigned int stride = channels * 2; - for (j = 0; j < channels; j++) - { - /* output sample(s) in 16-bit signed little-endian PCM */ - mad_fixed_t const *channel = _synth.pcm.samples[j]; - unsigned char *outputPtr = _outputBuffer + (j * 2); - - for (i = startingSample; i < sampleCount; i++) - { - signed short sample = audio_linear_round(bitsPerSample, channel[i]); + int ch; + int i; + int stride = bitsPerSample/8; + unsigned char *outputPtr = _outputBuffer; + + // samples [0 ... n] + for(i = startingSample; i < sampleCount; i++) + { + // channels [0 .. n] in this case LRLRLRLR + for (ch = 0; ch < channels; ch++) + { + signed long sample = audio_linear_dither(bitsPerSample, + _synth.pcm.samples[ch][i], + &channel_dither[ch], + &stats); - outputPtr[0] = sample>>8; - outputPtr[1] = sample & 0xff; + if(bitsPerSample == 24) + { + outputPtr[0] = sample >> 16; + outputPtr[1] = sample >> 8; + outputPtr[2] = sample >> 0; + } + else + { + outputPtr[0] = sample >> 8; + outputPtr[1] = sample & 0xff; + } outputPtr += stride; - } - } + } + } // Output to a file // FILE *f = fopen("data.raw", "a");