diff options
-rw-r--r-- | src/audio.h | 1 | ||||
-rw-r--r-- | src/pcm_utils.c | 163 | ||||
-rw-r--r-- | src/replayGain.c | 5 |
3 files changed, 144 insertions, 25 deletions
diff --git a/src/audio.h b/src/audio.h index 518db0588..aa139c8ab 100644 --- a/src/audio.h +++ b/src/audio.h @@ -35,7 +35,6 @@ typedef struct _AudioFormat { volatile mpd_uint32 sampleRate; volatile mpd_sint8 bits; volatile mpd_sint8 fracBits; - /*volatile mpd_sint8 floatSamples;*/ } AudioFormat; void copyAudioFormat(AudioFormat * dest, AudioFormat * src); diff --git a/src/pcm_utils.c b/src/pcm_utils.c index 696b96ff9..9e816e00d 100644 --- a/src/pcm_utils.c +++ b/src/pcm_utils.c @@ -100,12 +100,103 @@ void pcm_convertToIntWithDither(int bits, } } - -char *pcm_convertSampleRate(AudioFormat *inFormat, char *inBuffer, - int inFrames, AudioFormat *outFormat, int outFrames) +struct { + mpd_uint32 delay; + mpd_uint32 inRate; + mpd_uint32 outRate; +} convSampleRateData = {0,0,0}; + +void pcm_convertSampleRate( + AudioFormat * inFormat, mpd_fixed_t * inBuffer, int inFrames, + AudioFormat *outFormat, mpd_fixed_t *outBuffer, int outFrames) { - return NULL; + static int inRate; /* reduced bitRate */ + static int outRate; + static int shift; + static int unshift; + static mpd_fixed_t oldSampleL = 0; + static mpd_fixed_t oldSampleR = 0; + mpd_uint32 curTime; + + /* recalculate static data if samplerate has changed */ + if(inFormat->sampleRate != convSampleRateData.inRate || + outFormat->sampleRate != convSampleRateData.outRate) { + /* set new sample rate info and reset delay */ + convSampleRateData.inRate = inFormat->sampleRate; + convSampleRateData.outRate = outFormat->sampleRate; + convSampleRateData.delay = 0; + /* calculate the rates to use in calculations... */ + inRate = inFormat->sampleRate; + outRate = outFormat->sampleRate; + unshift=0; + shift = 16; /* worst case shift */ + /* ...reduce them to minimize shifting */ + while(!(inRate & 1) && !(outRate & 1)) { + unshift++; + shift--; + inRate >>= 1; + outRate >>= 1; + } + oldSampleL = 0; + oldSampleR = 0; + } + + /* compute */ + curTime = convSampleRateData.delay >> unshift; +/* +ERROR("pcm_convertSampleRate2: inFrames=%i, outFrames=%i, inRate=%i, outRate=%i, curTime=%i\n", + inFrames, outFrames, inRate, outRate, curTime);*/ + switch(inFormat->channels) { + case 1: + while(inFrames--) { + curTime += outRate; + /* calculate new samples */ + while(curTime >= inRate) { + curTime -= inRate; + *outBuffer++ = oldSampleL + + (((*inBuffer - oldSampleL) * + ((outRate - curTime) >> shift)) / + outRate); + outFrames--; + } + oldSampleL = *inBuffer++; + } + case 2: + while(inFrames--) { + curTime += outRate; + /* calculate new samples */ + while(curTime >= inRate) { + curTime -= inRate; + *outBuffer++ = oldSampleL + + (((*inBuffer - oldSampleL) * + ((outRate - curTime) >> shift)) / + outRate); + *outBuffer++ = oldSampleR + + (((inBuffer[1] - oldSampleR) * + ((outRate - curTime) >> shift)) / + outRate); + outFrames--; + } + oldSampleL = *inBuffer++; + oldSampleR = *inBuffer++; + } + } + convSampleRateData.delay = curTime << unshift; +/* +ERROR("pcm_convertSampleRate2: inFrames=%i, outFrames=%i, curTime=%i\n\n", + inFrames, outFrames, curTime);*/ + + /* some temporary debugging tests */ + if(inFrames>0) { + ERROR("pcm_convertSampleRate produced to few outFrames!\n"); + exit(EXIT_FAILURE); + } + if(outFrames>0) { + ERROR("pcm_convertSampleRate produced to many outFrames!\n"); + exit(EXIT_FAILURE); + } } + /****** exported procedures ***************************************************/ @@ -234,21 +325,25 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t { static char *convBuffer = NULL; static int convBufferLength = 0; + static char *convSampleBuffer = NULL; + static int convSampleBufferLength = 0; char * dataConv; int dataLen; int fracBits; const int inSamples = (inSize << 3) / inFormat->bits; const int inFrames = inSamples / inFormat->channels; - const int outFrames = (inFrames * (mpd_uint32)(outFormat->sampleRate)) / - inFormat->sampleRate; - const int outSamples = outFrames * outFormat->channels; - + const size_t outSize = pcm_sizeOfOutputBufferForAudioFormatConversion( + inFormat, inSize, outFormat); + const int outSamples = (outSize << 3) / outFormat->bits; + const int outFrames = outSamples / outFormat->channels; +/*ERROR("pcm_convertAudioFormat: outSize=%i, outSamples=%i, outFrames=%i," + "outFormat.bits=%i, outFormat.channels=%i\n", + outSize, outSamples, outFrames, outFormat->bits, outFormat->channels);*/ /* make sure convBuffer is big enough for 2 channels of 32 bit samples */ dataLen = inFrames << 3; if(dataLen > convBufferLength) { convBuffer = (char *) realloc(convBuffer, dataLen); - if(!convBuffer) - { + if(!convBuffer) { ERROR("Could not allocate more memory for convBuffer!\n"); exit(EXIT_FAILURE); } @@ -306,15 +401,28 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t /****** convert sample rate ******/ if(inFormat->sampleRate != outFormat->sampleRate) { - dataConv = pcm_convertSampleRate( - inFormat, dataConv, inFrames, - outFormat, outFrames); + /* check size of buffer */ + dataLen = outFrames << 3; + if(dataLen > convSampleBufferLength) { + convSampleBuffer = (char *) + realloc(convSampleBuffer, dataLen); + if(!convSampleBuffer) { + ERROR("Could not allocate memory for " + "convSampleBuffer!\n"); + exit(EXIT_FAILURE); + } + convSampleBufferLength = dataLen; + } + /* convert samples */ + pcm_convertSampleRate(inFormat, (mpd_fixed_t*)dataConv, inFrames, + outFormat, (mpd_fixed_t*)convSampleBuffer, outFrames); + dataConv = convSampleBuffer; } /****** convert to output format ******/ - /* if outformat is mpd_fixed_t then we are done TODO */ - if(outFormat->fracBits) { + /* if outformat is mpd_fixed_t then we are done ?! TODO */ + if(outFormat->fracBits==fracBits) { if(outFormat->bits==32) { if(outBuffer != dataConv) memcpy(outBuffer, dataConv, outSamples << 2); @@ -374,14 +482,25 @@ size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, { const int inShift = (inFormat->bits * inFormat->channels) >> 3; const int outShift = (outFormat->bits * outFormat->channels) >> 3; + const int inFrames = inSize / inShift; + mpd_uint32 outFrames; - size_t inFrames = inSize / inShift; - size_t outFrames = (inFrames * (mpd_uint32)(outFormat->sampleRate)) / - inFormat->sampleRate; - - size_t outSize = outFrames * outShift; - - return outSize; + if(inFormat->sampleRate == outFormat->sampleRate) + outFrames = inFrames; + else { + mpd_uint32 delay = convSampleRateData.delay; + if(inFormat->sampleRate != convSampleRateData.inRate || + outFormat->sampleRate != convSampleRateData.outRate) + { + delay = 0; + } + outFrames = (inFrames * (mpd_uint32)(outFormat->sampleRate) + + delay) / inFormat->sampleRate; +/* +ERROR("pcm_size... inSize=%i, inShift=%i, outShift=%i, inFrames=%i, outFrames=%i, delay=%i\n", + inSize, inShift, outShift, inFrames, outFrames, delay); */ + } + return outFrames * outShift; } diff --git a/src/replayGain.c b/src/replayGain.c index aac057668..d0de774a0 100644 --- a/src/replayGain.c +++ b/src/replayGain.c @@ -134,8 +134,9 @@ void doReplayGain(ReplayGainInfo * info, char * buffer, int bufferSize, } samples = bufferSize >> 2; - iScale = info->scale * 256; - shift = 8; + /* 64 steps is enough - gives 0.13db resolution at 0dB and 2.5dB at -25dB*/ + iScale = info->scale * 64; + shift = 6; /* handle negative or zero scale */ |