diff options
author | J. Alexander Treuman <jat@spatialrift.net> | 2007-05-22 23:11:36 +0000 |
---|---|---|
committer | J. Alexander Treuman <jat@spatialrift.net> | 2007-05-22 23:11:36 +0000 |
commit | 407497c40a34ae299cca56de1bd7c92e5724c54a (patch) | |
tree | deadb344cb967157fd97e3a80dac525bb3f651d8 | |
parent | e6d7663b10242d1cb9ad0411dbb45502db154b76 (diff) | |
download | mpd-407497c40a34ae299cca56de1bd7c92e5724c54a.tar.gz mpd-407497c40a34ae299cca56de1bd7c92e5724c54a.tar.xz mpd-407497c40a34ae299cca56de1bd7c92e5724c54a.zip |
Split pcm_convertAudioFormat into separate functions for bitrate, channel,
and samplerate conversion. This makes the code much easier to read, and
fixes a few bugs that were previously there.
git-svn-id: https://svn.musicpd.org/mpd/trunk@6224 09075e82-0dd4-0310-85a5-a0d7c8717e4f
Diffstat (limited to '')
-rw-r--r-- | src/audioOutput.c | 8 | ||||
-rw-r--r-- | src/outputBuffer.c | 9 | ||||
-rw-r--r-- | src/pcm_utils.c | 375 | ||||
-rw-r--r-- | src/pcm_utils.h | 11 |
4 files changed, 217 insertions, 186 deletions
diff --git a/src/audioOutput.c b/src/audioOutput.c index dd1635331..7a99dfd68 100644 --- a/src/audioOutput.c +++ b/src/audioOutput.c @@ -194,11 +194,9 @@ int openAudioOutput(AudioOutput * audioOutput, AudioFormat * audioFormat) static void convertAudioFormat(AudioOutput * audioOutput, char **chunkArgPtr, int *sizeArgPtr) { - int size = - pcm_sizeOfOutputBufferForAudioFormatConversion( - &(audioOutput->inAudioFormat), - *sizeArgPtr, - &(audioOutput->outAudioFormat)); + int size = pcm_sizeOfConvBuffer(&(audioOutput->inAudioFormat), + *sizeArgPtr, + &(audioOutput->outAudioFormat)); if (size > audioOutput->convBufferLen) { audioOutput->convBuffer = diff --git a/src/outputBuffer.c b/src/outputBuffer.c index 8b3a00b0e..36e15a78f 100644 --- a/src/outputBuffer.c +++ b/src/outputBuffer.c @@ -82,13 +82,8 @@ int sendDataToOutputBuffer(OutputBuffer * cb, InputStream * inStream, data = dataIn; datalen = dataInLen; } else { - datalen = - pcm_sizeOfOutputBufferForAudioFormatConversion(& - (dc-> - audioFormat), - dataInLen, - &(cb-> - audioFormat)); + datalen = pcm_sizeOfConvBuffer(&(dc->audioFormat), dataInLen, + &(cb->audioFormat)); if (datalen > convBufferLen) { convBuffer = xrealloc(convBuffer, datalen); convBufferLen = datalen; diff --git a/src/pcm_utils.c b/src/pcm_utils.c index c05559be5..ca5d6ca5c 100644 --- a/src/pcm_utils.c +++ b/src/pcm_utils.c @@ -153,7 +153,7 @@ void pcm_mix(char *buffer1, char *buffer2, size_t bufferSize1, } #ifdef HAVE_LIBSAMPLERATE -static int pcm_getSamplerateConverter(void) +static int pcm_getSampleRateConverter(void) { const char *conf, *test; int convalgo = SRC_SINC_FASTEST; @@ -185,198 +185,237 @@ static int pcm_getSamplerateConverter(void) } #endif -/* outFormat bits must be 16 and channels must be 1 or 2! */ -void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, - size_t inSize, AudioFormat * outFormat, - char *outBuffer) +#ifdef HAVE_LIBSAMPLERATE +static int pcm_convertSampleRate(mpd_sint8 channels, mpd_uint32 inSampleRate, + char *inBuffer, size_t inSize, + mpd_uint32 outSampleRate, char *outBuffer, + size_t outSize) { - static char *bitConvBuffer; - static int bitConvBufferLength; - static char *channelConvBuffer; - static int channelConvBufferLength; - char *dataChannelConv; - int dataChannelLen; - char *dataBitConv; - int dataBitLen; + static SRC_STATE *state; + static SRC_DATA data; + static size_t dataInSize; + static size_t dataOutSize; + size_t curDataInSize; + size_t curDataOutSize; + double ratio; + int error; + + if (!state) { + state = src_new(pcm_getSampleRateConverter(), channels, &error); + if (!state) { + ERROR("Cannot create new samplerate state: %s\n", + src_strerror(error)); + return 0; + } + DEBUG("Samplerate converter initialized\n"); + } - assert(outFormat->bits == 16); - assert(outFormat->channels == 2 || outFormat->channels == 1); + ratio = (double)outSampleRate / (double)inSampleRate; + if (ratio != data.src_ratio) { + DEBUG("Setting samplerate conversion ratio to %.2lf\n", ratio); + src_set_ratio(state, ratio); + data.src_ratio = ratio; + } - /* convert to 16 bit audio */ - switch (inFormat->bits) { - case 8: - dataBitLen = inSize << 1; - if (dataBitLen > bitConvBufferLength) { - bitConvBuffer = xrealloc(bitConvBuffer, dataBitLen); - bitConvBufferLength = dataBitLen; - } - dataBitConv = bitConvBuffer; - { - mpd_sint8 *in = (mpd_sint8 *) inBuffer; - mpd_sint16 *out = (mpd_sint16 *) dataBitConv; - int i; - for (i = 0; i < inSize; i++) { - *out++ = (*in++) << 8; - } - } - break; - case 16: - dataBitConv = inBuffer; - dataBitLen = inSize; - break; - case 24: - /* put dithering code from mp3_decode here */ - default: - ERROR("only 8 or 16 bits are supported for conversion!\n"); - exit(EXIT_FAILURE); + data.input_frames = inSize / 2 / channels; + curDataInSize = data.input_frames * sizeof(float) * channels; + if (curDataInSize > dataInSize) { + dataInSize = curDataInSize; + data.data_in = xrealloc(data.data_in, dataInSize); } - /* convert audio between mono and stereo */ - if (inFormat->channels == outFormat->channels) { - dataChannelConv = dataBitConv; - dataChannelLen = dataBitLen; - } else { - switch (inFormat->channels) { - case 1: /* convert from 1 -> 2 channels */ - dataChannelLen = (dataBitLen >> 1) << 2; - if (dataChannelLen > channelConvBufferLength) { - channelConvBuffer = xrealloc(channelConvBuffer, - dataChannelLen); - channelConvBufferLength = dataChannelLen; - } - dataChannelConv = channelConvBuffer; - { - mpd_sint16 *in = (mpd_sint16 *) dataBitConv; - mpd_sint16 *out = - (mpd_sint16 *) dataChannelConv; - int i, inSamples = dataBitLen >> 1; - for (i = 0; i < inSamples; i++) { - *out++ = *in; - *out++ = *in++; - } - } - break; - case 2: /* convert from 2 -> 1 channels */ - dataChannelLen = dataBitLen >> 1; - if (dataChannelLen > channelConvBufferLength) { - channelConvBuffer = xrealloc(channelConvBuffer, - dataChannelLen); - channelConvBufferLength = dataChannelLen; - } - dataChannelConv = channelConvBuffer; - { - mpd_sint16 *in = (mpd_sint16 *) dataBitConv; - mpd_sint16 *out = - (mpd_sint16 *) dataChannelConv; - int i, inSamples = dataBitLen >> 2; - for (i = 0; i < inSamples; i++) { - *out = (*in++) / 2; - *out++ += (*in++) / 2; - } - } - break; - default: - ERROR("only 1 or 2 channels are supported for " - "conversion!\n"); - exit(EXIT_FAILURE); - } + data.output_frames = outSize / 2 / channels; + curDataOutSize = data.output_frames * sizeof(float) * channels; + if (curDataOutSize > dataOutSize) { + dataOutSize = curDataOutSize; + data.data_out = xrealloc(data.data_out, dataOutSize); } - if (inFormat->sampleRate == outFormat->sampleRate) { - memcpy(outBuffer, dataChannelConv, dataChannelLen); - } else { -#ifdef HAVE_LIBSAMPLERATE - static SRC_STATE *state = NULL; - static SRC_DATA data; - static size_t data_in_size, data_out_size; - int error; - static double ratio = 0; - double newratio; - - if(!state) { - state = src_new(pcm_getSamplerateConverter(), outFormat->channels, &error); - if(!state) { - ERROR("Cannot create new samplerate state: %s\n", src_strerror(error)); - exit(EXIT_FAILURE); - } else { - DEBUG("Samplerate converter initialized\n"); - } + src_short_to_float_array((short *)inBuffer, data.data_in, + data.input_frames * channels); + + error = src_process(state, &data); + if (error) { + ERROR("Cannot process samples: %s\n", src_strerror(error)); + return 0; + } + + src_float_to_short_array(data.data_out, (short *)outBuffer, + data.output_frames_gen * channels); + + return 1; +} +#else /* !HAVE_LIBSAMPLERATE */ +/* resampling code blatantly ripped from ESD */ +static int pcm_convertSampleRate(mpd_sint8 channels, mpd_uint32 inSampleRate, + char *inBuffer, size_t inSize, + mpd_uint32 outSampleRate, char *outBuffer, + size_t outSize) +{ + mpd_uint32 rd_dat = 0; + mpd_uint32 wr_dat = 0; + mpd_sint16 *in = (mpd_sint16 *)inBuffer; + mpd_sint16 *out = (mpd_sint16 *)outBuffer; + mpd_uint32 nlen = outSize / 2; + mpd_sint16 lsample, rsample; + + switch (channels) { + case 1: + while (wr_dat < nlen) { + rd_dat = wr_dat * inSampleRate / outSampleRate; + + lsample = in[rd_dat++]; + + out[wr_dat++] = lsample; } + break; + case 2: + while (wr_dat < nlen) { + rd_dat = wr_dat * inSampleRate / outSampleRate; + rd_dat &= ~1; + + lsample = in[rd_dat++]; + rsample = in[rd_dat++]; - newratio = (double)outFormat->sampleRate / (double)inFormat->sampleRate; - if(newratio != ratio) { - DEBUG("Setting samplerate conversion ratio to %.2lf\n", newratio); - src_set_ratio(state, newratio); - ratio = newratio; + out[wr_dat++] = lsample; + out[wr_dat++] = rsample; } + break; + } - data.input_frames = dataChannelLen / 2 / outFormat->channels; - data.output_frames = pcm_sizeOfOutputBufferForAudioFormatConversion(inFormat, dataChannelLen, outFormat) / 2 / outFormat->channels; - data.src_ratio = (double)data.output_frames / (double)data.input_frames; + return 1; +} +#endif /* !HAVE_LIBSAMPLERATE */ - if (data_in_size != (data.input_frames * - outFormat->channels)) { - data_in_size = data.input_frames * outFormat->channels; - data.data_in = xrealloc(data.data_in, data_in_size); +static char *pcm_convertChannels(mpd_sint8 inChannels, char *inBuffer, + size_t inSize, size_t *outSize) +{ + static char *buf; + static size_t len; + char *outBuffer = NULL;; + mpd_sint16 *in; + mpd_sint16 *out; + int inSamples, i; + + switch (inChannels) { + /* convert from 1 -> 2 channels */ + case 1: + *outSize = (inSize >> 1) << 2; + if (*outSize > len) { + len = *outSize; + buf = xrealloc(buf, len); } - if (data_out_size != (data.output_frames * - outFormat->channels)) { - data_out_size = data.output_frames * - outFormat->channels; - data.data_out = xrealloc(data.data_out, data_out_size); + outBuffer = buf; + + inSamples = inSize >> 1; + in = (mpd_sint16 *)inBuffer; + out = (mpd_sint16 *)outBuffer; + for (i = 0; i < inSamples; i++) { + *out++ = *in; + *out++ = *in++; } - src_short_to_float_array((short *)dataChannelConv, data.data_in, data.input_frames * outFormat->channels); - error = src_process(state, &data); - if(error) { - ERROR("Cannot process samples: %s\n", src_strerror(error)); - exit(EXIT_FAILURE); + break; + /* convert from 2 -> 1 channels */ + case 2: + *outSize = inSize >> 1; + if (*outSize > len) { + len = *outSize; + buf = xrealloc(buf, len); + } + outBuffer = buf; + + inSamples = inSize >> 2; + in = (mpd_sint16 *)inBuffer; + out = (mpd_sint16 *)outBuffer; + for (i = 0; i < inSamples; i++) { + *out = (*in++) / 2; + *out++ += (*in++) / 2; } - src_float_to_short_array(data.data_out, (short *)outBuffer, data.output_frames * outFormat->channels); -#else - /* resampling code blatantly ripped from ESD */ - mpd_uint32 rd_dat = 0; - mpd_uint32 wr_dat = 0; - mpd_sint16 lsample, rsample; - mpd_sint16 *out = (mpd_sint16 *) outBuffer; - mpd_sint16 *in = (mpd_sint16 *) dataChannelConv; - mpd_uint32 nlen = pcm_sizeOfOutputBufferForAudioFormatConversion(inFormat, inSize, outFormat) / sizeof(mpd_sint16); - - switch (outFormat->channels) { - case 1: - while (wr_dat < nlen) { - rd_dat = wr_dat * inFormat->sampleRate / - outFormat->sampleRate; + break; + default: + ERROR("only 1 or 2 channels are supported for conversion!\n"); + } - lsample = in[rd_dat++]; + return outBuffer; +} - out[wr_dat++] = lsample; - } - break; - case 2: - while (wr_dat < nlen) { - rd_dat = wr_dat * inFormat->sampleRate / - outFormat->sampleRate; - rd_dat &= ~1; +static char *pcm_convertTo16bit(mpd_sint8 inBits, char *inBuffer, size_t inSize, + size_t *outSize) +{ + static char *buf; + static size_t len; + char *outBuffer = NULL; + mpd_sint8 *in; + mpd_sint16 *out; + int i; + + switch (inBits) { + case 8: + *outSize = inSize << 1; + if (*outSize > len) { + len = *outSize; + buf = xrealloc(buf, len); + } + outBuffer = buf; - lsample = in[rd_dat++]; - rsample = in[rd_dat++]; + in = (mpd_sint8 *)inBuffer; + out = (mpd_sint16 *)outBuffer; + for (i = 0; i < inSize; i++) + *out++ = (*in++) << 8; - out[wr_dat++] = lsample; - out[wr_dat++] = rsample; - } - break; - } -#endif + break; + case 16: + *outSize = inSize; + outBuffer = inBuffer; + break; + case 24: + /* put dithering code from mp3_decode here */ + default: + ERROR("only 8 or 16 bits are supported for conversion!\n"); } - return; + return outBuffer; +} + +/* outFormat bits must be 16 and channels must be 1 or 2! */ +void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, + size_t inSize, AudioFormat * outFormat, + char *outBuffer) +{ + char *buf; + size_t len; + size_t outSize = pcm_sizeOfConvBuffer(inFormat, inSize, outFormat); + + assert(outFormat->bits == 16); + assert(outFormat->channels == 2 || outFormat->channels == 1); + + /* everything else supports 16 bit only, so convert to that first */ + buf = pcm_convertTo16bit(inFormat->bits, inBuffer, inSize, &len); + if (!buf) + exit(EXIT_FAILURE); + + if (inFormat->channels != outFormat->channels) { + buf = pcm_convertChannels(inFormat->channels, buf, len, &len); + if (!buf) + exit(EXIT_FAILURE); + } + + if (inFormat->sampleRate == outFormat->sampleRate) { + assert(outSize >= len); + memcpy(outBuffer, buf, len); + } else { + if (!pcm_convertSampleRate(outFormat->channels, + inFormat->sampleRate, buf, len, + outFormat->sampleRate, outBuffer, + outSize)) + exit(EXIT_FAILURE); + } } -size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, - size_t inSize, - AudioFormat * outFormat) +size_t pcm_sizeOfConvBuffer(AudioFormat * inFormat, size_t inSize, + AudioFormat * outFormat) { const int shift = sizeof(mpd_sint16) * outFormat->channels; size_t outSize = inSize; diff --git a/src/pcm_utils.h b/src/pcm_utils.h index 85ec9e3e3..5142db17b 100644 --- a/src/pcm_utils.h +++ b/src/pcm_utils.h @@ -26,15 +26,14 @@ #include <stdlib.h> void pcm_volumeChange(char *buffer, int bufferSize, AudioFormat * format, - int volume); + int volume); void pcm_mix(char *buffer1, char *buffer2, size_t bufferSize1, - size_t bufferSize2, AudioFormat * format, float portion1); + size_t bufferSize2, AudioFormat * format, float portion1); void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, size_t - inSize, AudioFormat * outFormat, char *outBuffer); + inSize, AudioFormat * outFormat, char *outBuffer); -size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, - size_t inSize, - AudioFormat * outFormat); +size_t pcm_sizeOfConvBuffer(AudioFormat * inFormat, size_t inSize, + AudioFormat * outFormat); #endif |