diff options
Diffstat (limited to '')
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | doc/mpd.conf.5 | 29 | ||||
-rw-r--r-- | doc/mpdconf.example | 6 | ||||
-rw-r--r-- | src/conf.c | 1 | ||||
-rw-r--r-- | src/conf.h | 1 | ||||
-rw-r--r-- | src/pcm_utils.c | 109 |
6 files changed, 156 insertions, 11 deletions
diff --git a/configure.ac b/configure.ac index 8b41bbad0..95386d296 100644 --- a/configure.ac +++ b/configure.ac @@ -11,6 +11,7 @@ AC_SUBST(MP4FF_LIB) AC_SUBST(MP4FF_SUBDIR) AC_PROG_CC +AM_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_LIBTOOL AC_PROG_MAKE_SET @@ -79,6 +80,7 @@ AC_ARG_ENABLE(audiofile,[ --disable-audiofile disable audiofile support, di AC_ARG_ENABLE(mod,[ --enable-mod enable MOD support (default: disable],[enable_mod=$enableval],[enable_mod=yes]) AC_ARG_ENABLE(mpc,[ --disable-mpc disable musepack (MPC) support (default: enable)],[enable_mpc=$enableval],[enable_mpc=yes]) AC_ARG_ENABLE(id3,[ --disable-id3 disable id3 support (default: enable)],[enable_id3=$enableval],[enable_id3=yes]) +AC_ARG_ENABLE(lsr,[ --disable-lsr disable libsamplerate support (default: enable)],[enable_lsr=$enableval],[enable_lsr=yes]) AC_ARG_WITH(tremor,[[ --with-tremor[=PFX] Use Tremor(vorbisidec) integer Ogg-Vorbis decoder (with optional prefix)]], use_tremor=yes; test x$withval != xyes && tremor_prefix="$withval",) AC_ARG_WITH(tremor-libraries,[ --with-tremor-libraries=DIR Directory where Tremor library is installed (optional)], tremor_libraries="$withval", tremor_libraries="") @@ -101,6 +103,10 @@ AC_ARG_WITH(faad-libraries,[ --with-faad-libraries=DIR Directory where faad2 AC_ARG_WITH(faad-includes,[ --with-faad-includes=DIR Directory where faad2 header files are installed (optional)], faad_includes="$withval", faad_includes="") AC_ARG_WITH(zeroconf,[[ --with-zeroconf=[auto|avahi|bonjour|no] Enable zeroconf backend (default=auto)]], with_zeroconf="$withval", with_zeroconf="auto") +AC_ARG_WITH(lsr,[ --with-src=PFX Prefix where libsamplerate is installed], src_prefix="$withval", src_prefix="") +AC_ARG_WITH(lsr-libraries,[ --with-lsr-libraries=DIR Directory where libsamplerate library is installed (optional)], lsr_libraries="$withval", lsr_libraries="") +AC_ARG_WITH(lsr-includes,[ --with-lsr-includes=DIR Directory where libsamplerate header files are installed (optional)], lsr_includes="$withval", lsr_includes="") + AC_C_BIGENDIAN AC_CHECK_SIZEOF(short) @@ -186,6 +192,12 @@ if test x$enable_pulse = xyes; then [enable_pulse=no;AC_MSG_WARN([PulseAudio not found -- disabling])]) fi +if test x$enable_lsr = xyes; then + PKG_CHECK_MODULES([SAMPLERATE], [samplerate >= 0.0.15], + [enable_lsr=yes;AC_DEFINE([HAVE_LIBSAMPLERATE], 1, [Define to enable libsamplerate])] MPD_LIBS="$MPD_LIBS $SAMPLERATE_LIBS" MPD_CFLAGS="$MPD_CFLAGS $SAMPLERATE_CFLAGS", + [enable_lsr=no;AC_MSG_WARN([libsamplerate not found -- disabling])]) +fi + if test x$enable_mvp = xyes; then AC_DEFINE(HAVE_MVP,1,[Define to enable Hauppauge Media MVP support]) fi @@ -777,8 +789,15 @@ if fi echo "" - echo " Other features:" + +if test x$enable_lsr = xyes; then + echo " libsamplerate support .........enabled" +else + echo " libsamplerate support .........disabled" +fi + + if test x$with_zeroconf != xno; then echo " Zeroconf support ..............$with_zeroconf" else diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5 index 821500e8b..572738234 100644 --- a/doc/mpd.conf.5 +++ b/doc/mpd.conf.5 @@ -126,6 +126,35 @@ This is the gain (in dB) applied to songs with ReplayGain tags. .B volume_normalization <yes or no> If yes, mpd will normalize the volume of songs as they play. Default is no. .TP +.B samplerate_converter <integer or prefix> +Specifies the libsamplerate converter to use. +The supplied value should either be an integer or a prefix of the name of a converter. +The list of available converters at the time of writing is below. +More converters may exist, consult the +documentation of the Secret Rabbit Code libsamplerate (at http://www.mega-nerd.com/SRC/) for details. +.RS +.HP +Best Sinc Interpolator (0) + +Band limited sinc interpolation, best quality, 97dB SNR, 96% BW. +.HP +Medium Sinc Interpolator (1) + +Band limited sinc interpolation, medium quality, 97dB SNR, 90% BW. +.HP +Fastest Sinc Interpolator (2, default) + +Band limited sinc interpolation, fastest, 97dB SNR, 80% BW. +.HP +ZOH Interpolator (3) + +Zero order hold interpolator, very fast, very poor quality with audible distortions. +.HP +Linear Interpolator (4) + +Linear interpolator, very fast, poor quality. +.RE +.TP .B audio_buffer_size <size in KiB> This specifies the size of the audio output buffer that mpd uses. The default is 2048. diff --git a/doc/mpdconf.example b/doc/mpdconf.example index c7a294214..20ab89ec4 100644 --- a/doc/mpdconf.example +++ b/doc/mpdconf.example @@ -123,6 +123,12 @@ error_file "~/.mpd/mpd.error" # #audio_output_format "44100:16:2" # +# Specifies the libsamplerate converter to use (if compiled in), +# see man 5 mpd.conf for more information. +# +#samplerate_converter <integer or prefix> +# + ################################################################ diff --git a/src/conf.c b/src/conf.c index 0960dfbbc..7dfea6327 100644 --- a/src/conf.c +++ b/src/conf.c @@ -157,6 +157,7 @@ void initConf(void) registerConfigParam(CONF_REPLAYGAIN, 0, 0); registerConfigParam(CONF_REPLAYGAIN_PREAMP, 0, 0); registerConfigParam(CONF_VOLUME_NORMALIZATION, 0, 0); + registerConfigParam(CONF_SAMPLERATE_CONVERTER, 0, 0); registerConfigParam(CONF_AUDIO_BUFFER_SIZE, 0, 0); registerConfigParam(CONF_BUFFER_BEFORE_PLAY, 0, 0); registerConfigParam(CONF_HTTP_BUFFER_SIZE, 0, 0); diff --git a/src/conf.h b/src/conf.h index e2f568525..1cd4cf322 100644 --- a/src/conf.h +++ b/src/conf.h @@ -43,6 +43,7 @@ #define CONF_REPLAYGAIN "replaygain" #define CONF_REPLAYGAIN_PREAMP "replaygain_preamp" #define CONF_VOLUME_NORMALIZATION "volume_normalization" +#define CONF_SAMPLERATE_CONVERTER "samplerate_converter" #define CONF_AUDIO_BUFFER_SIZE "audio_buffer_size" #define CONF_BUFFER_BEFORE_PLAY "buffer_before_play" #define CONF_HTTP_BUFFER_SIZE "http_buffer_size" diff --git a/src/pcm_utils.c b/src/pcm_utils.c index b56ed33ac..f6b308258 100644 --- a/src/pcm_utils.c +++ b/src/pcm_utils.c @@ -21,11 +21,16 @@ #include "mpd_types.h" #include "log.h" #include "utils.h" +#include "conf.h" #include <string.h> #include <math.h> #include <assert.h> +#ifdef HAVE_LIBSAMPLERATE +#include <samplerate.h> +#endif + void pcm_volumeChange(char *buffer, int bufferSize, AudioFormat * format, int volume) { @@ -46,6 +51,9 @@ void pcm_volumeChange(char *buffer, int bufferSize, AudioFormat * format, while (bufferSize > 0) { temp32 = *buffer16; temp32 *= volume; + temp32 += rand() & 511; + temp32 -= rand() & 511; + temp32 += 500; temp32 /= 1000; *buffer16 = temp32 > 32767 ? 32767 : (temp32 < -32768 ? -32768 : temp32); @@ -57,6 +65,9 @@ void pcm_volumeChange(char *buffer, int bufferSize, AudioFormat * format, while (bufferSize > 0) { temp32 = *buffer8; temp32 *= volume; + temp32 += rand() & 511; + temp32 -= rand() & 511; + temp32 += 500; temp32 /= 1000; *buffer8 = temp32 > 127 ? 127 : (temp32 < -128 ? -128 : temp32); @@ -86,7 +97,11 @@ static void pcm_add(char *buffer1, char *buffer2, size_t bufferSize1, while (bufferSize1 > 0 && bufferSize2 > 0) { temp32 = (vol1 * (*buffer16_1) + - vol2 * (*buffer16_2)) / 1000; + vol2 * (*buffer16_2)); + temp32 += rand() & 511; + temp32 -= rand() & 511; + temp32 += 500; + temp32 /= 1000; *buffer16_1 = temp32 > 32767 ? 32767 : (temp32 < -32768 ? -32768 : temp32); @@ -101,7 +116,11 @@ static void pcm_add(char *buffer1, char *buffer2, size_t bufferSize1, case 8: while (bufferSize1 > 0 && bufferSize2 > 0) { temp32 = - (vol1 * (*buffer8_1) + vol2 * (*buffer8_2)) / 1000; + (vol1 * (*buffer8_1) + vol2 * (*buffer8_2)); + temp32 += rand() & 511; + temp32 -= rand() & 511; + temp32 += 500; + temp32 /= 1000; *buffer8_1 = temp32 > 127 ? 127 : (temp32 < -128 ? -128 : temp32); @@ -133,6 +152,38 @@ void pcm_mix(char *buffer1, char *buffer2, size_t bufferSize1, format); } +#ifdef HAVE_LIBSAMPLERATE +static int pcm_getSamplerateConverter(void) { + const char *conf, *test; + int convalgo = SRC_SINC_FASTEST; + int newalgo; + size_t len; + + conf = getConfigParamValue(CONF_SAMPLERATE_CONVERTER); + if(conf) { + newalgo = strtol(conf, (char **)&test, 10); + if(*test) { + len = strlen(conf); + for(newalgo = 0; ; newalgo++) { + test = src_get_name(newalgo); + if(!test) + break; /* FAIL */ + if(!strncasecmp(test, conf, len)) { + convalgo = newalgo; + break; + } + } + } else { + if(src_get_name(newalgo)) + convalgo = newalgo; + /* else FAIL */ + } + } + DEBUG("Selecting samplerate converter '%s'\n", src_get_name(convalgo)); + return convalgo; +} +#endif + /* outFormat bits must be 16 and channels must be 2! */ void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, size_t inSize, AudioFormat * outFormat, char *outBuffer) @@ -234,6 +285,47 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, size_t if (inFormat->sampleRate == outFormat->sampleRate) { memcpy(outBuffer, dataChannelConv, dataChannelLen); } else { +#ifdef HAVE_LIBSAMPLERATE + static SRC_STATE *state = NULL; + static SRC_DATA data; + 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"); + } + } + + newratio = (double)outFormat->sampleRate / (double)inFormat->sampleRate; + if(newratio != ratio) { + src_set_ratio(state, ratio = newratio); + DEBUG("Setting samplerate conversion ratio to %.2lf\n", ratio); + } + + 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; + + float conversionInBuffer[data.input_frames * outFormat->channels]; + float conversionOutBuffer[data.output_frames * outFormat->channels]; + data.data_in = conversionInBuffer; + data.data_out = conversionOutBuffer; + + 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); + } + + src_float_to_short_array(data.data_out, (short *)outBuffer, data.output_frames * outFormat->channels); +#else /* only works if outFormat is 16-bit stereo! */ /* resampling code blatantly ripped from ESD */ mpd_uint32 rd_dat = 0; @@ -241,11 +333,7 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, size_t mpd_sint16 lsample, rsample; mpd_sint16 *out = (mpd_sint16 *) outBuffer; mpd_sint16 *in = (mpd_sint16 *) dataChannelConv; - const int shift = sizeof(mpd_sint16) * outFormat->channels; - mpd_uint32 nlen = (((dataChannelLen / shift) * - (mpd_uint32) (outFormat->sampleRate)) / - inFormat->sampleRate); - nlen *= outFormat->channels; + mpd_uint32 nlen = pcm_sizeOfOutputBufferForAudioFormatConversion(inFormat, inSize, outFormat) / sizeof(mpd_sint16); switch (outFormat->channels) { case 1: @@ -272,6 +360,7 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char *inBuffer, size_t } break; } +#endif } return; @@ -306,9 +395,9 @@ size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, } } - outSize = (((outSize / shift) * (mpd_uint32) (outFormat->sampleRate)) / - inFormat->sampleRate); - + outSize /= shift; + outSize = floor(0.5 + (double)outSize * + ((double)outFormat->sampleRate / (double)inFormat->sampleRate)); outSize *= shift; return outSize; |