diff options
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | doc/mpdconf.example | 7 | ||||
-rw-r--r-- | doc/user.xml | 344 | ||||
-rw-r--r-- | src/config/ConfigOption.hxx | 1 | ||||
-rw-r--r-- | src/config/ConfigTemplates.cxx | 1 | ||||
-rw-r--r-- | src/pcm/ConfiguredResampler.cxx | 123 | ||||
-rw-r--r-- | src/pcm/LibsamplerateResampler.cxx | 4 | ||||
-rw-r--r-- | src/pcm/LibsamplerateResampler.hxx | 4 | ||||
-rw-r--r-- | src/pcm/SoxrResampler.cxx | 22 | ||||
-rw-r--r-- | src/pcm/SoxrResampler.hxx | 3 | ||||
-rw-r--r-- | test/run_convert.cxx | 8 |
12 files changed, 333 insertions, 192 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) \ @@ -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</programlisting> <para> Check the <link linkend="resampler_plugins">resampler plugin - reference</link> for a list of resamplers. + reference</link> for a list of resamplers and how to + configure them. </para> - - <para> - The setting <varname>samplerate_converter</varname> controls - how <application>MPD</application> shall resample music. - Possible values: - </para> - - <informaltable> - <tgroup cols="2"> - <thead> - <row> - <entry> - Value - </entry> - <entry> - Description - </entry> - </row> - </thead> - <tbody> - <row> - <entry> - "<parameter>internal</parameter>" - </entry> - <entry> - The internal resampler. Low CPU usage, but very - poor quality. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr very high</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Very - High Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr high</parameter>" or - "<parameter>soxr</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "High - Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr medium</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Medium - Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr low</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Low - Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr quick</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Quick" - setting. - </entry> - </row> - - <row> - <entry> - "<parameter>Best Sinc Interpolator</parameter>" or - "<parameter>0</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Band - limited sinc interpolation, best quality, 97dB SNR, - 96% BW. - </entry> - </row> - - <row> - <entry> - "<parameter>Medium Sinc Interpolator</parameter>" or - "<parameter>1</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Band - limited sinc interpolation, medium quality, 97dB - SNR, 90% BW. - </entry> - </row> - - <row> - <entry> - "<parameter>Fastest Sinc Interpolator</parameter>" or - "<parameter>2</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Band - limited sinc interpolation, fastest, 97dB SNR, 80% - BW. - </entry> - </row> - - <row> - <entry> - "<parameter>ZOH Sinc Interpolator</parameter>" or - "<parameter>3</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Zero order - hold interpolator, very fast, very poor quality with - audible distortions. - </entry> - </row> - - <row> - <entry> - "<parameter>Linear Interpolator</parameter>" or - "<parameter>4</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Linear - interpolator, very fast, poor quality. - </entry> - </row> - </tbody> - </tgroup> - </informaltable> </section> </section> @@ -2308,6 +2167,46 @@ buffer_size: 16384</programlisting> <section id="resampler_plugins"> <title>Resampler plugins</title> + <para> + The resampler can be configured in a block named + <varname>resampler</varname>, for example: + </para> + + <programlisting>resampler { + plugin "soxr" + quality "very high" +}</programlisting> + + <para> + The following table lists the <varname>resampler</varname> + options valid for all plugins: + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Name + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>plugin</varname> + </entry> + <entry> + The name of the plugin. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + <section id="internal_resampler"> <title><varname>internal</varname></title> @@ -2327,6 +2226,107 @@ buffer_size: 16384</programlisting> url="http://www.mega-nerd.com/SRC/"><application>libsamplerate</application></ulink> a.k.a. Secret Rabbit Code (SRC). </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Name + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>type</varname> + </entry> + <entry> + The interpolator type. See below for a list of + known types. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + + <para> + The following converter types are provided by + <application>libsamplerate</application>: + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Type + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + "<parameter>Best Sinc Interpolator</parameter>" or + "<parameter>0</parameter>" + </entry> + <entry> + Band limited sinc interpolation, best quality, 97dB + SNR, 96% BW. + </entry> + </row> + + <row> + <entry> + "<parameter>Medium Sinc Interpolator</parameter>" or + "<parameter>1</parameter>" + </entry> + <entry> + Band limited sinc interpolation, medium quality, + 97dB SNR, 90% BW. + </entry> + </row> + + <row> + <entry> + "<parameter>Fastest Sinc Interpolator</parameter>" or + "<parameter>2</parameter>" + </entry> + <entry> + Band limited sinc interpolation, fastest, 97dB SNR, + 80% BW. + </entry> + </row> + + <row> + <entry> + "<parameter>ZOH Sinc Interpolator</parameter>" or + "<parameter>3</parameter>" + </entry> + <entry> + Zero order hold interpolator, very fast, very poor + quality with audible distortions. + </entry> + </row> + + <row> + <entry> + "<parameter>Linear Interpolator</parameter>" or + "<parameter>4</parameter>" + </entry> + <entry> + Linear interpolator, very fast, poor quality. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> </section> <section id="soxr_resampler"> @@ -2337,6 +2337,64 @@ buffer_size: 16384</programlisting> url="http://sourceforge.net/projects/soxr/"><application>libsoxr</application></ulink>, the SoX Resampler library </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Name + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>quality</varname> + </entry> + <entry> + The <application>libsoxr</application> quality + setting. Valid values are: + + <itemizedlist> + <listitem> + <para> + "<parameter>very high</parameter>" + </para> + </listitem> + + <listitem> + <para> + "<parameter>high</parameter>" (the default) + </para> + </listitem> + + <listitem> + <para> + "<parameter>medium</parameter>" + </para> + </listitem> + + <listitem> + <para> + "<parameter>low</parameter>" + </para> + </listitem> + + <listitem> + <para> + "<parameter>quick</parameter>" + </para> + </listitem> + </itemizedlist> + </entry> + </row> + </tbody> + </tgroup> + </informaltable> </section> </section> 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; +static const ConfigBlock * +MakeResamplerDefaultConfig(ConfigBlock &block) +{ + assert(block.IsEmpty()); + +#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 (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 + + block.AddBlockParam("plugin", "libsamplerate"); + block.AddBlockParam("type", converter); + return █ +} + +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) { - const char *converter = - config_get_string(ConfigOption::SAMPLERATE_CONVERTER, ""); + 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(converter, "internal") == 0) + if (strcmp(plugin_name, "internal") == 0) { + selected_resampler = SelectedResampler::FALLBACK; return true; - #ifdef ENABLE_SOXR - if (memcmp(converter, "soxr", 4) == 0) { + } else if (strcmp(plugin_name, "soxr") == 0) { selected_resampler = SelectedResampler::SOXR; - return pcm_resample_soxr_global_init(converter, error); - } + return pcm_resample_soxr_global_init(*block, error); #endif - #ifdef ENABLE_LIBSAMPLERATE - selected_resampler = SelectedResampler::LIBSAMPLERATE; - return pcm_resample_lsr_global_init(converter, error); + } else if (strcmp(plugin_name, "libsamplerate") == 0) { + selected_resampler = SelectedResampler::LIBSAMPLERATE; + return pcm_resample_lsr_global_init(*block, error); #endif - - if (*converter == 0) - return true; - - error.Format(config_domain, - "The samplerate_converter '%s' is not available", - converter); - return false; + } 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 <samplerate.h> +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 <stdlib.h> #include <unistd.h> -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; |