summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am5
-rw-r--r--NEWS3
-rw-r--r--doc/mpdconf.example7
-rw-r--r--doc/user.xml344
-rw-r--r--src/config/ConfigOption.hxx1
-rw-r--r--src/config/ConfigTemplates.cxx1
-rw-r--r--src/pcm/ConfiguredResampler.cxx123
-rw-r--r--src/pcm/LibsamplerateResampler.cxx4
-rw-r--r--src/pcm/LibsamplerateResampler.hxx4
-rw-r--r--src/pcm/SoxrResampler.cxx22
-rw-r--r--src/pcm/SoxrResampler.hxx3
-rw-r--r--test/run_convert.cxx8
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) \
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</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 &block;
+}
+
+/**
+ * Convert the old "samplerate_converter" setting to a new-style
+ * "resampler" block.
+ */
+static const ConfigBlock *
+MigrateResamplerConfig(const config_param &param, 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 &block;
+ }
+
+#ifdef ENABLE_SOXR
+ if (strcmp(converter, "soxr") == 0) {
+ block.AddBlockParam("plugin", "soxr");
+ return &block;
+ }
+
+ if (memcmp(converter, "soxr ", 5) == 0) {
+ block.AddBlockParam("plugin", "soxr");
+ block.AddBlockParam("quality", converter + 5);
+ return &block;
+ }
+#endif
+
+ block.AddBlockParam("plugin", "libsamplerate");
+ block.AddBlockParam("type", converter);
+ return &block;
+}
+
+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;