diff options
-rw-r--r-- | src/audioOutput.c | 10 | ||||
-rw-r--r-- | src/audioOutput.h | 2 | ||||
-rw-r--r-- | src/outputBuffer.c | 3 | ||||
-rw-r--r-- | src/outputBuffer.h | 2 | ||||
-rw-r--r-- | src/pcm_utils.c | 81 | ||||
-rw-r--r-- | src/pcm_utils.h | 22 | ||||
-rw-r--r-- | src/playerData.c | 1 |
7 files changed, 85 insertions, 36 deletions
diff --git a/src/audioOutput.c b/src/audioOutput.c index 7a99dfd68..ccc701451 100644 --- a/src/audioOutput.c +++ b/src/audioOutput.c @@ -135,6 +135,7 @@ int initAudioOutput(AudioOutput *ao, ConfigParam * param) memset(&ao->inAudioFormat, 0, sizeof(AudioFormat)); memset(&ao->outAudioFormat, 0, sizeof(AudioFormat)); memset(&ao->reqAudioFormat, 0, sizeof(AudioFormat)); + memset(&ao->convState, 0, sizeof(ConvState)); if (format) { ao->convertAudioFormat = 1; @@ -205,10 +206,11 @@ static void convertAudioFormat(AudioOutput * audioOutput, char **chunkArgPtr, } pcm_convertAudioFormat(&(audioOutput->inAudioFormat), - *chunkArgPtr, - *sizeArgPtr, - &(audioOutput->outAudioFormat), - audioOutput->convBuffer); + *chunkArgPtr, + *sizeArgPtr, + &(audioOutput->outAudioFormat), + audioOutput->convBuffer, + &audioOutput->convState); *sizeArgPtr = size; *chunkArgPtr = audioOutput->convBuffer; diff --git a/src/audioOutput.h b/src/audioOutput.h index cba445a3c..bcbe7997d 100644 --- a/src/audioOutput.h +++ b/src/audioOutput.h @@ -21,6 +21,7 @@ #include "../config.h" +#include "pcm_utils.h" #include "mpd_types.h" #include "audio.h" #include "tag.h" @@ -66,6 +67,7 @@ struct _AudioOutput { AudioFormat inAudioFormat; AudioFormat outAudioFormat; AudioFormat reqAudioFormat; + ConvState convState; char *convBuffer; int convBufferLen; int sameInAndOutFormats; diff --git a/src/outputBuffer.c b/src/outputBuffer.c index 36e15a78f..d9cbcdc60 100644 --- a/src/outputBuffer.c +++ b/src/outputBuffer.c @@ -90,7 +90,8 @@ int sendDataToOutputBuffer(OutputBuffer * cb, InputStream * inStream, } data = convBuffer; pcm_convertAudioFormat(&(dc->audioFormat), dataIn, dataInLen, - &(cb->audioFormat), data); + &(cb->audioFormat), data, + &(cb->convState)); } if (replayGainInfo && (replayGainState != REPLAYGAIN_OFF)) diff --git a/src/outputBuffer.h b/src/outputBuffer.h index fecff91e0..f690941d4 100644 --- a/src/outputBuffer.h +++ b/src/outputBuffer.h @@ -19,6 +19,7 @@ #ifndef OUTPUT_BUFFER_H #define OUTPUT_BUFFER_H +#include "pcm_utils.h" #include "mpd_types.h" #include "decode.h" #include "audio.h" @@ -39,6 +40,7 @@ typedef struct _OutputBuffer { mpd_sint16 volatile begin; mpd_sint16 volatile end; AudioFormat audioFormat; + ConvState convState; MetadataChunk metadataChunks[BUFFERED_METACHUNKS]; mpd_sint8 metaChunkSet[BUFFERED_METACHUNKS]; mpd_sint8 *volatile metaChunk; diff --git a/src/pcm_utils.c b/src/pcm_utils.c index b3c6689ae..8cc19a9d4 100644 --- a/src/pcm_utils.c +++ b/src/pcm_utils.c @@ -27,10 +27,6 @@ #include <math.h> #include <assert.h> -#ifdef HAVE_LIBSAMPLERATE -#include <samplerate.h> -#endif - void pcm_volumeChange(char *buffer, int bufferSize, AudioFormat * format, int volume) { @@ -189,47 +185,74 @@ static int pcm_getSampleRateConverter(void) static int pcm_convertSampleRate(mpd_sint8 channels, mpd_uint32 inSampleRate, char *inBuffer, size_t inSize, mpd_uint32 outSampleRate, char *outBuffer, - size_t outSize) + size_t outSize, ConvState *convState) { static int convalgo = -1; - static SRC_DATA data; - static size_t dataInSize; - static size_t dataOutSize; - size_t curDataInSize; - size_t curDataOutSize; + SRC_DATA *data = &convState->data; + size_t dataInSize; + size_t dataOutSize; int error; if (convalgo < 0) convalgo = pcm_getSampleRateConverter(); - data.src_ratio = (double)outSampleRate / (double)inSampleRate; + /* (re)set the state/ratio if the in or out format changed */ + if ((channels != convState->lastChannels) || + (inSampleRate != convState->lastInSampleRate) || + (outSampleRate != convState->lastOutSampleRate)) { + convState->error = 0; + convState->lastChannels = channels; + convState->lastInSampleRate = inSampleRate; + convState->lastOutSampleRate = outSampleRate; + + if (convState->state) + convState->state = src_delete(convState->state); + + convState->state = src_new(convalgo, channels, &error); + if (!convState->state) { + ERROR("cannot create new libsamplerate state: %s\n", + src_strerror(error)); + convState->error = 1; + return 0; + } + + data->src_ratio = (double)outSampleRate / (double)inSampleRate; + DEBUG("setting samplerate conversion ratio to %.2lf\n", + data->src_ratio); + src_set_ratio(convState->state, data->src_ratio); + } + + /* there was an error previously, and nothing has changed */ + if (convState->error) + return 0; - 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); + data->input_frames = inSize / 2 / channels; + dataInSize = data->input_frames * sizeof(float) * channels; + if (dataInSize > convState->dataInSize) { + convState->dataInSize = dataInSize; + data->data_in = xrealloc(data->data_in, dataInSize); } - 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); + data->output_frames = outSize / 2 / channels; + dataOutSize = data->output_frames * sizeof(float) * channels; + if (dataOutSize > convState->dataOutSize) { + convState->dataOutSize = dataOutSize; + data->data_out = xrealloc(data->data_out, dataOutSize); } - src_short_to_float_array((short *)inBuffer, data.data_in, - data.input_frames * channels); + src_short_to_float_array((short *)inBuffer, data->data_in, + data->input_frames * channels); - error = src_simple(&data, convalgo, channels); + error = src_process(convState->state, data); if (error) { ERROR("error processing samples with libsamplerate: %s\n", src_strerror(error)); + convState->error = 1; return 0; } - src_float_to_short_array(data.data_out, (short *)outBuffer, - data.output_frames_gen * channels); + src_float_to_short_array(data->data_out, (short *)outBuffer, + data->output_frames_gen * channels); return 1; } @@ -238,7 +261,7 @@ static int pcm_convertSampleRate(mpd_sint8 channels, mpd_uint32 inSampleRate, static int pcm_convertSampleRate(mpd_sint8 channels, mpd_uint32 inSampleRate, char *inBuffer, size_t inSize, mpd_uint32 outSampleRate, char *outBuffer, - size_t outSize) + size_t outSize, ConvState *convState) { mpd_uint32 rd_dat = 0; mpd_uint32 wr_dat = 0; @@ -370,7 +393,7 @@ static char *pcm_convertTo16bit(mpd_sint8 bits, char *inBuffer, size_t inSize, /* 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 *outBuffer, ConvState *convState) { char *buf; size_t len; @@ -397,7 +420,7 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, if (!pcm_convertSampleRate(outFormat->channels, inFormat->sampleRate, buf, len, outFormat->sampleRate, outBuffer, - outSize)) + outSize, convState)) exit(EXIT_FAILURE); } } diff --git a/src/pcm_utils.h b/src/pcm_utils.h index 5142db17b..800ac27f4 100644 --- a/src/pcm_utils.h +++ b/src/pcm_utils.h @@ -25,14 +25,32 @@ #include <stdlib.h> +#ifdef HAVE_LIBSAMPLERATE +#include <samplerate.h> +#endif + +typedef struct _ConvState { +#ifdef HAVE_LIBSAMPLERATE + SRC_STATE *state; + SRC_DATA data; + size_t dataInSize; + size_t dataOutSize; + mpd_sint8 lastChannels; + mpd_sint32 lastInSampleRate; + mpd_sint32 lastOutSampleRate; + int error; +#endif +} ConvState; + void pcm_volumeChange(char *buffer, int bufferSize, AudioFormat * format, int volume); void pcm_mix(char *buffer1, char *buffer2, size_t bufferSize1, size_t bufferSize2, AudioFormat * format, float portion1); -void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, size_t - inSize, AudioFormat * outFormat, char *outBuffer); +void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, + size_t inSize, AudioFormat * outFormat, + char *outBuffer, ConvState *convState); size_t pcm_sizeOfConvBuffer(AudioFormat * inFormat, size_t inSize, AudioFormat * outFormat); diff --git a/src/playerData.c b/src/playerData.c index 31f27540c..0d5e6e9e4 100644 --- a/src/playerData.c +++ b/src/playerData.c @@ -116,6 +116,7 @@ void initPlayerData(void) allocationSize - device_array_size; buffer = &(playerData_pd->buffer); + memset(&buffer->convState, 0, sizeof(ConvState)); buffer->chunks = ((char *)playerData_pd) + sizeof(PlayerData); buffer->chunkSize = (mpd_uint16 *) (((char *)buffer->chunks) + buffered_chunks * CHUNK_SIZE); |