aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2013-11-11 22:31:46 +0100
committerMax Kellermann <max@duempel.org>2013-11-30 16:22:57 +0100
commit5ba90cd8ea87cf97d64409b7b4c59033c2450c77 (patch)
treeeec1565c805de7bc142de82e5100cc3c1f86aa8d /src
parente9127523db55a267f67532fd61e913f2879324fc (diff)
downloadmpd-5ba90cd8ea87cf97d64409b7b4c59033c2450c77.tar.gz
mpd-5ba90cd8ea87cf97d64409b7b4c59033c2450c77.tar.xz
mpd-5ba90cd8ea87cf97d64409b7b4c59033c2450c77.zip
pcm/PcmResampler: convert to abstract interface
The PcmResampler interface is implemented by the two classes FallbackPcmResampler and LibsampleratePcmResampler. This prepares for adding more resampler libraries.
Diffstat (limited to 'src')
-rw-r--r--src/pcm/ConfiguredResampler.cxx63
-rw-r--r--src/pcm/ConfiguredResampler.hxx38
-rw-r--r--src/pcm/FallbackResampler.cxx147
-rw-r--r--src/pcm/FallbackResampler.hxx45
-rw-r--r--src/pcm/GlueResampler.cxx99
-rw-r--r--src/pcm/GlueResampler.hxx27
-rw-r--r--src/pcm/LibsamplerateResampler.cxx163
-rw-r--r--src/pcm/LibsamplerateResampler.hxx55
-rw-r--r--src/pcm/PcmConvert.cxx62
-rw-r--r--src/pcm/PcmConvert.hxx4
-rw-r--r--src/pcm/PcmFormat.cxx1
-rw-r--r--src/pcm/PcmResample.cxx173
-rw-r--r--src/pcm/PcmResample.hxx133
-rw-r--r--src/pcm/PcmResampleFallback.cxx106
-rw-r--r--src/pcm/PcmResampleInternal.hxx100
-rw-r--r--src/pcm/PcmResampleLibsamplerate.cxx290
-rw-r--r--src/pcm/Resampler.hxx74
17 files changed, 694 insertions, 886 deletions
diff --git a/src/pcm/ConfiguredResampler.cxx b/src/pcm/ConfiguredResampler.cxx
new file mode 100644
index 000000000..92f3d0903
--- /dev/null
+++ b/src/pcm/ConfiguredResampler.cxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ConfiguredResampler.hxx"
+#include "FallbackResampler.hxx"
+
+#ifdef HAVE_LIBSAMPLERATE
+#include "LibsamplerateResampler.hxx"
+#include "ConfigGlobal.hxx"
+#include "ConfigOption.hxx"
+#endif
+
+#include <string.h>
+
+#ifdef HAVE_LIBSAMPLERATE
+static bool lsr_enabled;
+#endif
+
+bool
+pcm_resampler_global_init(Error &error)
+{
+#ifdef HAVE_LIBSAMPLERATE
+ const char *converter =
+ config_get_string(CONF_SAMPLERATE_CONVERTER, "");
+
+ lsr_enabled = strcmp(converter, "internal") != 0;
+ if (lsr_enabled)
+ return pcm_resample_lsr_global_init(converter, error);
+ else
+ return true;
+#else
+ (void)error;
+ return true;
+#endif
+}
+
+PcmResampler *
+pcm_resampler_create()
+{
+#ifdef HAVE_LIBSAMPLERATE
+ if (lsr_enabled)
+ return new LibsampleratePcmResampler();
+#endif
+
+ return new FallbackPcmResampler();
+}
diff --git a/src/pcm/ConfiguredResampler.hxx b/src/pcm/ConfiguredResampler.hxx
new file mode 100644
index 000000000..6d12ee9c6
--- /dev/null
+++ b/src/pcm/ConfiguredResampler.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_CONFIGURED_RESAMPLER_HXX
+#define MPD_CONFIGURED_RESAMPLER_HXX
+
+#include "check.h"
+
+class Error;
+class PcmResampler;
+
+bool
+pcm_resampler_global_init(Error &error);
+
+/**
+ * Create a #PcmResampler instance from the implementation class
+ * configured in mpd.conf.
+ */
+PcmResampler *
+pcm_resampler_create();
+
+#endif
diff --git a/src/pcm/FallbackResampler.cxx b/src/pcm/FallbackResampler.cxx
new file mode 100644
index 000000000..a3b6b78ee
--- /dev/null
+++ b/src/pcm/FallbackResampler.cxx
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FallbackResampler.hxx"
+
+#include <assert.h>
+
+AudioFormat
+FallbackPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate,
+ gcc_unused Error &error)
+{
+ assert(af.IsValid());
+ assert(audio_valid_sample_rate(new_sample_rate));
+
+ switch (af.format) {
+ case SampleFormat::UNDEFINED:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S8:
+ af.format = SampleFormat::S16;
+ break;
+
+ case SampleFormat::S16:
+ case SampleFormat::FLOAT:
+ case SampleFormat::S24_P32:
+ case SampleFormat::S32:
+ break;
+
+ case SampleFormat::DSD:
+ af.format = SampleFormat::FLOAT;
+ break;
+ }
+
+ format = af;
+ out_rate = new_sample_rate;
+
+ AudioFormat result = af;
+ result.sample_rate = new_sample_rate;
+ return result;
+}
+
+void
+FallbackPcmResampler::Close()
+{
+}
+
+template<typename T>
+static ConstBuffer<T>
+pcm_resample_fallback(PcmBuffer &buffer,
+ unsigned channels,
+ unsigned src_rate,
+ ConstBuffer<T> src,
+ unsigned dest_rate)
+{
+ unsigned dest_pos = 0;
+ unsigned src_frames = src.size / channels;
+ unsigned dest_frames =
+ (src_frames * dest_rate + src_rate - 1) / src_rate;
+ unsigned dest_samples = dest_frames * channels;
+ size_t dest_size = dest_samples * sizeof(*src.data);
+ T *dest_buffer = (T *)buffer.Get(dest_size);
+
+ assert((src.size % channels) == 0);
+
+ switch (channels) {
+ case 1:
+ while (dest_pos < dest_samples) {
+ unsigned src_pos = dest_pos * src_rate / dest_rate;
+
+ dest_buffer[dest_pos++] = src.data[src_pos];
+ }
+ break;
+ case 2:
+ while (dest_pos < dest_samples) {
+ unsigned src_pos = dest_pos * src_rate / dest_rate;
+ src_pos &= ~1;
+
+ dest_buffer[dest_pos++] = src.data[src_pos];
+ dest_buffer[dest_pos++] = src.data[src_pos + 1];
+ }
+ break;
+ }
+
+ return { dest_buffer, dest_samples };
+}
+
+template<typename T>
+static ConstBuffer<void>
+pcm_resample_fallback_void(PcmBuffer &buffer,
+ unsigned channels,
+ unsigned src_rate,
+ ConstBuffer<void> src,
+ unsigned dest_rate)
+{
+ const auto typed_src = ConstBuffer<T>::FromVoid(src);
+ return pcm_resample_fallback(buffer, channels, src_rate, typed_src,
+ dest_rate);
+}
+
+ConstBuffer<void>
+FallbackPcmResampler::Resample(ConstBuffer<void> src, gcc_unused Error &error)
+{
+ switch (format.format) {
+ case SampleFormat::UNDEFINED:
+ case SampleFormat::S8:
+ case SampleFormat::DSD:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S16:
+ return pcm_resample_fallback_void<int16_t>(buffer,
+ format.channels,
+ format.sample_rate,
+ src,
+ out_rate);
+
+ case SampleFormat::FLOAT:
+ case SampleFormat::S24_P32:
+ case SampleFormat::S32:
+ return pcm_resample_fallback_void<int32_t>(buffer,
+ format.channels,
+ format.sample_rate,
+ src,
+ out_rate);
+ }
+
+ assert(false);
+ gcc_unreachable();
+}
diff --git a/src/pcm/FallbackResampler.hxx b/src/pcm/FallbackResampler.hxx
new file mode 100644
index 000000000..1b8d0377d
--- /dev/null
+++ b/src/pcm/FallbackResampler.hxx
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_FALLBACK_RESAMPLER_HXX
+#define MPD_PCM_FALLBACK_RESAMPLER_HXX
+
+#include "Resampler.hxx"
+#include "PcmBuffer.hxx"
+#include "AudioFormat.hxx"
+
+/**
+ * A naive resampler that is used when no external library was found
+ * (or when the user explicitly asks for bad quality).
+ */
+class FallbackPcmResampler final : public PcmResampler {
+ AudioFormat format;
+ unsigned out_rate;
+
+ PcmBuffer buffer;
+
+public:
+ virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate,
+ Error &error) override;
+ virtual void Close() override;
+ virtual ConstBuffer<void> Resample(ConstBuffer<void> src,
+ Error &error) override;
+};
+
+#endif
diff --git a/src/pcm/GlueResampler.cxx b/src/pcm/GlueResampler.cxx
index 8aee98f38..ef80e08a5 100644
--- a/src/pcm/GlueResampler.cxx
+++ b/src/pcm/GlueResampler.cxx
@@ -19,76 +19,67 @@
#include "config.h"
#include "GlueResampler.hxx"
-#include "PcmConvert.hxx"
-#include "PcmFormat.hxx"
-#include "util/ConstBuffer.hxx"
-#include "util/Error.hxx"
+#include "ConfiguredResampler.hxx"
+#include "Resampler.hxx"
+
+#include <assert.h>
+
+GluePcmResampler::GluePcmResampler()
+ :resampler(pcm_resampler_create()) {}
+
+GluePcmResampler::~GluePcmResampler()
+{
+ delete resampler;
+}
bool
-GluePcmResampler::Open(AudioFormat _src_format, unsigned _new_sample_rate,
- gcc_unused Error &error)
+GluePcmResampler::Open(AudioFormat src_format, unsigned new_sample_rate,
+ Error &error)
{
- src_format = _src_format;
- new_sample_rate = _new_sample_rate;
+ assert(src_format.IsValid());
+ assert(audio_valid_sample_rate(new_sample_rate));
+
+ AudioFormat requested_format = src_format;
+ AudioFormat dest_format = resampler->Open(requested_format,
+ new_sample_rate,
+ error);
+ if (!dest_format.IsValid())
+ return false;
+
+ assert(requested_format.channels == src_format.channels);
+ assert(dest_format.channels == src_format.channels);
+ assert(dest_format.sample_rate == new_sample_rate);
+ if (requested_format.format != src_format.format &&
+ !format_converter.Open(src_format.format, requested_format.format,
+ error))
+ return false;
+
+ src_sample_format = src_format.format;
+ requested_sample_format = requested_format.format;
+ output_sample_format = dest_format.format;
return true;
}
void
GluePcmResampler::Close()
{
- resampler.Reset();
+ if (requested_sample_format != src_sample_format)
+ format_converter.Close();
+
+ resampler->Close();
}
ConstBuffer<void>
GluePcmResampler::Resample(ConstBuffer<void> src, Error &error)
{
- const void *result;
- size_t size;
-
- switch (src_format.format) {
- case SampleFormat::S16:
- result = resampler.Resample16(src_format.channels,
- src_format.sample_rate,
- (const int16_t *)src.data,
- src.size,
- new_sample_rate, &size,
- error);
- break;
-
- case SampleFormat::S24_P32:
- result = resampler.Resample24(src_format.channels,
- src_format.sample_rate,
- (const int32_t *)src.data,
- src.size,
- new_sample_rate, &size,
- error);
- break;
-
- case SampleFormat::S32:
- result = resampler.Resample24(src_format.channels,
- src_format.sample_rate,
- (const int32_t *)src.data,
- src.size,
- new_sample_rate, &size,
- error);
- break;
-
- case SampleFormat::FLOAT:
- result = resampler.ResampleFloat(src_format.channels,
- src_format.sample_rate,
- (const float *)src.data,
- src.size,
- new_sample_rate, &size,
- error);
- break;
+ assert(!src.IsNull());
- default:
- error.Format(pcm_convert_domain,
- "Resampling %s is not implemented",
- sample_format_to_string(src_format.format));
- return nullptr;
+ if (requested_sample_format != src_sample_format) {
+ src = format_converter.Convert(src, error);
+ if (src.IsNull())
+ return nullptr;
}
- return { result, size };
+ return resampler->Resample(src, error);
}
diff --git a/src/pcm/GlueResampler.hxx b/src/pcm/GlueResampler.hxx
index a7e0a84f2..7bd923bab 100644
--- a/src/pcm/GlueResampler.hxx
+++ b/src/pcm/GlueResampler.hxx
@@ -22,22 +22,41 @@
#include "check.h"
#include "AudioFormat.hxx"
-#include "PcmResample.hxx"
+#include "FormatConverter.hxx"
class Error;
+class PcmResampler;
template<typename T> struct ConstBuffer;
+/**
+ * A glue class that integrates a #PcmResampler and automatically
+ * converts source data to the sample format required by the
+ * #PcmResampler instance.
+ */
class GluePcmResampler {
- PcmResampler resampler;
+ PcmResampler *const resampler;
+
+ SampleFormat src_sample_format, requested_sample_format;
+ SampleFormat output_sample_format;
- AudioFormat src_format;
- unsigned new_sample_rate;
+ /**
+ * This object converts input data to the sample format
+ * requested by the #PcmResampler.
+ */
+ PcmFormatConverter format_converter;
public:
+ GluePcmResampler();
+ ~GluePcmResampler();
+
bool Open(AudioFormat src_format, unsigned new_sample_rate,
Error &error);
void Close();
+ SampleFormat GetOutputSampleFormat() const {
+ return output_sample_format;
+ }
+
ConstBuffer<void> Resample(ConstBuffer<void> src, Error &error);
};
diff --git a/src/pcm/LibsamplerateResampler.cxx b/src/pcm/LibsamplerateResampler.cxx
new file mode 100644
index 000000000..586391e67
--- /dev/null
+++ b/src/pcm/LibsamplerateResampler.cxx
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "LibsamplerateResampler.hxx"
+#include "util/ASCII.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+static constexpr Domain libsamplerate_domain("libsamplerate");
+
+static int lsr_converter = SRC_SINC_FASTEST;
+
+static bool
+lsr_parse_converter(const char *s)
+{
+ assert(s != nullptr);
+
+ if (*s == 0)
+ return true;
+
+ char *endptr;
+ long l = strtol(s, &endptr, 10);
+ if (*endptr == 0 && src_get_name(l) != nullptr) {
+ lsr_converter = l;
+ return true;
+ }
+
+ size_t length = strlen(s);
+ for (int i = 0;; ++i) {
+ const char *name = src_get_name(i);
+ if (name == nullptr)
+ break;
+
+ if (StringEqualsCaseASCII(s, name, length)) {
+ lsr_converter = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+pcm_resample_lsr_global_init(const char *converter, Error &error)
+{
+ if (!lsr_parse_converter(converter)) {
+ error.Format(libsamplerate_domain,
+ "unknown samplerate converter '%s'", converter);
+ return false;
+ }
+
+ FormatDebug(libsamplerate_domain,
+ "libsamplerate converter '%s'",
+ src_get_name(lsr_converter));
+
+ return true;
+}
+
+AudioFormat
+LibsampleratePcmResampler::Open(AudioFormat &af, unsigned new_sample_rate,
+ Error &error)
+{
+ assert(af.IsValid());
+ assert(audio_valid_sample_rate(new_sample_rate));
+
+ src_rate = af.sample_rate;
+ dest_rate = new_sample_rate;
+ channels = af.channels;
+
+ /* libsamplerate works with floating point samples */
+ af.format = SampleFormat::FLOAT;
+
+ int src_error;
+ state = src_new(lsr_converter, channels, &src_error);
+ if (!state) {
+ error.Format(libsamplerate_domain, src_error,
+ "libsamplerate initialization has failed: %s",
+ src_strerror(src_error));
+ return AudioFormat::Undefined();
+ }
+
+ memset(&data, 0, sizeof(data));
+
+ data.src_ratio = double(new_sample_rate) / double(af.sample_rate);
+ FormatDebug(libsamplerate_domain,
+ "setting samplerate conversion ratio to %.2lf",
+ data.src_ratio);
+ src_set_ratio(state, data.src_ratio);
+
+ AudioFormat result = af;
+ result.sample_rate = new_sample_rate;
+ return result;
+}
+
+void
+LibsampleratePcmResampler::Close()
+{
+ state = src_delete(state);
+}
+
+static bool
+src_process(SRC_STATE *state, SRC_DATA *data, Error &error)
+{
+ int result = src_process(state, data);
+ if (result != 0) {
+ error.Format(libsamplerate_domain, result,
+ "libsamplerate has failed: %s",
+ src_strerror(result));
+ return false;
+ }
+
+ return true;
+}
+
+inline ConstBuffer<float>
+LibsampleratePcmResampler::Resample2(ConstBuffer<float> src, Error &error)
+{
+ assert(src.size % channels == 0);
+
+ const unsigned src_frames = src.size / channels;
+ const unsigned dest_frames =
+ (src_frames * dest_rate + src_rate - 1) / src_rate;
+ size_t data_out_size = dest_frames * sizeof(float) * channels;
+
+ data.data_in = const_cast<float *>(src.data);
+ data.data_out = (float *)buffer.Get(data_out_size);
+ data.input_frames = src_frames;
+ data.output_frames = dest_frames;
+
+ if (!src_process(state, &data, error))
+ return nullptr;
+
+ return ConstBuffer<float>(data.data_out,
+ data.output_frames_gen * channels);
+}
+
+ConstBuffer<void>
+LibsampleratePcmResampler::Resample(ConstBuffer<void> src, Error &error)
+{
+ return Resample2(ConstBuffer<float>::FromVoid(src), error).ToVoid();
+}
diff --git a/src/pcm/LibsamplerateResampler.hxx b/src/pcm/LibsamplerateResampler.hxx
new file mode 100644
index 000000000..0c1f613c8
--- /dev/null
+++ b/src/pcm/LibsamplerateResampler.hxx
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_LIBSAMPLERATE_RESAMPLER_HXX
+#define MPD_PCM_LIBSAMPLERATE_RESAMPLER_HXX
+
+#include "Resampler.hxx"
+#include "PcmBuffer.hxx"
+#include "AudioFormat.hxx"
+
+#include <samplerate.h>
+
+/**
+ * A resampler using libsamplerate.
+ */
+class LibsampleratePcmResampler final : public PcmResampler {
+ unsigned src_rate, dest_rate;
+ unsigned channels;
+
+ SRC_STATE *state;
+ SRC_DATA data;
+
+ PcmBuffer buffer;
+
+public:
+ virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate,
+ Error &error) override;
+ virtual void Close() override;
+ virtual ConstBuffer<void> Resample(ConstBuffer<void> src,
+ Error &error) override;
+
+private:
+ ConstBuffer<float> Resample2(ConstBuffer<float> src, Error &error);
+};
+
+bool
+pcm_resample_lsr_global_init(const char *converter, Error &error);
+
+#endif
diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx
index 0552962aa..5501d8ddf 100644
--- a/src/pcm/PcmConvert.cxx
+++ b/src/pcm/PcmConvert.cxx
@@ -19,6 +19,7 @@
#include "config.h"
#include "PcmConvert.hxx"
+#include "ConfiguredResampler.hxx"
#include "AudioFormat.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
@@ -33,7 +34,7 @@ const Domain pcm_convert_domain("pcm_convert");
bool
pcm_convert_global_init(Error &error)
{
- return pcm_resample_global_init(error);
+ return pcm_resampler_global_init(error);
}
PcmConvert::PcmConvert()
@@ -66,39 +67,51 @@ PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format,
if (format.format == SampleFormat::DSD)
format.format = SampleFormat::FLOAT;
- if (format.format != dest_format.format &&
- !format_converter.Open(format.format, dest_format.format, error))
+ enable_resampler = format.sample_rate != dest_format.sample_rate;
+ if (enable_resampler) {
+ if (!resampler.Open(format, dest_format.sample_rate, error))
+ return false;
+
+ format.format = resampler.GetOutputSampleFormat();
+ format.sample_rate = dest_format.sample_rate;
+ }
+
+ enable_format = format.format != dest_format.format;
+ if (enable_format &&
+ !format_converter.Open(format.format, dest_format.format, error)) {
+ if (enable_resampler)
+ resampler.Close();
return false;
+ }
+
format.format = dest_format.format;
- if (format.channels != dest_format.channels &&
+ enable_channels = format.channels != dest_format.channels;
+ if (enable_channels &&
!channels_converter.Open(format.format, format.channels,
dest_format.channels, error)) {
- format_converter.Close();
+ if (enable_format)
+ format_converter.Close();
+ if (enable_resampler)
+ resampler.Close();
return false;
}
- if (format.sample_rate != dest_format.sample_rate &&
- !resampler.Open(format, dest_format.sample_rate, error))
- return false;
-
return true;
}
void
PcmConvert::Close()
{
- if (src_format.channels != dest_format.channels)
+ if (enable_channels)
channels_converter.Close();
-
- if (src_format.format != dest_format.format)
+ if (enable_format)
format_converter.Close();
+ if (enable_resampler)
+ resampler.Close();
dsd.Reset();
- if (src_format.sample_rate != dest_format.sample_rate)
- resampler.Close();
-
#ifndef NDEBUG
src_format.Clear();
dest_format.Clear();
@@ -127,28 +140,29 @@ PcmConvert::Convert(const void *src, size_t src_size,
format.format = SampleFormat::FLOAT;
}
- if (format.format != dest_format.format) {
- buffer = format_converter.Convert(buffer, error);
+ if (enable_resampler) {
+ buffer = resampler.Resample(buffer, error);
if (buffer.IsNull())
return nullptr;
- format.format = dest_format.format;
+ format.format = resampler.GetOutputSampleFormat();
+ format.sample_rate = dest_format.sample_rate;
}
- if (format.channels != dest_format.channels) {
- buffer = channels_converter.Convert(buffer, error);
+ if (enable_format) {
+ buffer = format_converter.Convert(buffer, error);
if (buffer.IsNull())
return nullptr;
- format.channels = dest_format.channels;
+ format.format = dest_format.format;
}
- if (format.sample_rate != dest_format.sample_rate) {
- buffer = resampler.Resample(buffer, error);
+ if (enable_channels) {
+ buffer = channels_converter.Convert(buffer, error);
if (buffer.IsNull())
return nullptr;
- format.sample_rate = dest_format.sample_rate;
+ format.channels = dest_format.channels;
}
*dest_size_r = buffer.size;
diff --git a/src/pcm/PcmConvert.hxx b/src/pcm/PcmConvert.hxx
index 9835045d6..13dff4427 100644
--- a/src/pcm/PcmConvert.hxx
+++ b/src/pcm/PcmConvert.hxx
@@ -41,12 +41,14 @@ class Domain;
class PcmConvert {
PcmDsd dsd;
+ GluePcmResampler resampler;
PcmFormatConverter format_converter;
PcmChannelsConverter channels_converter;
- GluePcmResampler resampler;
AudioFormat src_format, dest_format;
+ bool enable_resampler, enable_format, enable_channels;
+
public:
PcmConvert();
~PcmConvert();
diff --git a/src/pcm/PcmFormat.cxx b/src/pcm/PcmFormat.cxx
index 4565c71c6..b7a496264 100644
--- a/src/pcm/PcmFormat.cxx
+++ b/src/pcm/PcmFormat.cxx
@@ -442,7 +442,6 @@ ConvertToFloat(float *dest,
constexpr float factor = 0.5 / (1 << (Traits::BITS - 2));
while (src != end)
*dest++ = float(*src++) * factor;
-
}
template<SampleFormat F, class Traits=SampleTraits<F>>
diff --git a/src/pcm/PcmResample.cxx b/src/pcm/PcmResample.cxx
deleted file mode 100644
index b30e01407..000000000
--- a/src/pcm/PcmResample.cxx
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "PcmResampleInternal.hxx"
-
-#ifdef HAVE_LIBSAMPLERATE
-#include "ConfigGlobal.hxx"
-#include "ConfigOption.hxx"
-#endif
-
-#include <string.h>
-
-#ifdef HAVE_LIBSAMPLERATE
-static bool lsr_enabled;
-#endif
-
-#ifdef HAVE_LIBSAMPLERATE
-static bool
-pcm_resample_lsr_enabled(void)
-{
- return lsr_enabled;
-}
-#endif
-
-bool
-pcm_resample_global_init(Error &error)
-{
-#ifdef HAVE_LIBSAMPLERATE
- const char *converter =
- config_get_string(CONF_SAMPLERATE_CONVERTER, "");
-
- lsr_enabled = strcmp(converter, "internal") != 0;
- if (lsr_enabled)
- return pcm_resample_lsr_global_init(converter, error);
- else
- return true;
-#else
- (void)error;
- return true;
-#endif
-}
-
-PcmResampler::PcmResampler()
-{
-#ifdef HAVE_LIBSAMPLERATE
- if (pcm_resample_lsr_enabled())
- pcm_resample_lsr_init(this);
-#endif
-}
-
-PcmResampler::~PcmResampler()
-{
-#ifdef HAVE_LIBSAMPLERATE
- if (pcm_resample_lsr_enabled())
- pcm_resample_lsr_deinit(this);
-#endif
-}
-
-void
-PcmResampler::Reset()
-{
-#ifdef HAVE_LIBSAMPLERATE
- pcm_resample_lsr_reset(this);
-#endif
-}
-
-const float *
-PcmResampler::ResampleFloat(unsigned channels, unsigned src_rate,
- const float *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r)
-{
-#ifdef HAVE_LIBSAMPLERATE
- if (pcm_resample_lsr_enabled())
- return pcm_resample_lsr_float(this, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r,
- error_r);
-#else
- (void)error_r;
-#endif
-
- /* sizeof(float)==sizeof(int32_t); the fallback resampler does
- not do any math on the sample values, so this hack is
- possible: */
- return (const float *)
- pcm_resample_fallback_32(buffer, channels,
- src_rate, (const int32_t *)src_buffer,
- src_size,
- dest_rate, dest_size_r);
-}
-
-const int16_t *
-PcmResampler::Resample16(unsigned channels,
- unsigned src_rate, const int16_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r)
-{
-#ifdef HAVE_LIBSAMPLERATE
- if (pcm_resample_lsr_enabled())
- return pcm_resample_lsr_16(this, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r,
- error_r);
-#else
- (void)error_r;
-#endif
-
- return pcm_resample_fallback_16(buffer, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r);
-}
-
-const int32_t *
-PcmResampler::Resample32(unsigned channels, unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r)
-{
-#ifdef HAVE_LIBSAMPLERATE
- if (pcm_resample_lsr_enabled())
- return pcm_resample_lsr_32(this, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r,
- error_r);
-#else
- (void)error_r;
-#endif
-
- return pcm_resample_fallback_32(buffer, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r);
-}
-
-const int32_t *
-PcmResampler::Resample24(unsigned channels, unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r)
-{
-#ifdef HAVE_LIBSAMPLERATE
- if (pcm_resample_lsr_enabled())
- return pcm_resample_lsr_24(this, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r,
- error_r);
-#else
- (void)error_r;
-#endif
-
- /* reuse the 32 bit code - the resampler code doesn't care if
- the upper 8 bits are actually used */
- return pcm_resample_fallback_32(buffer, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r);
-}
diff --git a/src/pcm/PcmResample.hxx b/src/pcm/PcmResample.hxx
deleted file mode 100644
index e839d6ecd..000000000
--- a/src/pcm/PcmResample.hxx
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef MPD_PCM_RESAMPLE_HXX
-#define MPD_PCM_RESAMPLE_HXX
-
-#include "check.h"
-#include "PcmBuffer.hxx"
-
-#include <stdint.h>
-#include <stddef.h>
-
-#ifdef HAVE_LIBSAMPLERATE
-#include <samplerate.h>
-#endif
-
-class Error;
-
-/**
- * This object is statically allocated (within another struct), and
- * holds buffer allocations and the state for the resampler.
- */
-struct PcmResampler {
-#ifdef HAVE_LIBSAMPLERATE
- SRC_STATE *state;
- SRC_DATA data;
-
- PcmBuffer in, out;
-
- struct {
- unsigned src_rate;
- unsigned dest_rate;
- unsigned channels;
- } prev;
-
- int error;
-#endif
-
- PcmBuffer buffer;
-
- PcmResampler();
- ~PcmResampler();
-
- /**
- * @see pcm_convert_reset()
- */
- void Reset();
-
- /**
- * Resamples 32 bit float data.
- *
- * @param channels the number of channels
- * @param src_rate the source sample rate
- * @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_rate the requested destination sample rate
- * @param dest_size_r returns the number of bytes of the destination buffer
- * @return the destination buffer
- */
- const float *ResampleFloat(unsigned channels, unsigned src_rate,
- const float *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r);
-
- /**
- * Resamples 16 bit PCM data.
- *
- * @param channels the number of channels
- * @param src_rate the source sample rate
- * @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_rate the requested destination sample rate
- * @param dest_size_r returns the number of bytes of the destination buffer
- * @return the destination buffer
- */
- const int16_t *Resample16(unsigned channels, unsigned src_rate,
- const int16_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r);
-
- /**
- * Resamples 32 bit PCM data.
- *
- * @param channels the number of channels
- * @param src_rate the source sample rate
- * @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_rate the requested destination sample rate
- * @param dest_size_r returns the number of bytes of the destination buffer
- * @return the destination buffer
- */
- const int32_t *Resample32(unsigned channels, unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r);
-
- /**
- * Resamples 24 bit PCM data.
- *
- * @param channels the number of channels
- * @param src_rate the source sample rate
- * @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_rate the requested destination sample rate
- * @param dest_size_r returns the number of bytes of the destination buffer
- * @return the destination buffer
- */
- const int32_t *Resample24(unsigned channels, unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error_r);
-};
-
-bool
-pcm_resample_global_init(Error &error);
-
-#endif
diff --git a/src/pcm/PcmResampleFallback.cxx b/src/pcm/PcmResampleFallback.cxx
deleted file mode 100644
index ca92e5a83..000000000
--- a/src/pcm/PcmResampleFallback.cxx
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "PcmResampleInternal.hxx"
-
-#include <assert.h>
-
-/* resampling code blatantly ripped from ESD */
-const int16_t *
-pcm_resample_fallback_16(PcmBuffer &buffer,
- unsigned channels,
- unsigned src_rate,
- const int16_t *src_buffer, size_t src_size,
- unsigned dest_rate,
- size_t *dest_size_r)
-{
- unsigned dest_pos = 0;
- unsigned src_frames = src_size / channels / sizeof(*src_buffer);
- unsigned dest_frames =
- (src_frames * dest_rate + src_rate - 1) / src_rate;
- unsigned dest_samples = dest_frames * channels;
- size_t dest_size = dest_samples * sizeof(*src_buffer);
- int16_t *dest_buffer = (int16_t *)buffer.Get(dest_size);
-
- assert((src_size % (sizeof(*src_buffer) * channels)) == 0);
-
- switch (channels) {
- case 1:
- while (dest_pos < dest_samples) {
- unsigned src_pos = dest_pos * src_rate / dest_rate;
-
- dest_buffer[dest_pos++] = src_buffer[src_pos];
- }
- break;
- case 2:
- while (dest_pos < dest_samples) {
- unsigned src_pos = dest_pos * src_rate / dest_rate;
- src_pos &= ~1;
-
- dest_buffer[dest_pos++] = src_buffer[src_pos];
- dest_buffer[dest_pos++] = src_buffer[src_pos + 1];
- }
- break;
- }
-
- *dest_size_r = dest_size;
- return dest_buffer;
-}
-
-const int32_t *
-pcm_resample_fallback_32(PcmBuffer &buffer,
- unsigned channels,
- unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate,
- size_t *dest_size_r)
-{
- unsigned dest_pos = 0;
- unsigned src_frames = src_size / channels / sizeof(*src_buffer);
- unsigned dest_frames =
- (src_frames * dest_rate + src_rate - 1) / src_rate;
- unsigned dest_samples = dest_frames * channels;
- size_t dest_size = dest_samples * sizeof(*src_buffer);
- int32_t *dest_buffer = (int32_t *)buffer.Get(dest_size);
-
- assert((src_size % (sizeof(*src_buffer) * channels)) == 0);
-
- switch (channels) {
- case 1:
- while (dest_pos < dest_samples) {
- unsigned src_pos = dest_pos * src_rate / dest_rate;
-
- dest_buffer[dest_pos++] = src_buffer[src_pos];
- }
- break;
- case 2:
- while (dest_pos < dest_samples) {
- unsigned src_pos = dest_pos * src_rate / dest_rate;
- src_pos &= ~1;
-
- dest_buffer[dest_pos++] = src_buffer[src_pos];
- dest_buffer[dest_pos++] = src_buffer[src_pos + 1];
- }
- break;
- }
-
- *dest_size_r = dest_size;
- return dest_buffer;
-}
diff --git a/src/pcm/PcmResampleInternal.hxx b/src/pcm/PcmResampleInternal.hxx
deleted file mode 100644
index 94cef94ff..000000000
--- a/src/pcm/PcmResampleInternal.hxx
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/** \file
- *
- * Internal declarations for the pcm_resample library. The "internal"
- * resampler is called "fallback" in the MPD source, so the file name
- * of this header is somewhat unrelated to it.
- */
-
-#ifndef MPD_PCM_RESAMPLE_INTERNAL_HXX
-#define MPD_PCM_RESAMPLE_INTERNAL_HXX
-
-#include "check.h"
-#include "PcmResample.hxx"
-
-#ifdef HAVE_LIBSAMPLERATE
-
-bool
-pcm_resample_lsr_global_init(const char *converter, Error &error);
-
-void
-pcm_resample_lsr_init(PcmResampler *state);
-
-void
-pcm_resample_lsr_deinit(PcmResampler *state);
-
-void
-pcm_resample_lsr_reset(PcmResampler *state);
-
-const float *
-pcm_resample_lsr_float(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const float *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error);
-
-const int16_t *
-pcm_resample_lsr_16(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const int16_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error);
-
-const int32_t *
-pcm_resample_lsr_32(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const int32_t *src_buffer,
- size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error);
-
-const int32_t *
-pcm_resample_lsr_24(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const int32_t *src_buffer,
- size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error);
-
-#endif
-
-const int16_t *
-pcm_resample_fallback_16(PcmBuffer &buffer,
- unsigned channels,
- unsigned src_rate,
- const int16_t *src_buffer, size_t src_size,
- unsigned dest_rate,
- size_t *dest_size_r);
-
-const int32_t *
-pcm_resample_fallback_32(PcmBuffer &buffer,
- unsigned channels,
- unsigned src_rate,
- const int32_t *src_buffer,
- size_t src_size,
- unsigned dest_rate,
- size_t *dest_size_r);
-
-#endif
diff --git a/src/pcm/PcmResampleLibsamplerate.cxx b/src/pcm/PcmResampleLibsamplerate.cxx
deleted file mode 100644
index e61ff2edf..000000000
--- a/src/pcm/PcmResampleLibsamplerate.cxx
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
- * http://www.musicpd.org
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "config.h"
-#include "PcmResampleInternal.hxx"
-#include "PcmUtils.hxx"
-#include "util/ASCII.hxx"
-#include "util/Error.hxx"
-#include "util/Domain.hxx"
-#include "Log.hxx"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-static int lsr_converter = SRC_SINC_FASTEST;
-
-static constexpr Domain libsamplerate_domain("libsamplerate");
-
-static bool
-lsr_parse_converter(const char *s)
-{
- assert(s != nullptr);
-
- if (*s == 0)
- return true;
-
- char *endptr;
- long l = strtol(s, &endptr, 10);
- if (*endptr == 0 && src_get_name(l) != nullptr) {
- lsr_converter = l;
- return true;
- }
-
- size_t length = strlen(s);
- for (int i = 0;; ++i) {
- const char *name = src_get_name(i);
- if (name == nullptr)
- break;
-
- if (StringEqualsCaseASCII(s, name, length)) {
- lsr_converter = i;
- return true;
- }
- }
-
- return false;
-}
-
-bool
-pcm_resample_lsr_global_init(const char *converter, Error &error)
-{
- if (!lsr_parse_converter(converter)) {
- error.Format(libsamplerate_domain,
- "unknown samplerate converter '%s'", converter);
- return false;
- }
-
- FormatDebug(libsamplerate_domain,
- "libsamplerate converter '%s'",
- src_get_name(lsr_converter));
-
- return true;
-}
-
-void
-pcm_resample_lsr_init(PcmResampler *state)
-{
- state->state = nullptr;
- memset(&state->data, 0, sizeof(state->data));
- memset(&state->prev, 0, sizeof(state->prev));
- state->error = 0;
-}
-
-void
-pcm_resample_lsr_deinit(PcmResampler *state)
-{
- if (state->state != nullptr)
- state->state = src_delete(state->state);
-}
-
-void
-pcm_resample_lsr_reset(PcmResampler *state)
-{
- if (state->state != nullptr)
- src_reset(state->state);
-}
-
-static bool
-pcm_resample_set(PcmResampler *state,
- unsigned channels, unsigned src_rate, unsigned dest_rate,
- Error &error_r)
-{
- /* (re)set the state/ratio if the in or out format changed */
- if (channels == state->prev.channels &&
- src_rate == state->prev.src_rate &&
- dest_rate == state->prev.dest_rate)
- return true;
-
- state->error = 0;
- state->prev.channels = channels;
- state->prev.src_rate = src_rate;
- state->prev.dest_rate = dest_rate;
-
- if (state->state)
- state->state = src_delete(state->state);
-
- int error;
- state->state = src_new(lsr_converter, channels, &error);
- if (!state->state) {
- error_r.Format(libsamplerate_domain, error,
- "libsamplerate initialization has failed: %s",
- src_strerror(error));
- return false;
- }
-
- SRC_DATA *data = &state->data;
- data->src_ratio = (double)dest_rate / (double)src_rate;
- FormatDebug(libsamplerate_domain,
- "setting samplerate conversion ratio to %.2lf",
- data->src_ratio);
- src_set_ratio(state->state, data->src_ratio);
-
- return true;
-}
-
-static bool
-lsr_process(PcmResampler *state, Error &error)
-{
- if (state->error == 0)
- state->error = src_process(state->state, &state->data);
- if (state->error) {
- error.Format(libsamplerate_domain, state->error,
- "libsamplerate has failed: %s",
- src_strerror(state->error));
- return false;
- }
-
- return true;
-}
-
-const float *
-pcm_resample_lsr_float(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const float *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error)
-{
- SRC_DATA *data = &state->data;
-
- assert((src_size % (sizeof(*src_buffer) * channels)) == 0);
-
- if (!pcm_resample_set(state, channels, src_rate, dest_rate, error))
- return nullptr;
-
- data->input_frames = src_size / sizeof(*src_buffer) / channels;
- data->data_in = const_cast<float *>(src_buffer);
-
- data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate;
- size_t data_out_size = data->output_frames * sizeof(float) * channels;
- data->data_out = (float *)state->out.Get(data_out_size);
-
- if (!lsr_process(state, error))
- return nullptr;
-
- *dest_size_r = data->output_frames_gen *
- sizeof(*data->data_out) * channels;
- return data->data_out;
-}
-
-const int16_t *
-pcm_resample_lsr_16(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const int16_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error)
-{
- SRC_DATA *data = &state->data;
-
- assert((src_size % (sizeof(*src_buffer) * channels)) == 0);
-
- if (!pcm_resample_set(state, channels, src_rate, dest_rate,
- error))
- return nullptr;
-
- data->input_frames = src_size / sizeof(*src_buffer) / channels;
- size_t data_in_size = data->input_frames * sizeof(float) * channels;
- data->data_in = (float *)state->in.Get(data_in_size);
-
- data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate;
- size_t data_out_size = data->output_frames * sizeof(float) * channels;
- data->data_out = (float *)state->out.Get(data_out_size);
-
- src_short_to_float_array(src_buffer, data->data_in,
- data->input_frames * channels);
-
- if (!lsr_process(state, error))
- return nullptr;
-
- int16_t *dest_buffer;
- *dest_size_r = data->output_frames_gen *
- sizeof(*dest_buffer) * channels;
- dest_buffer = (int16_t *)state->buffer.Get(*dest_size_r);
- src_float_to_short_array(data->data_out, dest_buffer,
- data->output_frames_gen * channels);
-
- return dest_buffer;
-}
-
-const int32_t *
-pcm_resample_lsr_32(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error)
-{
- SRC_DATA *data = &state->data;
-
- assert((src_size % (sizeof(*src_buffer) * channels)) == 0);
-
- if (!pcm_resample_set(state, channels, src_rate, dest_rate,
- error))
- return nullptr;
-
- data->input_frames = src_size / sizeof(*src_buffer) / channels;
- size_t data_in_size = data->input_frames * sizeof(float) * channels;
- data->data_in = (float *)state->in.Get(data_in_size);
-
- data->output_frames = (src_size * dest_rate + src_rate - 1) / src_rate;
- size_t data_out_size = data->output_frames * sizeof(float) * channels;
- data->data_out = (float *)state->out.Get(data_out_size);
-
- src_int_to_float_array(src_buffer, data->data_in,
- data->input_frames * channels);
-
- if (!lsr_process(state, error))
- return nullptr;
-
- int32_t *dest_buffer;
- *dest_size_r = data->output_frames_gen *
- sizeof(*dest_buffer) * channels;
- dest_buffer = (int32_t *)state->buffer.Get(*dest_size_r);
- src_float_to_int_array(data->data_out, dest_buffer,
- data->output_frames_gen * channels);
-
- return dest_buffer;
-}
-
-const int32_t *
-pcm_resample_lsr_24(PcmResampler *state,
- unsigned channels,
- unsigned src_rate,
- const int32_t *src_buffer, size_t src_size,
- unsigned dest_rate, size_t *dest_size_r,
- Error &error)
-{
- const auto result = pcm_resample_lsr_32(state, channels,
- src_rate, src_buffer, src_size,
- dest_rate, dest_size_r,
- error);
- if (result != nullptr)
- /* src_float_to_int_array() clamps for 32 bit
- integers; now make sure everything's fine for 24
- bit */
- /* TODO: eliminate the 32 bit clamp to reduce overhead */
- PcmClampN<int32_t, int32_t, 24>(const_cast<int32_t *>(result),
- result,
- *dest_size_r / sizeof(*result));
-
- return result;
-}
diff --git a/src/pcm/Resampler.hxx b/src/pcm/Resampler.hxx
new file mode 100644
index 000000000..a74ef4e77
--- /dev/null
+++ b/src/pcm/Resampler.hxx
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_RESAMPLER_HXX
+#define MPD_PCM_RESAMPLER_HXX
+
+#include "util/ConstBuffer.hxx"
+#include "Compiler.h"
+
+struct AudioFormat;
+class Error;
+
+/**
+ * This is an interface for plugins that convert PCM data to a
+ * specific sample rate.
+ */
+class PcmResampler {
+public:
+ virtual ~PcmResampler() {}
+
+ /**
+ * Opens the resampler, preparing it for Resample().
+ *
+ * @param af the audio format of incoming data; the plugin may
+ * modify the object to enforce another input format (however,
+ * it may not request a different input sample rate)
+ * @param new_sample_rate the requested output sample rate
+ * @param error location to store the error
+ * @return the format of outgoing data or
+ * AudioFormat::Undefined() on error
+ */
+ virtual AudioFormat Open(AudioFormat &af, unsigned new_sample_rate,
+ Error &error) = 0;
+
+ /**
+ * Closes the resampler. After that, you may call Open()
+ * again.
+ */
+ virtual void Close() = 0;
+
+ /**
+ * Resamples a block of PCM data.
+ *
+ * @param src the input buffer
+ * @param src_size the size of #src_buffer in bytes
+ * @param dest_size_r the size of the returned buffer
+ * @param error location to store the error occurring, or nullptr
+ * to ignore errors.
+ * @return the destination buffer on success (will be
+ * invalidated by filter_close() or filter_filter()), nullptr on
+ * error
+ */
+ gcc_pure
+ virtual ConstBuffer<void> Resample(ConstBuffer<void> src,
+ Error &error) = 0;
+};
+
+#endif