From 0d3b26b3aaee6646ce5e825feca43299365797ec Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 21 Jan 2015 20:42:41 +0100 Subject: [PATCH] Config: add section "resampler" --- Makefile.am | 5 +- NEWS | 3 + doc/mpdconf.example | 7 - doc/user.xml | 344 +++++++++++++++++------------ src/config/ConfigOption.hxx | 1 + src/config/ConfigTemplates.cxx | 1 + src/pcm/ConfiguredResampler.cxx | 125 +++++++++-- src/pcm/LibsamplerateResampler.cxx | 4 +- src/pcm/LibsamplerateResampler.hxx | 4 +- src/pcm/SoxrResampler.cxx | 22 +- src/pcm/SoxrResampler.hxx | 3 +- test/run_convert.cxx | 8 - 12 files changed, 334 insertions(+), 193 deletions(-) diff --git a/Makefile.am b/Makefile.am index 349e00e14..1bbf9d2b1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1951,14 +1951,17 @@ test_run_normalize_LDADD = \ $(GLIB_LIBS) test_run_convert_SOURCES = test/run_convert.cxx \ - src/config/ConfigError.cxx \ src/Log.cxx src/LogBackend.cxx \ src/AudioFormat.cxx \ src/CheckAudioFormat.cxx \ src/AudioParser.cxx test_run_convert_LDADD = \ $(PCM_LIBS) \ + libconf.a \ + $(FS_LIBS) \ + libsystem.a \ libutil.a \ + $(ICU_LDADD) \ $(GLIB_LIBS) test_run_output_LDADD = $(MPD_LIBS) \ diff --git a/NEWS b/NEWS index b8a460938..a1e57d65a 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ ver 0.20 (not yet released) - recorder: allow dynamic file names * mixer - null: new plugin +* resampler + - new block "resampler" in configuration file + replacing the old "samplerate_converter" setting * reset song priority on playback * write database and state file atomically * remove dependency on GLib diff --git a/doc/mpdconf.example b/doc/mpdconf.example index 7a2db5d16..73a71f1f4 100644 --- a/doc/mpdconf.example +++ b/doc/mpdconf.example @@ -325,13 +325,6 @@ input { # mixer_type "none" # optional #} # -# If MPD has been compiled with libsamplerate support, this setting specifies -# the sample rate converter to use. Possible values can be found in the -# mpd.conf man page or the libsamplerate documentation. By default, this is -# setting is disabled. -# -#samplerate_converter "Fastest Sinc Interpolator" -# ############################################################################### diff --git a/doc/user.xml b/doc/user.xml index 914a89620..310b50d53 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -771,150 +771,9 @@ systemctl start mpd.socket Check the resampler plugin - reference for a list of resamplers. + reference for a list of resamplers and how to + configure them. - - - The setting samplerate_converter controls - how MPD shall resample music. - Possible values: - - - - - - - - Value - - - Description - - - - - - - "internal" - - - The internal resampler. Low CPU usage, but very - poor quality. - - - - - - "soxr very high" - - - Use libsoxr with "Very - High Quality" setting. - - - - - - "soxr high" or - "soxr" - - - Use libsoxr with "High - Quality" setting. - - - - - - "soxr medium" - - - Use libsoxr with "Medium - Quality" setting. - - - - - - "soxr low" - - - Use libsoxr with "Low - Quality" setting. - - - - - - "soxr quick" - - - Use libsoxr with "Quick" - setting. - - - - - - "Best Sinc Interpolator" or - "0" - - - libsamplerate: Band - limited sinc interpolation, best quality, 97dB SNR, - 96% BW. - - - - - - "Medium Sinc Interpolator" or - "1" - - - libsamplerate: Band - limited sinc interpolation, medium quality, 97dB - SNR, 90% BW. - - - - - - "Fastest Sinc Interpolator" or - "2" - - - libsamplerate: Band - limited sinc interpolation, fastest, 97dB SNR, 80% - BW. - - - - - - "ZOH Sinc Interpolator" or - "3" - - - libsamplerate: Zero order - hold interpolator, very fast, very poor quality with - audible distortions. - - - - - - "Linear Interpolator" or - "4" - - - libsamplerate: Linear - interpolator, very fast, poor quality. - - - - - @@ -2308,6 +2167,46 @@ buffer_size: 16384
Resampler plugins + + The resampler can be configured in a block named + resampler, for example: + + + resampler { + plugin "soxr" + quality "very high" +} + + + The following table lists the resampler + options valid for all plugins: + + + + + + + + Name + + + Description + + + + + + + plugin + + + The name of the plugin. + + + + + +
<varname>internal</varname> @@ -2327,6 +2226,107 @@ buffer_size: 16384 url="http://www.mega-nerd.com/SRC/">libsamplerate a.k.a. Secret Rabbit Code (SRC). + + + + + + + Name + + + Description + + + + + + + type + + + The interpolator type. See below for a list of + known types. + + + + + + + + The following converter types are provided by + libsamplerate: + + + + + + + + Type + + + Description + + + + + + + "Best Sinc Interpolator" or + "0" + + + Band limited sinc interpolation, best quality, 97dB + SNR, 96% BW. + + + + + + "Medium Sinc Interpolator" or + "1" + + + Band limited sinc interpolation, medium quality, + 97dB SNR, 90% BW. + + + + + + "Fastest Sinc Interpolator" or + "2" + + + Band limited sinc interpolation, fastest, 97dB SNR, + 80% BW. + + + + + + "ZOH Sinc Interpolator" or + "3" + + + Zero order hold interpolator, very fast, very poor + quality with audible distortions. + + + + + + "Linear Interpolator" or + "4" + + + Linear interpolator, very fast, poor quality. + + + + +
@@ -2337,6 +2337,64 @@ buffer_size: 16384 url="http://sourceforge.net/projects/soxr/">libsoxr, the SoX Resampler library + + + + + + + Name + + + Description + + + + + + + quality + + + The libsoxr quality + setting. Valid values are: + + + + + "very high" + + + + + + "high" (the default) + + + + + + "medium" + + + + + + "low" + + + + + + "quick" + + + + + + + +
diff --git a/src/config/ConfigOption.hxx b/src/config/ConfigOption.hxx index 8cc0d42d5..5acd6fd49 100644 --- a/src/config/ConfigOption.hxx +++ b/src/config/ConfigOption.hxx @@ -86,6 +86,7 @@ enum class ConfigBlockOption { DECODER, INPUT, PLAYLIST_PLUGIN, + RESAMPLER, AUDIO_FILTER, DATABASE, NEIGHBORS, diff --git a/src/config/ConfigTemplates.cxx b/src/config/ConfigTemplates.cxx index 1ceef9119..6fbf025fc 100644 --- a/src/config/ConfigTemplates.cxx +++ b/src/config/ConfigTemplates.cxx @@ -86,6 +86,7 @@ const ConfigTemplate config_block_templates[] = { { "decoder", true }, { "input", true }, { "playlist_plugin", true }, + { "resampler", false }, { "filter", true }, { "database", false }, { "neighbors", true }, diff --git a/src/pcm/ConfiguredResampler.cxx b/src/pcm/ConfiguredResampler.cxx index bf6ea58ee..30cb801c7 100644 --- a/src/pcm/ConfiguredResampler.cxx +++ b/src/pcm/ConfiguredResampler.cxx @@ -23,6 +23,8 @@ #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "config/ConfigError.hxx" +#include "config/Block.hxx" +#include "config/Param.hxx" #include "util/Error.hxx" #ifdef ENABLE_LIBSAMPLERATE @@ -49,34 +51,119 @@ enum class SelectedResampler { static SelectedResampler selected_resampler = SelectedResampler::FALLBACK; -bool -pcm_resampler_global_init(Error &error) +static const ConfigBlock * +MakeResamplerDefaultConfig(ConfigBlock &block) { - const char *converter = - config_get_string(ConfigOption::SAMPLERATE_CONVERTER, ""); + assert(block.IsEmpty()); - if (strcmp(converter, "internal") == 0) - return true; +#ifdef ENABLE_LIBSAMPLERATE + block.AddBlockParam("plugin", "libsamplerate"); +#elif defined(ENABLE_SOXR) + block.AddBlockParam("plugin", "soxr"); +#else + block.AddBlockParam("plugin", "internal"); +#endif + return █ +} + +/** + * Convert the old "samplerate_converter" setting to a new-style + * "resampler" block. + */ +static const ConfigBlock * +MigrateResamplerConfig(const config_param ¶m, ConfigBlock &block) +{ + assert(block.IsEmpty()); + + block.line = param.line; + + const char *converter = param.value.c_str(); + if (*converter == 0 || strcmp(converter, "internal") == 0) { + block.AddBlockParam("plugin", "internal"); + return █ + } #ifdef ENABLE_SOXR - if (memcmp(converter, "soxr", 4) == 0) { - selected_resampler = SelectedResampler::SOXR; - return pcm_resample_soxr_global_init(converter, error); + if (strcmp(converter, "soxr") == 0) { + block.AddBlockParam("plugin", "soxr"); + return █ + } + + if (memcmp(converter, "soxr ", 5) == 0) { + block.AddBlockParam("plugin", "soxr"); + block.AddBlockParam("quality", converter + 5); + return █ } #endif -#ifdef ENABLE_LIBSAMPLERATE - selected_resampler = SelectedResampler::LIBSAMPLERATE; - return pcm_resample_lsr_global_init(converter, error); -#endif + block.AddBlockParam("plugin", "libsamplerate"); + block.AddBlockParam("type", converter); + return █ +} - if (*converter == 0) +static const ConfigBlock * +MigrateResamplerConfig(const config_param *param, ConfigBlock &buffer) +{ + assert(buffer.IsEmpty()); + + return param == nullptr + ? MakeResamplerDefaultConfig(buffer) + : MigrateResamplerConfig(*param, buffer); +} + +static const ConfigBlock * +GetResamplerConfig(ConfigBlock &buffer, Error &error) +{ + const auto *old_param = + config_get_param(ConfigOption::SAMPLERATE_CONVERTER); + const auto *block = config_get_block(ConfigBlockOption::RESAMPLER); + if (block == nullptr) + return MigrateResamplerConfig(old_param, buffer); + + if (old_param != nullptr) { + error.Format(config_domain, + "Cannot use both 'resampler' (line %d) and 'samplerate_converter' (line %d)", + block->line, old_param->line); + return nullptr; + } + + return block; +} + +bool +pcm_resampler_global_init(Error &error) +{ + ConfigBlock buffer; + const auto *block = GetResamplerConfig(buffer, error); + if (block == nullptr) + return false; + + const char *plugin_name = block->GetBlockValue("plugin"); + if (plugin_name == nullptr) { + error.Format(config_domain, + "'plugin' missing in line %d", block->line); + return false; + } + + if (strcmp(plugin_name, "internal") == 0) { + selected_resampler = SelectedResampler::FALLBACK; return true; - - error.Format(config_domain, - "The samplerate_converter '%s' is not available", - converter); - return false; +#ifdef ENABLE_SOXR + } else if (strcmp(plugin_name, "soxr") == 0) { + selected_resampler = SelectedResampler::SOXR; + return pcm_resample_soxr_global_init(*block, error); +#endif +#ifdef ENABLE_LIBSAMPLERATE + } else if (strcmp(plugin_name, "libsamplerate") == 0) { + selected_resampler = SelectedResampler::LIBSAMPLERATE; + return pcm_resample_lsr_global_init(*block, error); +#endif + } else { + error.Format(config_domain, + "No such resampler plugin: %s", + plugin_name); + return false; + } } PcmResampler * diff --git a/src/pcm/LibsamplerateResampler.cxx b/src/pcm/LibsamplerateResampler.cxx index 73ed33644..cc6f3d43f 100644 --- a/src/pcm/LibsamplerateResampler.cxx +++ b/src/pcm/LibsamplerateResampler.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "LibsamplerateResampler.hxx" +#include "config/Block.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -63,8 +64,9 @@ lsr_parse_converter(const char *s) } bool -pcm_resample_lsr_global_init(const char *converter, Error &error) +pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error) { + const char *converter = block.GetBlockValue("type", "2"); if (!lsr_parse_converter(converter)) { error.Format(libsamplerate_domain, "unknown samplerate converter '%s'", converter); diff --git a/src/pcm/LibsamplerateResampler.hxx b/src/pcm/LibsamplerateResampler.hxx index 1ae70ef71..f19dc19eb 100644 --- a/src/pcm/LibsamplerateResampler.hxx +++ b/src/pcm/LibsamplerateResampler.hxx @@ -27,6 +27,8 @@ #include +struct ConfigBlock; + /** * A resampler using libsamplerate. */ @@ -51,6 +53,6 @@ private: }; bool -pcm_resample_lsr_global_init(const char *converter, Error &error); +pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error); #endif diff --git a/src/pcm/SoxrResampler.cxx b/src/pcm/SoxrResampler.cxx index 878c95337..3c271261b 100644 --- a/src/pcm/SoxrResampler.cxx +++ b/src/pcm/SoxrResampler.cxx @@ -20,6 +20,7 @@ #include "config.h" #include "SoxrResampler.hxx" #include "AudioFormat.hxx" +#include "config/Block.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -62,18 +63,11 @@ soxr_quality_name(unsigned long recipe) gcc_pure static unsigned long -soxr_parse_converter(const char *converter) +soxr_parse_quality(const char *quality) { - assert(converter != nullptr); - - assert(memcmp(converter, "soxr", 4) == 0); - if (converter[4] == '\0') + if (quality == nullptr) return SOXR_DEFAULT_RECIPE; - if (converter[4] != ' ') - return SOXR_INVALID_RECIPE; - // converter example is "soxr very high", we want the "very high" part - const char *quality = converter + 5; if (strcmp(quality, "very high") == 0) return SOXR_VHQ; else if (strcmp(quality, "high") == 0) @@ -89,12 +83,16 @@ soxr_parse_converter(const char *converter) } bool -pcm_resample_soxr_global_init(const char *converter, Error &error) +pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error) { - unsigned long recipe = soxr_parse_converter(converter); + const char *quality_string = block.GetBlockValue("quality"); + unsigned long recipe = soxr_parse_quality(quality_string); if (recipe == SOXR_INVALID_RECIPE) { + assert(quality_string != nullptr); + error.Format(soxr_domain, - "unknown samplerate converter '%s'", converter); + "unknown quality setting '%s' in line %d", + quality_string, block.line); return false; } diff --git a/src/pcm/SoxrResampler.hxx b/src/pcm/SoxrResampler.hxx index 7756bcea8..6c31ca45a 100644 --- a/src/pcm/SoxrResampler.hxx +++ b/src/pcm/SoxrResampler.hxx @@ -25,6 +25,7 @@ #include "Compiler.h" struct AudioFormat; +struct ConfigBlock; /** * A resampler using soxr. @@ -46,6 +47,6 @@ public: }; bool -pcm_resample_soxr_global_init(const char *converter, Error &error); +pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error); #endif diff --git a/test/run_convert.cxx b/test/run_convert.cxx index 7673c25ca..1929d6c02 100644 --- a/test/run_convert.cxx +++ b/test/run_convert.cxx @@ -27,7 +27,6 @@ #include "AudioParser.hxx" #include "AudioFormat.hxx" #include "pcm/PcmConvert.hxx" -#include "config/ConfigGlobal.hxx" #include "util/ConstBuffer.hxx" #include "util/StaticFifoBuffer.hxx" #include "util/Error.hxx" @@ -39,13 +38,6 @@ #include #include -const char * -config_get_string(gcc_unused enum ConfigOption option, - const char *default_value) -{ - return default_value; -} - int main(int argc, char **argv) { AudioFormat in_audio_format, out_audio_format;