Commit 0d3b26b3 authored by Max Kellermann's avatar Max Kellermann

Config: add section "resampler"

parent 52acea7b
...@@ -1951,14 +1951,17 @@ test_run_normalize_LDADD = \ ...@@ -1951,14 +1951,17 @@ test_run_normalize_LDADD = \
$(GLIB_LIBS) $(GLIB_LIBS)
test_run_convert_SOURCES = test/run_convert.cxx \ test_run_convert_SOURCES = test/run_convert.cxx \
src/config/ConfigError.cxx \
src/Log.cxx src/LogBackend.cxx \ src/Log.cxx src/LogBackend.cxx \
src/AudioFormat.cxx \ src/AudioFormat.cxx \
src/CheckAudioFormat.cxx \ src/CheckAudioFormat.cxx \
src/AudioParser.cxx src/AudioParser.cxx
test_run_convert_LDADD = \ test_run_convert_LDADD = \
$(PCM_LIBS) \ $(PCM_LIBS) \
libconf.a \
$(FS_LIBS) \
libsystem.a \
libutil.a \ libutil.a \
$(ICU_LDADD) \
$(GLIB_LIBS) $(GLIB_LIBS)
test_run_output_LDADD = $(MPD_LIBS) \ test_run_output_LDADD = $(MPD_LIBS) \
......
...@@ -18,6 +18,9 @@ ver 0.20 (not yet released) ...@@ -18,6 +18,9 @@ ver 0.20 (not yet released)
- recorder: allow dynamic file names - recorder: allow dynamic file names
* mixer * mixer
- null: new plugin - null: new plugin
* resampler
- new block "resampler" in configuration file
replacing the old "samplerate_converter" setting
* reset song priority on playback * reset song priority on playback
* write database and state file atomically * write database and state file atomically
* remove dependency on GLib * remove dependency on GLib
......
...@@ -325,13 +325,6 @@ input { ...@@ -325,13 +325,6 @@ input {
# mixer_type "none" # optional # 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"
#
############################################################################### ###############################################################################
......
...@@ -86,6 +86,7 @@ enum class ConfigBlockOption { ...@@ -86,6 +86,7 @@ enum class ConfigBlockOption {
DECODER, DECODER,
INPUT, INPUT,
PLAYLIST_PLUGIN, PLAYLIST_PLUGIN,
RESAMPLER,
AUDIO_FILTER, AUDIO_FILTER,
DATABASE, DATABASE,
NEIGHBORS, NEIGHBORS,
......
...@@ -86,6 +86,7 @@ const ConfigTemplate config_block_templates[] = { ...@@ -86,6 +86,7 @@ const ConfigTemplate config_block_templates[] = {
{ "decoder", true }, { "decoder", true },
{ "input", true }, { "input", true },
{ "playlist_plugin", true }, { "playlist_plugin", true },
{ "resampler", false },
{ "filter", true }, { "filter", true },
{ "database", false }, { "database", false },
{ "neighbors", true }, { "neighbors", true },
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
#include "config/ConfigGlobal.hxx" #include "config/ConfigGlobal.hxx"
#include "config/ConfigOption.hxx" #include "config/ConfigOption.hxx"
#include "config/ConfigError.hxx" #include "config/ConfigError.hxx"
#include "config/Block.hxx"
#include "config/Param.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#ifdef ENABLE_LIBSAMPLERATE #ifdef ENABLE_LIBSAMPLERATE
...@@ -49,34 +51,119 @@ enum class SelectedResampler { ...@@ -49,34 +51,119 @@ enum class SelectedResampler {
static SelectedResampler selected_resampler = SelectedResampler::FALLBACK; 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 &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 █
}
#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 bool
pcm_resampler_global_init(Error &error) pcm_resampler_global_init(Error &error)
{ {
const char *converter = ConfigBlock buffer;
config_get_string(ConfigOption::SAMPLERATE_CONVERTER, ""); 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; return true;
#ifdef ENABLE_SOXR #ifdef ENABLE_SOXR
if (memcmp(converter, "soxr", 4) == 0) { } else if (strcmp(plugin_name, "soxr") == 0) {
selected_resampler = SelectedResampler::SOXR; selected_resampler = SelectedResampler::SOXR;
return pcm_resample_soxr_global_init(converter, error); return pcm_resample_soxr_global_init(*block, error);
}
#endif #endif
#ifdef ENABLE_LIBSAMPLERATE #ifdef ENABLE_LIBSAMPLERATE
selected_resampler = SelectedResampler::LIBSAMPLERATE; } else if (strcmp(plugin_name, "libsamplerate") == 0) {
return pcm_resample_lsr_global_init(converter, error); selected_resampler = SelectedResampler::LIBSAMPLERATE;
return pcm_resample_lsr_global_init(*block, error);
#endif #endif
} else {
if (*converter == 0) error.Format(config_domain,
return true; "No such resampler plugin: %s",
plugin_name);
error.Format(config_domain, return false;
"The samplerate_converter '%s' is not available", }
converter);
return false;
} }
PcmResampler * PcmResampler *
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "LibsamplerateResampler.hxx" #include "LibsamplerateResampler.hxx"
#include "config/Block.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
...@@ -63,8 +64,9 @@ lsr_parse_converter(const char *s) ...@@ -63,8 +64,9 @@ lsr_parse_converter(const char *s)
} }
bool 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)) { if (!lsr_parse_converter(converter)) {
error.Format(libsamplerate_domain, error.Format(libsamplerate_domain,
"unknown samplerate converter '%s'", converter); "unknown samplerate converter '%s'", converter);
......
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include <samplerate.h> #include <samplerate.h>
struct ConfigBlock;
/** /**
* A resampler using libsamplerate. * A resampler using libsamplerate.
*/ */
...@@ -51,6 +53,6 @@ private: ...@@ -51,6 +53,6 @@ private:
}; };
bool bool
pcm_resample_lsr_global_init(const char *converter, Error &error); pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error);
#endif #endif
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "SoxrResampler.hxx" #include "SoxrResampler.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "config/Block.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
...@@ -62,18 +63,11 @@ soxr_quality_name(unsigned long recipe) ...@@ -62,18 +63,11 @@ soxr_quality_name(unsigned long recipe)
gcc_pure gcc_pure
static unsigned long static unsigned long
soxr_parse_converter(const char *converter) soxr_parse_quality(const char *quality)
{ {
assert(converter != nullptr); if (quality == nullptr)
assert(memcmp(converter, "soxr", 4) == 0);
if (converter[4] == '\0')
return SOXR_DEFAULT_RECIPE; 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) if (strcmp(quality, "very high") == 0)
return SOXR_VHQ; return SOXR_VHQ;
else if (strcmp(quality, "high") == 0) else if (strcmp(quality, "high") == 0)
...@@ -89,12 +83,16 @@ soxr_parse_converter(const char *converter) ...@@ -89,12 +83,16 @@ soxr_parse_converter(const char *converter)
} }
bool 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) { if (recipe == SOXR_INVALID_RECIPE) {
assert(quality_string != nullptr);
error.Format(soxr_domain, error.Format(soxr_domain,
"unknown samplerate converter '%s'", converter); "unknown quality setting '%s' in line %d",
quality_string, block.line);
return false; return false;
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "Compiler.h" #include "Compiler.h"
struct AudioFormat; struct AudioFormat;
struct ConfigBlock;
/** /**
* A resampler using soxr. * A resampler using soxr.
...@@ -46,6 +47,6 @@ public: ...@@ -46,6 +47,6 @@ public:
}; };
bool bool
pcm_resample_soxr_global_init(const char *converter, Error &error); pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error);
#endif #endif
...@@ -27,7 +27,6 @@ ...@@ -27,7 +27,6 @@
#include "AudioParser.hxx" #include "AudioParser.hxx"
#include "AudioFormat.hxx" #include "AudioFormat.hxx"
#include "pcm/PcmConvert.hxx" #include "pcm/PcmConvert.hxx"
#include "config/ConfigGlobal.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/StaticFifoBuffer.hxx" #include "util/StaticFifoBuffer.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
...@@ -39,13 +38,6 @@ ...@@ -39,13 +38,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.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) int main(int argc, char **argv)
{ {
AudioFormat in_audio_format, out_audio_format; AudioFormat in_audio_format, out_audio_format;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment