aboutsummaryrefslogtreecommitdiffstats
path: root/src/pcm
diff options
context:
space:
mode:
Diffstat (limited to 'src/pcm')
-rw-r--r--src/pcm/ChannelsConverter.cxx97
-rw-r--r--src/pcm/ChannelsConverter.hxx83
-rw-r--r--src/pcm/ConfiguredResampler.cxx101
-rw-r--r--src/pcm/ConfiguredResampler.hxx38
-rw-r--r--src/pcm/Domain.cxx23
-rw-r--r--src/pcm/Domain.hxx27
-rw-r--r--src/pcm/FallbackResampler.cxx147
-rw-r--r--src/pcm/FallbackResampler.hxx45
-rw-r--r--src/pcm/FormatConverter.cxx104
-rw-r--r--src/pcm/FormatConverter.hxx84
-rw-r--r--src/pcm/GlueResampler.cxx85
-rw-r--r--src/pcm/GlueResampler.hxx63
-rw-r--r--src/pcm/LibsamplerateResampler.cxx163
-rw-r--r--src/pcm/LibsamplerateResampler.hxx55
-rw-r--r--src/pcm/PcmBuffer.cxx1
-rw-r--r--src/pcm/PcmBuffer.hxx6
-rw-r--r--src/pcm/PcmChannels.cxx295
-rw-r--r--src/pcm/PcmChannels.hxx33
-rw-r--r--src/pcm/PcmConvert.cxx332
-rw-r--r--src/pcm/PcmConvert.hxx65
-rw-r--r--src/pcm/PcmDither.cxx59
-rw-r--r--src/pcm/PcmDither.hxx14
-rw-r--r--src/pcm/PcmDsd.cxx22
-rw-r--r--src/pcm/PcmDsd.hxx10
-rw-r--r--src/pcm/PcmDsdUsb.cxx2
-rw-r--r--src/pcm/PcmFormat.cxx51
-rw-r--r--src/pcm/PcmMix.cxx98
-rw-r--r--src/pcm/PcmPrng.hxx2
-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.cxx310
-rw-r--r--src/pcm/PcmUtils.hxx36
-rw-r--r--src/pcm/PcmVolume.cxx188
-rw-r--r--src/pcm/Resampler.hxx74
-rw-r--r--src/pcm/SoxrResampler.cxx95
-rw-r--r--src/pcm/SoxrResampler.hxx47
-rw-r--r--src/pcm/Traits.hxx153
-rw-r--r--src/pcm/Volume.cxx232
-rw-r--r--src/pcm/Volume.hxx (renamed from src/pcm/PcmVolume.hxx)93
41 files changed, 2164 insertions, 1681 deletions
diff --git a/src/pcm/ChannelsConverter.cxx b/src/pcm/ChannelsConverter.cxx
new file mode 100644
index 000000000..8ffcbfe41
--- /dev/null
+++ b/src/pcm/ChannelsConverter.cxx
@@ -0,0 +1,97 @@
+/*
+ * 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 "ChannelsConverter.hxx"
+#include "PcmChannels.hxx"
+#include "Domain.hxx"
+#include "util/ConstBuffer.hxx"
+#include "util/Error.hxx"
+
+#include <assert.h>
+
+bool
+PcmChannelsConverter::Open(SampleFormat _format,
+ unsigned _src_channels, unsigned _dest_channels,
+ gcc_unused Error &error)
+{
+ assert(_format != SampleFormat::UNDEFINED);
+
+ switch (_format) {
+ case SampleFormat::S16:
+ case SampleFormat::S24_P32:
+ case SampleFormat::S32:
+ case SampleFormat::FLOAT:
+ break;
+
+ default:
+ error.Format(pcm_domain,
+ "PCM channel conversion for %s is not implemented",
+ sample_format_to_string(format));
+ return false;
+ }
+
+ format = _format;
+ src_channels = _src_channels;
+ dest_channels = _dest_channels;
+ return true;
+}
+
+void
+PcmChannelsConverter::Close()
+{
+#ifndef NDEBUG
+ format = SampleFormat::UNDEFINED;
+#endif
+}
+
+ConstBuffer<void>
+PcmChannelsConverter::Convert(ConstBuffer<void> src, gcc_unused Error &error)
+{
+ switch (format) {
+ case SampleFormat::UNDEFINED:
+ case SampleFormat::S8:
+ case SampleFormat::DSD:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S16:
+ return pcm_convert_channels_16(buffer, dest_channels,
+ src_channels,
+ ConstBuffer<int16_t>::FromVoid(src)).ToVoid();
+
+ case SampleFormat::S24_P32:
+ return pcm_convert_channels_24(buffer, dest_channels,
+ src_channels,
+ ConstBuffer<int32_t>::FromVoid(src)).ToVoid();
+
+ case SampleFormat::S32:
+ return pcm_convert_channels_32(buffer, dest_channels,
+ src_channels,
+ ConstBuffer<int32_t>::FromVoid(src)).ToVoid();
+
+ case SampleFormat::FLOAT:
+ return pcm_convert_channels_float(buffer, dest_channels,
+ src_channels,
+ ConstBuffer<float>::FromVoid(src)).ToVoid();
+ }
+
+ assert(false);
+ gcc_unreachable();
+}
diff --git a/src/pcm/ChannelsConverter.hxx b/src/pcm/ChannelsConverter.hxx
new file mode 100644
index 000000000..4311b9671
--- /dev/null
+++ b/src/pcm/ChannelsConverter.hxx
@@ -0,0 +1,83 @@
+/*
+ * 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_CHANNELS_CONVERTER_HXX
+#define MPD_PCM_CHANNELS_CONVERTER_HXX
+
+#include "check.h"
+#include "AudioFormat.hxx"
+#include "PcmBuffer.hxx"
+
+#ifndef NDEBUG
+#include <assert.h>
+#endif
+
+class Error;
+template<typename T> struct ConstBuffer;
+
+/**
+ * A class that converts samples from one format to another.
+ */
+class PcmChannelsConverter {
+ SampleFormat format;
+ unsigned src_channels, dest_channels;
+
+ PcmBuffer buffer;
+
+public:
+#ifndef NDEBUG
+ PcmChannelsConverter()
+ :format(SampleFormat::UNDEFINED) {}
+
+ ~PcmChannelsConverter() {
+ assert(format == SampleFormat::UNDEFINED);
+ }
+#endif
+
+ /**
+ * Opens the object, prepare for Convert().
+ *
+ * @param format the sample format
+ * @param src_channels the number of source channels
+ * @param dest_channels the number of destination channels
+ * @param error location to store the error
+ * @return true on success
+ */
+ bool Open(SampleFormat format,
+ unsigned src_channels, unsigned dest_channels,
+ Error &error);
+
+ /**
+ * Closes the object. After that, you may call Open() again.
+ */
+ void Close();
+
+ /**
+ * Convert a block of PCM data.
+ *
+ * @param src the input buffer
+ * @param error location to store the error
+ * @return the destination buffer on success,
+ * ConstBuffer::Null() on error
+ */
+ gcc_pure
+ ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error);
+};
+
+#endif
diff --git a/src/pcm/ConfiguredResampler.cxx b/src/pcm/ConfiguredResampler.cxx
new file mode 100644
index 000000000..845fa2332
--- /dev/null
+++ b/src/pcm/ConfiguredResampler.cxx
@@ -0,0 +1,101 @@
+/*
+ * 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"
+#include "ConfigGlobal.hxx"
+#include "ConfigOption.hxx"
+#include "ConfigError.hxx"
+#include "util/Error.hxx"
+
+#ifdef HAVE_LIBSAMPLERATE
+#include "LibsamplerateResampler.hxx"
+#endif
+
+#ifdef HAVE_SOXR
+#include "SoxrResampler.hxx"
+#endif
+
+#include <string.h>
+
+enum class SelectedResampler {
+ FALLBACK,
+
+#ifdef HAVE_LIBSAMPLERATE
+ LIBSAMPLERATE,
+#endif
+
+#ifdef HAVE_SOXR
+ SOXR,
+#endif
+};
+
+static SelectedResampler selected_resampler = SelectedResampler::FALLBACK;
+
+bool
+pcm_resampler_global_init(Error &error)
+{
+ const char *converter =
+ config_get_string(CONF_SAMPLERATE_CONVERTER, "");
+
+ if (strcmp(converter, "internal") == 0)
+ return true;
+
+#ifdef HAVE_SOXR
+ if (strcmp(converter, "soxr") == 0) {
+ selected_resampler = SelectedResampler::SOXR;
+ return true;
+ }
+#endif
+
+#ifdef HAVE_LIBSAMPLERATE
+ selected_resampler = SelectedResampler::LIBSAMPLERATE;
+ return pcm_resample_lsr_global_init(converter, error);
+#endif
+
+ if (*converter == 0)
+ return true;
+
+ error.Format(config_domain,
+ "The samplerate_converter '%s' is not available",
+ converter);
+ return false;
+}
+
+PcmResampler *
+pcm_resampler_create()
+{
+ switch (selected_resampler) {
+ case SelectedResampler::FALLBACK:
+ return new FallbackPcmResampler();
+
+#ifdef HAVE_LIBSAMPLERATE
+ case SelectedResampler::LIBSAMPLERATE:
+ return new LibsampleratePcmResampler();
+#endif
+
+#ifdef HAVE_SOXR
+ case SelectedResampler::SOXR:
+ return new SoxrPcmResampler();
+#endif
+ }
+
+ gcc_unreachable();
+}
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/Domain.cxx b/src/pcm/Domain.cxx
new file mode 100644
index 000000000..9f07d80dd
--- /dev/null
+++ b/src/pcm/Domain.cxx
@@ -0,0 +1,23 @@
+/*
+ * 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 "Domain.hxx"
+#include "util/Domain.hxx"
+
+const Domain pcm_domain("pcm");
diff --git a/src/pcm/Domain.hxx b/src/pcm/Domain.hxx
new file mode 100644
index 000000000..170d7406d
--- /dev/null
+++ b/src/pcm/Domain.hxx
@@ -0,0 +1,27 @@
+/*
+ * 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 PCM_DOMAIN_HXX
+#define PCM_DOMAIN_HXX
+
+class Domain;
+
+extern const Domain pcm_domain;
+
+#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/FormatConverter.cxx b/src/pcm/FormatConverter.cxx
new file mode 100644
index 000000000..8886a8ab0
--- /dev/null
+++ b/src/pcm/FormatConverter.cxx
@@ -0,0 +1,104 @@
+/*
+ * 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 "FormatConverter.hxx"
+#include "PcmFormat.hxx"
+#include "Domain.hxx"
+#include "util/ConstBuffer.hxx"
+#include "util/Error.hxx"
+
+#include <assert.h>
+
+bool
+PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format,
+ gcc_unused Error &error)
+{
+ assert(_src_format != SampleFormat::UNDEFINED);
+ assert(_dest_format != SampleFormat::UNDEFINED);
+
+ src_format = _src_format;
+ dest_format = _dest_format;
+ return true;
+}
+
+void
+PcmFormatConverter::Close()
+{
+#ifndef NDEBUG
+ src_format = SampleFormat::UNDEFINED;
+ dest_format = SampleFormat::UNDEFINED;
+#endif
+}
+
+ConstBuffer<void>
+PcmFormatConverter::Convert(ConstBuffer<void> src, Error &error)
+{
+ const void *result = nullptr;
+ size_t result_size = 0;
+
+ switch (dest_format) {
+ case SampleFormat::UNDEFINED:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S8:
+ case SampleFormat::DSD:
+ result = nullptr;
+ break;
+
+ case SampleFormat::S16:
+ result = pcm_convert_to_16(buffer, dither,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+
+ case SampleFormat::S24_P32:
+ result = pcm_convert_to_24(buffer,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+
+ case SampleFormat::S32:
+ result = pcm_convert_to_32(buffer,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+
+ case SampleFormat::FLOAT:
+ result = pcm_convert_to_float(buffer,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+ }
+
+ if (result == nullptr) {
+ error.Format(pcm_domain,
+ "PCM conversion from %s to %s is not implemented",
+ sample_format_to_string(src_format),
+ sample_format_to_string(dest_format));
+ return nullptr;
+ }
+
+ return { result, result_size };
+}
diff --git a/src/pcm/FormatConverter.hxx b/src/pcm/FormatConverter.hxx
new file mode 100644
index 000000000..f5b13a0b0
--- /dev/null
+++ b/src/pcm/FormatConverter.hxx
@@ -0,0 +1,84 @@
+/*
+ * 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_FORMAT_CONVERTER_HXX
+#define MPD_PCM_FORMAT_CONVERTER_HXX
+
+#include "check.h"
+#include "AudioFormat.hxx"
+#include "PcmBuffer.hxx"
+#include "PcmDither.hxx"
+
+#ifndef NDEBUG
+#include <assert.h>
+#endif
+
+class Error;
+template<typename T> struct ConstBuffer;
+
+/**
+ * A class that converts samples from one format to another.
+ */
+class PcmFormatConverter {
+ SampleFormat src_format, dest_format;
+
+ PcmBuffer buffer;
+ PcmDither dither;
+
+public:
+#ifndef NDEBUG
+ PcmFormatConverter()
+ :src_format(SampleFormat::UNDEFINED),
+ dest_format(SampleFormat::UNDEFINED) {}
+
+ ~PcmFormatConverter() {
+ assert(src_format == SampleFormat::UNDEFINED);
+ assert(dest_format == SampleFormat::UNDEFINED);
+ }
+#endif
+
+ /**
+ * Opens the object, prepare for Convert().
+ *
+ * @param src_format the sample format of incoming data
+ * @param dest_format the sample format of outgoing data
+ * @param error location to store the error
+ * @return true on success
+ */
+ bool Open(SampleFormat src_format, SampleFormat dest_format,
+ Error &error);
+
+ /**
+ * Closes the object. After that, you may call Open() again.
+ */
+ void Close();
+
+ /**
+ * Convert a block of PCM data.
+ *
+ * @param src the input buffer
+ * @param error location to store the error
+ * @return the destination buffer on success,
+ * ConstBuffer::Null() on error
+ */
+ gcc_pure
+ ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error);
+};
+
+#endif
diff --git a/src/pcm/GlueResampler.cxx b/src/pcm/GlueResampler.cxx
new file mode 100644
index 000000000..ef80e08a5
--- /dev/null
+++ b/src/pcm/GlueResampler.cxx
@@ -0,0 +1,85 @@
+/*
+ * 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 "GlueResampler.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,
+ Error &error)
+{
+ 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()
+{
+ if (requested_sample_format != src_sample_format)
+ format_converter.Close();
+
+ resampler->Close();
+}
+
+ConstBuffer<void>
+GluePcmResampler::Resample(ConstBuffer<void> src, Error &error)
+{
+ assert(!src.IsNull());
+
+ if (requested_sample_format != src_sample_format) {
+ src = format_converter.Convert(src, error);
+ if (src.IsNull())
+ return nullptr;
+ }
+
+ return resampler->Resample(src, error);
+}
diff --git a/src/pcm/GlueResampler.hxx b/src/pcm/GlueResampler.hxx
new file mode 100644
index 000000000..7bd923bab
--- /dev/null
+++ b/src/pcm/GlueResampler.hxx
@@ -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.
+ */
+
+#ifndef MPD_GLUE_RESAMPLER_HXX
+#define MPD_GLUE_RESAMPLER_HXX
+
+#include "check.h"
+#include "AudioFormat.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 *const resampler;
+
+ SampleFormat src_sample_format, requested_sample_format;
+ SampleFormat output_sample_format;
+
+ /**
+ * 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);
+};
+
+#endif
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/PcmBuffer.cxx b/src/pcm/PcmBuffer.cxx
index 578c579be..5af9a978d 100644
--- a/src/pcm/PcmBuffer.cxx
+++ b/src/pcm/PcmBuffer.cxx
@@ -19,7 +19,6 @@
#include "config.h"
#include "PcmBuffer.hxx"
-#include "poison.h"
void *
PcmBuffer::Get(size_t new_size)
diff --git a/src/pcm/PcmBuffer.hxx b/src/pcm/PcmBuffer.hxx
index 717e24938..44a3ebf99 100644
--- a/src/pcm/PcmBuffer.hxx
+++ b/src/pcm/PcmBuffer.hxx
@@ -49,6 +49,12 @@ public:
*/
gcc_malloc
void *Get(size_t size);
+
+ template<typename T>
+ gcc_malloc
+ T *GetT(size_t n) {
+ return (T *)Get(n * sizeof(T));
+ }
};
#endif
diff --git a/src/pcm/PcmChannels.cxx b/src/pcm/PcmChannels.cxx
index eb69985c1..2e433e611 100644
--- a/src/pcm/PcmChannels.cxx
+++ b/src/pcm/PcmChannels.cxx
@@ -20,7 +20,9 @@
#include "config.h"
#include "PcmChannels.hxx"
#include "PcmBuffer.hxx"
-#include "PcmUtils.hxx"
+#include "Traits.hxx"
+#include "AudioFormat.hxx"
+#include "util/ConstBuffer.hxx"
#include <assert.h>
@@ -37,254 +39,143 @@ MonoToStereo(D dest, S src, S end)
}
-static void
-pcm_convert_channels_16_2_to_1(int16_t *gcc_restrict dest,
- const int16_t *gcc_restrict src,
- const int16_t *gcc_restrict src_end)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static typename Traits::value_type
+StereoToMono(typename Traits::value_type _a,
+ typename Traits::value_type _b)
{
- while (src < src_end) {
- int32_t a = *src++, b = *src++;
+ typename Traits::sum_type a(_a);
+ typename Traits::sum_type b(_b);
- *dest++ = (a + b) / 2;
- }
+ return typename Traits::value_type((a + b) / 2);
}
-static void
-pcm_convert_channels_16_n_to_2(int16_t *gcc_restrict dest,
- unsigned src_channels,
- const int16_t *gcc_restrict src,
- const int16_t *gcc_restrict src_end)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static typename Traits::pointer_type
+StereoToMono(typename Traits::pointer_type dest,
+ typename Traits::const_pointer_type src,
+ typename Traits::const_pointer_type end)
{
- unsigned c;
-
- assert(src_channels > 0);
-
- while (src < src_end) {
- int32_t sum = 0;
- int16_t value;
-
- for (c = 0; c < src_channels; ++c)
- sum += *src++;
- value = sum / (int)src_channels;
+ while (src != end) {
+ const auto a = *src++;
+ const auto b = *src++;
- /* XXX this is actually only mono ... */
- *dest++ = value;
- *dest++ = value;
+ *dest++ = StereoToMono<F, Traits>(a, b);
}
-}
-
-const int16_t *
-pcm_convert_channels_16(PcmBuffer &buffer,
- unsigned dest_channels,
- unsigned src_channels, const int16_t *src,
- size_t src_size, size_t *dest_size_r)
-{
- assert(src_size % (sizeof(*src) * src_channels) == 0);
-
- size_t dest_size = src_size / src_channels * dest_channels;
- *dest_size_r = dest_size;
-
- int16_t *dest = (int16_t *)buffer.Get(dest_size);
- const int16_t *src_end = pcm_end_pointer(src, src_size);
-
- if (src_channels == 1 && dest_channels == 2)
- MonoToStereo(dest, src, src_end);
- else if (src_channels == 2 && dest_channels == 1)
- pcm_convert_channels_16_2_to_1(dest, src, src_end);
- else if (dest_channels == 2)
- pcm_convert_channels_16_n_to_2(dest, src_channels, src,
- src_end);
- else
- return nullptr;
return dest;
}
-static void
-pcm_convert_channels_24_2_to_1(int32_t *gcc_restrict dest,
- const int32_t *gcc_restrict src,
- const int32_t *gcc_restrict src_end)
-{
- while (src < src_end) {
- int32_t a = *src++, b = *src++;
-
- *dest++ = (a + b) / 2;
- }
-}
-
-static void
-pcm_convert_channels_24_n_to_2(int32_t *gcc_restrict dest,
- unsigned src_channels,
- const int32_t *gcc_restrict src,
- const int32_t *gcc_restrict src_end)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static typename Traits::pointer_type
+NToStereo(typename Traits::pointer_type dest,
+ unsigned src_channels,
+ typename Traits::const_pointer_type src,
+ typename Traits::const_pointer_type end)
{
- unsigned c;
+ assert((end - src) % src_channels == 0);
- assert(src_channels > 0);
-
- while (src < src_end) {
- int32_t sum = 0;
- int32_t value;
-
- for (c = 0; c < src_channels; ++c)
+ while (src != end) {
+ typename Traits::sum_type sum = *src++;
+ for (unsigned c = 1; c < src_channels; ++c)
sum += *src++;
- value = sum / (int)src_channels;
- /* XXX this is actually only mono ... */
+ typename Traits::value_type value(sum / int(src_channels));
+
+ /* TODO: this is actually only mono ... */
*dest++ = value;
*dest++ = value;
}
-}
-
-const int32_t *
-pcm_convert_channels_24(PcmBuffer &buffer,
- unsigned dest_channels,
- unsigned src_channels, const int32_t *src,
- size_t src_size, size_t *dest_size_r)
-{
- assert(src_size % (sizeof(*src) * src_channels) == 0);
-
- size_t dest_size = src_size / src_channels * dest_channels;
- *dest_size_r = dest_size;
-
- int32_t *dest = (int32_t *)buffer.Get(dest_size);
- const int32_t *src_end = (const int32_t *)
- pcm_end_pointer(src, src_size);
-
- if (src_channels == 1 && dest_channels == 2)
- MonoToStereo(dest, src, src_end);
- else if (src_channels == 2 && dest_channels == 1)
- pcm_convert_channels_24_2_to_1(dest, src, src_end);
- else if (dest_channels == 2)
- pcm_convert_channels_24_n_to_2(dest, src_channels, src,
- src_end);
- else
- return nullptr;
return dest;
}
-static void
-pcm_convert_channels_32_2_to_1(int32_t *gcc_restrict dest,
- const int32_t *gcc_restrict src,
- const int32_t *gcc_restrict src_end)
-{
- while (src < src_end) {
- int64_t a = *src++, b = *src++;
-
- *dest++ = (a + b) / 2;
- }
-}
-
-static void
-pcm_convert_channels_32_n_to_2(int32_t *dest,
- unsigned src_channels, const int32_t *src,
- const int32_t *src_end)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static typename Traits::pointer_type
+NToM(typename Traits::pointer_type dest,
+ unsigned dest_channels,
+ unsigned src_channels,
+ typename Traits::const_pointer_type src,
+ typename Traits::const_pointer_type end)
{
- unsigned c;
+ assert((end - src) % src_channels == 0);
- assert(src_channels > 0);
-
- while (src < src_end) {
- int64_t sum = 0;
- int32_t value;
-
- for (c = 0; c < src_channels; ++c)
+ while (src != end) {
+ typename Traits::sum_type sum = *src++;
+ for (unsigned c = 1; c < src_channels; ++c)
sum += *src++;
- value = sum / (int64_t)src_channels;
- /* XXX this is actually only mono ... */
- *dest++ = value;
- *dest++ = value;
+ typename Traits::value_type value(sum / int(src_channels));
+
+ /* TODO: this is actually only mono ... */
+ for (unsigned c = 0; c < dest_channels; ++c)
+ *dest++ = value;
}
+
+ return dest;
}
-const int32_t *
-pcm_convert_channels_32(PcmBuffer &buffer,
- unsigned dest_channels,
- unsigned src_channels, const int32_t *src,
- size_t src_size, size_t *dest_size_r)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static ConstBuffer<typename Traits::value_type>
+ConvertChannels(PcmBuffer &buffer,
+ unsigned dest_channels,
+ unsigned src_channels,
+ ConstBuffer<typename Traits::value_type> src)
{
- assert(src_size % (sizeof(*src) * src_channels) == 0);
-
- size_t dest_size = src_size / src_channels * dest_channels;
- *dest_size_r = dest_size;
+ assert(src.size % src_channels == 0);
- int32_t *dest = (int32_t *)buffer.Get(dest_size);
- const int32_t *src_end = (const int32_t *)
- pcm_end_pointer(src, src_size);
+ const size_t dest_size = src.size / src_channels * dest_channels;
+ auto dest = buffer.GetT<typename Traits::value_type>(dest_size);
if (src_channels == 1 && dest_channels == 2)
- MonoToStereo(dest, src, src_end);
+ MonoToStereo(dest, src.begin(), src.end());
else if (src_channels == 2 && dest_channels == 1)
- pcm_convert_channels_32_2_to_1(dest, src, src_end);
+ StereoToMono<F>(dest, src.begin(), src.end());
else if (dest_channels == 2)
- pcm_convert_channels_32_n_to_2(dest, src_channels, src,
- src_end);
+ NToStereo<F>(dest, src_channels, src.begin(), src.end());
else
- return nullptr;
+ NToM<F>(dest, dest_channels,
+ src_channels, src.begin(), src.end());
- return dest;
+ return { dest, dest_size };
}
-static void
-pcm_convert_channels_float_2_to_1(float *gcc_restrict dest,
- const float *gcc_restrict src,
- const float *gcc_restrict src_end)
+ConstBuffer<int16_t>
+pcm_convert_channels_16(PcmBuffer &buffer,
+ unsigned dest_channels,
+ unsigned src_channels,
+ ConstBuffer<int16_t> src)
{
- while (src < src_end) {
- double a = *src++, b = *src++;
-
- *dest++ = (a + b) / 2;
- }
+ return ConvertChannels<SampleFormat::S16>(buffer, dest_channels,
+ src_channels, src);
}
-static void
-pcm_convert_channels_float_n_to_2(float *dest,
- unsigned src_channels, const float *src,
- const float *src_end)
+ConstBuffer<int32_t>
+pcm_convert_channels_24(PcmBuffer &buffer,
+ unsigned dest_channels,
+ unsigned src_channels,
+ ConstBuffer<int32_t> src)
{
- unsigned c;
-
- assert(src_channels > 0);
-
- while (src < src_end) {
- double sum = 0;
- float value;
-
- for (c = 0; c < src_channels; ++c)
- sum += *src++;
- value = sum / (double)src_channels;
+ return ConvertChannels<SampleFormat::S24_P32>(buffer, dest_channels,
+ src_channels, src);
+}
- /* XXX this is actually only mono ... */
- *dest++ = value;
- *dest++ = value;
- }
+ConstBuffer<int32_t>
+pcm_convert_channels_32(PcmBuffer &buffer,
+ unsigned dest_channels,
+ unsigned src_channels,
+ ConstBuffer<int32_t> src)
+{
+ return ConvertChannels<SampleFormat::S32>(buffer, dest_channels,
+ src_channels, src);
}
-const float *
+ConstBuffer<float>
pcm_convert_channels_float(PcmBuffer &buffer,
unsigned dest_channels,
- unsigned src_channels, const float *src,
- size_t src_size, size_t *dest_size_r)
+ unsigned src_channels,
+ ConstBuffer<float> src)
{
- assert(src_size % (sizeof(*src) * src_channels) == 0);
-
- size_t dest_size = src_size / src_channels * dest_channels;
- *dest_size_r = dest_size;
-
- float *dest = (float *)buffer.Get(dest_size);
- const float *src_end = (const float *)pcm_end_pointer(src, src_size);
-
- if (src_channels == 1 && dest_channels == 2)
- MonoToStereo(dest, src, src_end);
- else if (src_channels == 2 && dest_channels == 1)
- pcm_convert_channels_float_2_to_1(dest, src, src_end);
- else if (dest_channels == 2)
- pcm_convert_channels_float_n_to_2(dest, src_channels, src,
- src_end);
- else
- return nullptr;
-
- return dest;
+ return ConvertChannels<SampleFormat::FLOAT>(buffer, dest_channels,
+ src_channels, src);
}
diff --git a/src/pcm/PcmChannels.hxx b/src/pcm/PcmChannels.hxx
index c67822825..ca4e06a8c 100644
--- a/src/pcm/PcmChannels.hxx
+++ b/src/pcm/PcmChannels.hxx
@@ -24,6 +24,7 @@
#include <stddef.h>
class PcmBuffer;
+template<typename T> struct ConstBuffer;
/**
* Changes the number of channels in 16 bit PCM data.
@@ -32,15 +33,13 @@ class PcmBuffer;
* @param dest_channels the number of channels requested
* @param src_channels the number of channels in the source buffer
* @param src the source PCM buffer
- * @param src_size the number of bytes in #src
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const int16_t *
+ConstBuffer<int16_t>
pcm_convert_channels_16(PcmBuffer &buffer,
unsigned dest_channels,
- unsigned src_channels, const int16_t *src,
- size_t src_size, size_t *dest_size_r);
+ unsigned src_channels,
+ ConstBuffer<int16_t> src);
/**
* Changes the number of channels in 24 bit PCM data (aligned at 32
@@ -50,15 +49,13 @@ pcm_convert_channels_16(PcmBuffer &buffer,
* @param dest_channels the number of channels requested
* @param src_channels the number of channels in the source buffer
* @param src the source PCM buffer
- * @param src_size the number of bytes in #src
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const int32_t *
+ConstBuffer<int32_t>
pcm_convert_channels_24(PcmBuffer &buffer,
unsigned dest_channels,
- unsigned src_channels, const int32_t *src,
- size_t src_size, size_t *dest_size_r);
+ unsigned src_channels,
+ ConstBuffer<int32_t> src);
/**
* Changes the number of channels in 32 bit PCM data.
@@ -67,15 +64,13 @@ pcm_convert_channels_24(PcmBuffer &buffer,
* @param dest_channels the number of channels requested
* @param src_channels the number of channels in the source buffer
* @param src the source PCM buffer
- * @param src_size the number of bytes in #src
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const int32_t *
+ConstBuffer<int32_t>
pcm_convert_channels_32(PcmBuffer &buffer,
unsigned dest_channels,
- unsigned src_channels, const int32_t *src,
- size_t src_size, size_t *dest_size_r);
+ unsigned src_channels,
+ ConstBuffer<int32_t> src);
/**
* Changes the number of channels in 32 bit float PCM data.
@@ -84,14 +79,12 @@ pcm_convert_channels_32(PcmBuffer &buffer,
* @param dest_channels the number of channels requested
* @param src_channels the number of channels in the source buffer
* @param src the source PCM buffer
- * @param src_size the number of bytes in #src
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const float *
+ConstBuffer<float>
pcm_convert_channels_float(PcmBuffer &buffer,
unsigned dest_channels,
- unsigned src_channels, const float *src,
- size_t src_size, size_t *dest_size_r);
+ unsigned src_channels,
+ ConstBuffer<float> src);
#endif
diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx
index 8eafe527c..6eecd3b01 100644
--- a/src/pcm/PcmConvert.cxx
+++ b/src/pcm/PcmConvert.cxx
@@ -19,289 +19,151 @@
#include "config.h"
#include "PcmConvert.hxx"
-#include "PcmChannels.hxx"
-#include "PcmFormat.hxx"
+#include "Domain.hxx"
+#include "ConfiguredResampler.hxx"
#include "AudioFormat.hxx"
+#include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
+#include "util/ConstBuffer.hxx"
#include <assert.h>
#include <math.h>
-const Domain pcm_convert_domain("pcm_convert");
+bool
+pcm_convert_global_init(Error &error)
+{
+ return pcm_resampler_global_init(error);
+}
PcmConvert::PcmConvert()
{
+#ifndef NDEBUG
+ src_format.Clear();
+ dest_format.Clear();
+#endif
}
PcmConvert::~PcmConvert()
{
+ assert(!src_format.IsValid());
+ assert(!dest_format.IsValid());
}
-void
-PcmConvert::Reset()
+bool
+PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format,
+ Error &error)
{
- dsd.Reset();
- resampler.Reset();
-}
+ assert(!src_format.IsValid());
+ assert(!dest_format.IsValid());
+ assert(_src_format.IsValid());
+ assert(_dest_format.IsValid());
-inline const int16_t *
-PcmConvert::Convert16(const AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- const AudioFormat dest_format, size_t *dest_size_r,
- Error &error)
-{
- const int16_t *buf;
- size_t len;
+ src_format = _src_format;
+ dest_format = _dest_format;
+
+ AudioFormat format = src_format;
+ if (format.format == SampleFormat::DSD)
+ format.format = SampleFormat::FLOAT;
- assert(dest_format.format == SampleFormat::S16);
+ enable_resampler = format.sample_rate != dest_format.sample_rate;
+ if (enable_resampler) {
+ if (!resampler.Open(format, dest_format.sample_rate, error))
+ return false;
- buf = pcm_convert_to_16(format_buffer, dither,
- src_format.format,
- src_buffer, src_size,
- &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to 16 bit is not implemented",
- sample_format_to_string(src_format.format));
- return nullptr;
+ format.format = resampler.GetOutputSampleFormat();
+ format.sample_rate = dest_format.sample_rate;
}
- if (src_format.channels != dest_format.channels) {
- buf = pcm_convert_channels_16(channels_buffer,
- dest_format.channels,
- src_format.channels,
- buf, len, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format.channels,
- dest_format.channels);
- return nullptr;
- }
+ 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;
}
- if (src_format.sample_rate != dest_format.sample_rate) {
- buf = resampler.Resample16(dest_format.channels,
- src_format.sample_rate, buf, len,
- dest_format.sample_rate, &len,
- error);
- if (buf == nullptr)
- return nullptr;
+ format.format = dest_format.format;
+
+ enable_channels = format.channels != dest_format.channels;
+ if (enable_channels &&
+ !channels_converter.Open(format.format, format.channels,
+ dest_format.channels, error)) {
+ if (enable_format)
+ format_converter.Close();
+ if (enable_resampler)
+ resampler.Close();
+ return false;
}
- *dest_size_r = len;
- return buf;
+ return true;
}
-inline const int32_t *
-PcmConvert::Convert24(const AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- const AudioFormat dest_format, size_t *dest_size_r,
- Error &error)
+void
+PcmConvert::Close()
{
- const int32_t *buf;
- size_t len;
-
- assert(dest_format.format == SampleFormat::S24_P32);
-
- buf = pcm_convert_to_24(format_buffer,
- src_format.format,
- src_buffer, src_size, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to 24 bit is not implemented",
- sample_format_to_string(src_format.format));
- return nullptr;
- }
+ if (enable_channels)
+ channels_converter.Close();
+ if (enable_format)
+ format_converter.Close();
+ if (enable_resampler)
+ resampler.Close();
- if (src_format.channels != dest_format.channels) {
- buf = pcm_convert_channels_24(channels_buffer,
- dest_format.channels,
- src_format.channels,
- buf, len, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format.channels,
- dest_format.channels);
- return nullptr;
- }
- }
-
- if (src_format.sample_rate != dest_format.sample_rate) {
- buf = resampler.Resample24(dest_format.channels,
- src_format.sample_rate, buf, len,
- dest_format.sample_rate, &len,
- error);
- if (buf == nullptr)
- return nullptr;
- }
+ dsd.Reset();
- *dest_size_r = len;
- return buf;
+#ifndef NDEBUG
+ src_format.Clear();
+ dest_format.Clear();
+#endif
}
-inline const int32_t *
-PcmConvert::Convert32(const AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- const AudioFormat dest_format, size_t *dest_size_r,
- Error &error)
+const void *
+PcmConvert::Convert(const void *src, size_t src_size,
+ size_t *dest_size_r,
+ Error &error)
{
- const int32_t *buf;
- size_t len;
-
- assert(dest_format.format == SampleFormat::S32);
-
- buf = pcm_convert_to_32(format_buffer,
- src_format.format,
- src_buffer, src_size, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to 32 bit is not implemented",
- sample_format_to_string(src_format.format));
- return nullptr;
- }
-
- if (src_format.channels != dest_format.channels) {
- buf = pcm_convert_channels_32(channels_buffer,
- dest_format.channels,
- src_format.channels,
- buf, len, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format.channels,
- dest_format.channels);
+ ConstBuffer<void> buffer(src, src_size);
+ AudioFormat format = src_format;
+
+ if (format.format == SampleFormat::DSD) {
+ auto s = ConstBuffer<uint8_t>::FromVoid(buffer);
+ auto d = dsd.ToFloat(format.channels,
+ false, s);
+ if (d.IsNull()) {
+ error.Set(pcm_domain,
+ "DSD to PCM conversion failed");
return nullptr;
}
- }
- if (src_format.sample_rate != dest_format.sample_rate) {
- buf = resampler.Resample32(dest_format.channels,
- src_format.sample_rate, buf, len,
- dest_format.sample_rate, &len,
- error);
- if (buf == nullptr)
- return buf;
+ buffer = d.ToVoid();
+ format.format = SampleFormat::FLOAT;
}
- *dest_size_r = len;
- return buf;
-}
-
-inline const float *
-PcmConvert::ConvertFloat(const AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- const AudioFormat dest_format, size_t *dest_size_r,
- Error &error)
-{
- const float *buffer = (const float *)src_buffer;
- size_t size = src_size;
-
- assert(dest_format.format == SampleFormat::FLOAT);
-
- /* convert to float now */
+ if (enable_resampler) {
+ buffer = resampler.Resample(buffer, error);
+ if (buffer.IsNull())
+ return nullptr;
- buffer = pcm_convert_to_float(format_buffer,
- src_format.format,
- buffer, size, &size);
- if (buffer == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to float is not implemented",
- sample_format_to_string(src_format.format));
- return nullptr;
+ format.format = resampler.GetOutputSampleFormat();
+ format.sample_rate = dest_format.sample_rate;
}
- /* convert channels */
-
- if (src_format.channels != dest_format.channels) {
- buffer = pcm_convert_channels_float(channels_buffer,
- dest_format.channels,
- src_format.channels,
- buffer, size, &size);
- if (buffer == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- src_format.channels,
- dest_format.channels);
+ if (enable_format) {
+ buffer = format_converter.Convert(buffer, error);
+ if (buffer.IsNull())
return nullptr;
- }
- }
- /* resample with float, because this is the best format for
- libsamplerate */
-
- if (src_format.sample_rate != dest_format.sample_rate) {
- buffer = resampler.ResampleFloat(dest_format.channels,
- src_format.sample_rate,
- buffer, size,
- dest_format.sample_rate,
- &size, error);
- if (buffer == nullptr)
- return nullptr;
+ format.format = dest_format.format;
}
- *dest_size_r = size;
- return buffer;
-}
-
-const void *
-PcmConvert::Convert(AudioFormat src_format,
- const void *src, size_t src_size,
- const AudioFormat dest_format,
- size_t *dest_size_r,
- Error &error)
-{
- AudioFormat float_format;
- if (src_format.format == SampleFormat::DSD) {
- size_t f_size;
- const float *f = dsd.ToFloat(src_format.channels,
- false, (const uint8_t *)src,
- src_size, &f_size);
- if (f == nullptr) {
- error.Set(pcm_convert_domain,
- "DSD to PCM conversion failed");
+ if (enable_channels) {
+ buffer = channels_converter.Convert(buffer, error);
+ if (buffer.IsNull())
return nullptr;
- }
- float_format = src_format;
- float_format.format = SampleFormat::FLOAT;
-
- src_format = float_format;
- src = f;
- src_size = f_size;
+ format.channels = dest_format.channels;
}
- switch (dest_format.format) {
- case SampleFormat::S16:
- return Convert16(src_format, src, src_size,
- dest_format, dest_size_r,
- error);
-
- case SampleFormat::S24_P32:
- return Convert24(src_format, src, src_size,
- dest_format, dest_size_r,
- error);
-
- case SampleFormat::S32:
- return Convert32(src_format, src, src_size,
- dest_format, dest_size_r,
- error);
-
- case SampleFormat::FLOAT:
- return ConvertFloat(src_format, src, src_size,
- dest_format, dest_size_r,
- error);
-
- default:
- error.Format(pcm_convert_domain,
- "PCM conversion to %s is not implemented",
- sample_format_to_string(dest_format.format));
- return nullptr;
- }
+ *dest_size_r = buffer.size;
+ return buffer.data;
}
diff --git a/src/pcm/PcmConvert.hxx b/src/pcm/PcmConvert.hxx
index 40f785179..586c303f2 100644
--- a/src/pcm/PcmConvert.hxx
+++ b/src/pcm/PcmConvert.hxx
@@ -20,15 +20,18 @@
#ifndef PCM_CONVERT_HXX
#define PCM_CONVERT_HXX
-#include "PcmDither.hxx"
#include "PcmDsd.hxx"
-#include "PcmResample.hxx"
#include "PcmBuffer.hxx"
+#include "FormatConverter.hxx"
+#include "ChannelsConverter.hxx"
+#include "GlueResampler.hxx"
+#include "AudioFormat.hxx"
#include <stddef.h>
-struct AudioFormat;
+template<typename T> struct ConstBuffer;
class Error;
+class Domain;
/**
* This object is statically allocated (within another struct), and
@@ -38,27 +41,29 @@ class Error;
class PcmConvert {
PcmDsd dsd;
- PcmResampler resampler;
+ GluePcmResampler resampler;
+ PcmFormatConverter format_converter;
+ PcmChannelsConverter channels_converter;
- PcmDither dither;
+ AudioFormat src_format, dest_format;
- /** the buffer for converting the sample format */
- PcmBuffer format_buffer;
-
- /** the buffer for converting the channel count */
- PcmBuffer channels_buffer;
+ bool enable_resampler, enable_format, enable_channels;
public:
PcmConvert();
~PcmConvert();
+ /**
+ * Prepare the object. Call Close() when done.
+ */
+ bool Open(AudioFormat _src_format, AudioFormat _dest_format,
+ Error &error);
/**
- * Reset the pcm_convert_state object. Use this at the
- * boundary between two distinct songs and each time the
- * format changes.
+ * Close the object after it was prepared with Open(). After
+ * that, it may be reused by calling Open() again.
*/
- void Reset();
+ void Close();
/**
* Converts PCM data between two audio formats.
@@ -72,38 +77,12 @@ public:
* ignore errors
* @return the destination buffer, or NULL on error
*/
- const void *Convert(AudioFormat src_format,
- const void *src, size_t src_size,
- AudioFormat dest_format,
+ const void *Convert(const void *src, size_t src_size,
size_t *dest_size_r,
Error &error);
-
-private:
- const int16_t *Convert16(AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- AudioFormat dest_format,
- size_t *dest_size_r,
- Error &error);
-
- const int32_t *Convert24(AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- AudioFormat dest_format,
- size_t *dest_size_r,
- Error &error);
-
- const int32_t *Convert32(AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- AudioFormat dest_format,
- size_t *dest_size_r,
- Error &error);
-
- const float *ConvertFloat(AudioFormat src_format,
- const void *src_buffer, size_t src_size,
- AudioFormat dest_format,
- size_t *dest_size_r,
- Error &error);
};
-extern const class Domain pcm_convert_domain;
+bool
+pcm_convert_global_init(Error &error);
#endif
diff --git a/src/pcm/PcmDither.cxx b/src/pcm/PcmDither.cxx
index 98d0d443e..71ec5db1e 100644
--- a/src/pcm/PcmDither.cxx
+++ b/src/pcm/PcmDither.cxx
@@ -20,18 +20,14 @@
#include "config.h"
#include "PcmDither.hxx"
#include "PcmPrng.hxx"
+#include "Traits.hxx"
-inline int16_t
-PcmDither::Dither24To16(int_fast32_t sample)
+template<typename T, T MIN, T MAX, unsigned scale_bits>
+inline T
+PcmDither::Dither(T sample)
{
- constexpr unsigned from_bits = 24;
- constexpr unsigned to_bits = 16;
- constexpr unsigned scale_bits = from_bits - to_bits;
- constexpr int_fast32_t round = 1 << (scale_bits - 1);
- constexpr int_fast32_t mask = (1 << scale_bits) - 1;
- constexpr int_fast32_t ONE = 1 << (from_bits - 1);
- constexpr int_fast32_t MIN = -ONE;
- constexpr int_fast32_t MAX = ONE - 1;
+ constexpr T round = 1 << (scale_bits - 1);
+ constexpr T mask = (1 << scale_bits) - 1;
sample += error[0] - error[1] + error[2];
@@ -39,9 +35,9 @@ PcmDither::Dither24To16(int_fast32_t sample)
error[1] = error[0] / 2;
/* round */
- int_fast32_t output = sample + round;
+ T output = sample + round;
- int_fast32_t rnd = pcm_prng(random);
+ const T rnd = pcm_prng(random);
output += (rnd & mask) - (random & mask);
random = rnd;
@@ -63,27 +59,46 @@ PcmDither::Dither24To16(int_fast32_t sample)
error[0] = sample - output;
- return (int16_t)(output >> scale_bits);
+ return output;
}
-void
-PcmDither::Dither24To16(int16_t *dest, const int32_t *src,
- const int32_t *src_end)
+template<typename ST, typename DT>
+inline typename DT::value_type
+PcmDither::DitherShift(typename ST::value_type sample)
+{
+ static_assert(ST::BITS > DT::BITS,
+ "Sample formats cannot be dithered");
+
+ constexpr unsigned scale_bits = ST::BITS - DT::BITS;
+
+ return Dither<typename ST::sum_type, ST::MIN, ST::MAX,
+ scale_bits>(sample) >> scale_bits;
+}
+
+template<typename ST, typename DT>
+inline void
+PcmDither::DitherShift(typename DT::pointer_type dest,
+ typename ST::const_pointer_type src,
+ typename ST::const_pointer_type src_end)
{
while (src < src_end)
- *dest++ = Dither24To16(*src++);
+ *dest++ = DitherShift<ST, DT>(*src++);
}
-inline int16_t
-PcmDither::Dither32To16(int_fast32_t sample)
+void
+PcmDither::Dither24To16(int16_t *dest, const int32_t *src,
+ const int32_t *src_end)
{
- return Dither24To16(sample >> 8);
+ typedef SampleTraits<SampleFormat::S24_P32> ST;
+ typedef SampleTraits<SampleFormat::S16> DT;
+ DitherShift<ST, DT>(dest, src, src_end);
}
void
PcmDither::Dither32To16(int16_t *dest, const int32_t *src,
const int32_t *src_end)
{
- while (src < src_end)
- *dest++ = Dither32To16(*src++);
+ typedef SampleTraits<SampleFormat::S32> ST;
+ typedef SampleTraits<SampleFormat::S16> DT;
+ DitherShift<ST, DT>(dest, src, src_end);
}
diff --git a/src/pcm/PcmDither.hxx b/src/pcm/PcmDither.hxx
index 106382307..2b75cdaeb 100644
--- a/src/pcm/PcmDither.hxx
+++ b/src/pcm/PcmDither.hxx
@@ -22,6 +22,8 @@
#include <stdint.h>
+enum class SampleFormat : uint8_t;
+
class PcmDither {
int32_t error[3];
int32_t random;
@@ -37,8 +39,16 @@ public:
const int32_t *src_end);
private:
- int16_t Dither24To16(int_fast32_t sample);
- int16_t Dither32To16(int_fast32_t sample);
+ template<typename T, T MIN, T MAX, unsigned scale_bits>
+ T Dither(T sample);
+
+ template<typename ST, typename DT>
+ typename DT::value_type DitherShift(typename ST::value_type sample);
+
+ template<typename ST, typename DT>
+ void DitherShift(typename DT::pointer_type dest,
+ typename ST::const_pointer_type src,
+ typename ST::const_pointer_type src_end);
};
#endif
diff --git a/src/pcm/PcmDsd.cxx b/src/pcm/PcmDsd.cxx
index 4db274635..5952cad7c 100644
--- a/src/pcm/PcmDsd.cxx
+++ b/src/pcm/PcmDsd.cxx
@@ -21,11 +21,11 @@
#include "PcmDsd.hxx"
#include "dsd2pcm/dsd2pcm.h"
#include "util/Macros.hxx"
+#include "util/ConstBuffer.hxx"
#include <algorithm>
#include <assert.h>
-#include <string.h>
PcmDsd::PcmDsd()
{
@@ -47,22 +47,20 @@ PcmDsd::Reset()
dsd2pcm_reset(dsd2pcm[i]);
}
-const float *
+ConstBuffer<float>
PcmDsd::ToFloat(unsigned channels, bool lsbfirst,
- const uint8_t *src, size_t src_size,
- size_t *dest_size_r)
+ ConstBuffer<uint8_t> src)
{
- assert(src != nullptr);
- assert(src_size > 0);
- assert(src_size % channels == 0);
+ assert(!src.IsNull());
+ assert(!src.IsEmpty());
+ assert(src.size % channels == 0);
assert(channels <= ARRAY_SIZE(dsd2pcm));
- const unsigned num_samples = src_size;
- const unsigned num_frames = src_size / channels;
+ const unsigned num_samples = src.size;
+ const unsigned num_frames = src.size / channels;
float *dest;
const size_t dest_size = num_samples * sizeof(*dest);
- *dest_size_r = dest_size;
dest = (float *)buffer.Get(dest_size);
for (unsigned c = 0; c < channels; ++c) {
@@ -73,9 +71,9 @@ PcmDsd::ToFloat(unsigned channels, bool lsbfirst,
}
dsd2pcm_translate(dsd2pcm[c], num_frames,
- src + c, channels,
+ src.data + c, channels,
lsbfirst, dest + c, channels);
}
- return dest;
+ return { dest, num_samples };
}
diff --git a/src/pcm/PcmDsd.hxx b/src/pcm/PcmDsd.hxx
index 26ee11b13..b9b6d51ee 100644
--- a/src/pcm/PcmDsd.hxx
+++ b/src/pcm/PcmDsd.hxx
@@ -25,22 +25,24 @@
#include <stdint.h>
+template<typename T> struct ConstBuffer;
+
/**
* Wrapper for the dsd2pcm library.
*/
-struct PcmDsd {
+class PcmDsd {
PcmBuffer buffer;
struct dsd2pcm_ctx_s *dsd2pcm[32];
+public:
PcmDsd();
~PcmDsd();
void Reset();
- const float *ToFloat(unsigned channels, bool lsbfirst,
- const uint8_t *src, size_t src_size,
- size_t *dest_size_r);
+ ConstBuffer<float> ToFloat(unsigned channels, bool lsbfirst,
+ ConstBuffer<uint8_t> src);
};
#endif
diff --git a/src/pcm/PcmDsdUsb.cxx b/src/pcm/PcmDsdUsb.cxx
index 2d0f33a15..833d84653 100644
--- a/src/pcm/PcmDsdUsb.cxx
+++ b/src/pcm/PcmDsdUsb.cxx
@@ -22,6 +22,8 @@
#include "PcmBuffer.hxx"
#include "AudioFormat.hxx"
+#include <assert.h>
+
constexpr
static inline uint32_t
pcm_two_dsd_to_usb_marker1(uint8_t a, uint8_t b)
diff --git a/src/pcm/PcmFormat.cxx b/src/pcm/PcmFormat.cxx
index 4565c71c6..a0e0bb2de 100644
--- a/src/pcm/PcmFormat.cxx
+++ b/src/pcm/PcmFormat.cxx
@@ -22,51 +22,7 @@
#include "PcmDither.hxx"
#include "PcmBuffer.hxx"
#include "PcmUtils.hxx"
-
-#include <type_traits>
-
-template<SampleFormat F>
-struct SampleTraits {};
-
-template<>
-struct SampleTraits<SampleFormat::S8> {
- typedef int8_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
-
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = sizeof(value_type) * 8;
-};
-
-template<>
-struct SampleTraits<SampleFormat::S16> {
- typedef int16_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
-
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = sizeof(value_type) * 8;
-};
-
-template<>
-struct SampleTraits<SampleFormat::S32> {
- typedef int32_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
-
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = sizeof(value_type) * 8;
-};
-
-template<>
-struct SampleTraits<SampleFormat::S24_P32> {
- typedef int32_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
-
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = 24;
-};
+#include "Traits.hxx"
static void
pcm_convert_8_to_16(int16_t *out, const int8_t *in, const int8_t *in_end)
@@ -100,8 +56,8 @@ ConvertFromFloat(typename Traits::pointer_type dest,
const float factor = 1 << (bits - 1);
while (src != end) {
- int sample(*src++ * factor);
- *dest++ = PcmClamp<typename Traits::value_type, int, bits>(sample);
+ typename Traits::long_type sample(*src++ * factor);
+ *dest++ = PcmClamp<F, Traits>(sample);
}
}
@@ -442,7 +398,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/PcmMix.cxx b/src/pcm/PcmMix.cxx
index 001794061..caf7fe516 100644
--- a/src/pcm/PcmMix.cxx
+++ b/src/pcm/PcmMix.cxx
@@ -19,42 +19,50 @@
#include "config.h"
#include "PcmMix.hxx"
-#include "PcmVolume.hxx"
+#include "Volume.hxx"
#include "PcmUtils.hxx"
#include "AudioFormat.hxx"
+#include "Traits.hxx"
+#include "util/Clamp.hxx"
+#include <assert.h>
#include <math.h>
-template<typename T, typename U, unsigned bits>
-static T
-PcmAddVolume(T _a, T _b, int volume1, int volume2)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static typename Traits::value_type
+PcmAddVolume(typename Traits::value_type _a, typename Traits::value_type _b,
+ int volume1, int volume2)
{
- U a(_a), b(_b);
+ typename Traits::long_type a(_a), b(_b);
- U c = ((a * volume1 + b * volume2) +
- pcm_volume_dither() + PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
+ typename Traits::value_type c = ((a * volume1 + b * volume2) +
+ pcm_volume_dither() + PCM_VOLUME_1S / 2)
+ / PCM_VOLUME_1S;
- return PcmClamp<T, U, bits>(c);
+ return PcmClamp<F, Traits>(c);
}
-template<typename T, typename U, unsigned bits>
+template<SampleFormat F, class Traits=SampleTraits<F>>
static void
-PcmAddVolume(T *a, const T *b, unsigned n, int volume1, int volume2)
+PcmAddVolume(typename Traits::pointer_type a,
+ typename Traits::const_pointer_type b,
+ size_t n, int volume1, int volume2)
{
for (size_t i = 0; i != n; ++i)
- a[i] = PcmAddVolume<T, U, bits>(a[i], b[i], volume1, volume2);
+ a[i] = PcmAddVolume<F, Traits>(a[i], b[i], volume1, volume2);
}
-template<typename T, typename U, unsigned bits>
+template<SampleFormat F, class Traits=SampleTraits<F>>
static void
PcmAddVolumeVoid(void *a, const void *b, size_t size, int volume1, int volume2)
{
- constexpr size_t sample_size = sizeof(T);
+ constexpr size_t sample_size = Traits::SAMPLE_SIZE;
assert(size % sample_size == 0);
- PcmAddVolume<T, U, bits>((T *)a, (const T *)b, size / sample_size,
- volume1, volume2);
+ PcmAddVolume<F, Traits>(typename Traits::pointer_type(a),
+ typename Traits::const_pointer_type(b),
+ size / sample_size,
+ volume1, volume2);
}
static void
@@ -83,23 +91,23 @@ pcm_add_vol(void *buffer1, const void *buffer2, size_t size,
return false;
case SampleFormat::S8:
- PcmAddVolumeVoid<int8_t, int32_t, 8>(buffer1, buffer2, size,
- vol1, vol2);
+ PcmAddVolumeVoid<SampleFormat::S8>(buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::S16:
- PcmAddVolumeVoid<int16_t, int32_t, 16>(buffer1, buffer2, size,
- vol1, vol2);
+ PcmAddVolumeVoid<SampleFormat::S16>(buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::S24_P32:
- PcmAddVolumeVoid<int32_t, int64_t, 24>(buffer1, buffer2, size,
- vol1, vol2);
+ PcmAddVolumeVoid<SampleFormat::S24_P32>(buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::S32:
- PcmAddVolumeVoid<int32_t, int64_t, 32>(buffer1, buffer2, size,
- vol1, vol2);
+ PcmAddVolumeVoid<SampleFormat::S32>(buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::FLOAT:
@@ -114,30 +122,35 @@ pcm_add_vol(void *buffer1, const void *buffer2, size_t size,
gcc_unreachable();
}
-template<typename T, typename U, unsigned bits>
-static T
-PcmAdd(T _a, T _b)
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static typename Traits::value_type
+PcmAdd(typename Traits::value_type _a, typename Traits::value_type _b)
{
- U a(_a), b(_b);
- return PcmClamp<T, U, bits>(a + b);
+ typename Traits::sum_type a(_a), b(_b);
+
+ return PcmClamp<F, Traits>(a + b);
}
-template<typename T, typename U, unsigned bits>
+template<SampleFormat F, class Traits=SampleTraits<F>>
static void
-PcmAdd(T *a, const T *b, unsigned n)
+PcmAdd(typename Traits::pointer_type a,
+ typename Traits::const_pointer_type b,
+ size_t n)
{
for (size_t i = 0; i != n; ++i)
- a[i] = PcmAdd<T, U, bits>(a[i], b[i]);
+ a[i] = PcmAdd<F, Traits>(a[i], b[i]);
}
-template<typename T, typename U, unsigned bits>
+template<SampleFormat F, class Traits=SampleTraits<F>>
static void
PcmAddVoid(void *a, const void *b, size_t size)
{
- constexpr size_t sample_size = sizeof(T);
+ constexpr size_t sample_size = Traits::SAMPLE_SIZE;
assert(size % sample_size == 0);
- PcmAdd<T, U, bits>((T *)a, (const T *)b, size / sample_size);
+ PcmAdd<F, Traits>(typename Traits::pointer_type(a),
+ typename Traits::const_pointer_type(b),
+ size / sample_size);
}
static void
@@ -162,19 +175,19 @@ pcm_add(void *buffer1, const void *buffer2, size_t size,
return false;
case SampleFormat::S8:
- PcmAddVoid<int8_t, int32_t, 8>(buffer1, buffer2, size);
+ PcmAddVoid<SampleFormat::S8>(buffer1, buffer2, size);
return true;
case SampleFormat::S16:
- PcmAddVoid<int16_t, int32_t, 16>(buffer1, buffer2, size);
+ PcmAddVoid<SampleFormat::S16>(buffer1, buffer2, size);
return true;
case SampleFormat::S24_P32:
- PcmAddVoid<int32_t, int64_t, 24>(buffer1, buffer2, size);
+ PcmAddVoid<SampleFormat::S24_P32>(buffer1, buffer2, size);
return true;
case SampleFormat::S32:
- PcmAddVoid<int32_t, int64_t, 32>(buffer1, buffer2, size);
+ PcmAddVoid<SampleFormat::S32>(buffer1, buffer2, size);
return true;
case SampleFormat::FLOAT:
@@ -191,7 +204,6 @@ bool
pcm_mix(void *buffer1, const void *buffer2, size_t size,
SampleFormat format, float portion1)
{
- int vol1;
float s;
/* portion1 is between 0.0 and 1.0 for crossfading, MixRamp uses -1
@@ -202,8 +214,8 @@ pcm_mix(void *buffer1, const void *buffer2, size_t size,
s = sin(M_PI_2 * portion1);
s *= s;
- vol1 = s * PCM_VOLUME_1 + 0.5;
- vol1 = vol1 > PCM_VOLUME_1 ? PCM_VOLUME_1 : (vol1 < 0 ? 0 : vol1);
+ int vol1 = s * PCM_VOLUME_1S + 0.5;
+ vol1 = Clamp<int>(vol1, 0, PCM_VOLUME_1S);
- return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1 - vol1, format);
+ return pcm_add_vol(buffer1, buffer2, size, vol1, PCM_VOLUME_1S - vol1, format);
}
diff --git a/src/pcm/PcmPrng.hxx b/src/pcm/PcmPrng.hxx
index 0c823250d..73b1456a8 100644
--- a/src/pcm/PcmPrng.hxx
+++ b/src/pcm/PcmPrng.hxx
@@ -24,7 +24,7 @@
* A very simple linear congruential PRNG. It's good enough for PCM
* dithering.
*/
-static unsigned long
+constexpr static inline unsigned long
pcm_prng(unsigned long state)
{
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
diff --git a/src/pcm/PcmResample.cxx b/src/pcm/PcmResample.cxx
deleted file mode 100644
index 01f269ea9..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(this, 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(this, 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(this, 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(this, 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 a62cd64f7..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(PcmResampler *state,
- 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 *)state->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(PcmResampler *state,
- 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 *)state->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 5090c13d1..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(PcmResampler *state,
- 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(PcmResampler *state,
- 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 9eac2d545..000000000
--- a/src/pcm/PcmResampleLibsamplerate.cxx
+++ /dev/null
@@ -1,310 +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;
-}
-
-#ifdef HAVE_LIBSAMPLERATE_NOINT
-
-/* libsamplerate introduced these functions in v0.1.3 */
-
-static void
-src_int_to_float_array(const int *in, float *out, int len)
-{
- while (len-- > 0)
- *out++ = *in++ / (float)(1 << (24 - 1));
-}
-
-static void
-src_float_to_int_array (const float *in, int *out, int len)
-{
- while (len-- > 0)
- *out++ = *in++ * (float)(1 << (24 - 1));
-}
-
-#endif
-
-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/PcmUtils.hxx b/src/pcm/PcmUtils.hxx
index febe12d7b..0ca5dd4ac 100644
--- a/src/pcm/PcmUtils.hxx
+++ b/src/pcm/PcmUtils.hxx
@@ -26,6 +26,9 @@
#include <stdint.h>
+enum class SampleFormat : uint8_t;
+template<SampleFormat F> struct SampleTraits;
+
/**
* Add a byte count to the specified pointer. This is a utility
* function to convert a source pointer and a byte count to an "end"
@@ -42,37 +45,24 @@ pcm_end_pointer(const T *p, size_t size)
* Check if the value is within the range of the provided bit size,
* and caps it if necessary.
*/
-template<typename T, typename U, unsigned bits>
+template<SampleFormat F, class Traits=SampleTraits<F>>
gcc_const
-static inline T
-PcmClamp(U x)
+static inline typename Traits::value_type
+PcmClamp(typename Traits::long_type x)
{
- constexpr U MIN_VALUE = -(U(1) << (bits - 1));
- constexpr U MAX_VALUE = (U(1) << (bits - 1)) - 1;
+ typedef typename Traits::value_type T;
typedef std::numeric_limits<T> limits;
- static_assert(MIN_VALUE >= limits::min(), "out of range");
- static_assert(MAX_VALUE <= limits::max(), "out of range");
+ static_assert(Traits::MIN >= limits::min(), "out of range");
+ static_assert(Traits::MAX <= limits::max(), "out of range");
- if (gcc_unlikely(x < MIN_VALUE))
- return T(MIN_VALUE);
+ if (gcc_unlikely(x < Traits::MIN))
+ return T(Traits::MIN);
- if (gcc_unlikely(x > MAX_VALUE))
- return T(MAX_VALUE);
+ if (gcc_unlikely(x > Traits::MAX))
+ return T(Traits::MAX);
return T(x);
}
-/**
- * Check if the values in this buffer are within the range of the
- * provided bit size, and clamps them whenever necessary.
- */
-template<typename T, typename U, unsigned bits>
-static inline void
-PcmClampN(T *dest, const U *src, unsigned n)
-{
- while (n-- > 0)
- *dest++ = PcmClamp<T, U, bits>(*src++);
-}
-
#endif
diff --git a/src/pcm/PcmVolume.cxx b/src/pcm/PcmVolume.cxx
deleted file mode 100644
index 564880633..000000000
--- a/src/pcm/PcmVolume.cxx
+++ /dev/null
@@ -1,188 +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 "PcmVolume.hxx"
-#include "PcmUtils.hxx"
-#include "AudioFormat.hxx"
-
-#include <stdint.h>
-#include <string.h>
-
-static void
-pcm_volume_change_8(int8_t *buffer, const int8_t *end, int volume)
-{
- while (buffer < end) {
- int32_t sample = *buffer;
-
- sample = (sample * volume + pcm_volume_dither() +
- PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-
- *buffer++ = PcmClamp<int8_t, int16_t, 8>(sample);
- }
-}
-
-static void
-pcm_volume_change_16(int16_t *buffer, const int16_t *end, int volume)
-{
- while (buffer < end) {
- int32_t sample = *buffer;
-
- sample = (sample * volume + pcm_volume_dither() +
- PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-
- *buffer++ = PcmClamp<int16_t, int32_t, 16>(sample);
- }
-}
-
-#ifdef __i386__
-/**
- * Optimized volume function for i386. Use the EDX:EAX 2*32 bit
- * multiplication result instead of emulating 64 bit multiplication.
- */
-static inline int32_t
-pcm_volume_sample_24(int32_t sample, int32_t volume, gcc_unused int32_t dither)
-{
- int32_t result;
-
- asm(/* edx:eax = sample * volume */
- "imul %2\n"
-
- /* "add %3, %1\n" dithering disabled for now, because we
- have no overflow check - is dithering really important
- here? */
-
- /* eax = edx:eax / PCM_VOLUME_1 */
- "sal $22, %%edx\n"
- "shr $10, %1\n"
- "or %%edx, %1\n"
-
- : "=a"(result)
- : "0"(sample), "r"(volume) /* , "r"(dither) */
- : "edx"
- );
-
- return result;
-}
-#endif
-
-static void
-pcm_volume_change_24(int32_t *buffer, const int32_t *end, int volume)
-{
- while (buffer < end) {
-#ifdef __i386__
- /* assembly version for i386 */
- int32_t sample = *buffer;
-
- sample = pcm_volume_sample_24(sample, volume,
- pcm_volume_dither());
-#else
- /* portable version */
- int64_t sample = *buffer;
-
- sample = (sample * volume + pcm_volume_dither() +
- PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
-#endif
- *buffer++ = PcmClamp<int32_t, int32_t, 24>(sample);
- }
-}
-
-static void
-pcm_volume_change_32(int32_t *buffer, const int32_t *end, int volume)
-{
- while (buffer < end) {
-#ifdef __i386__
- /* assembly version for i386 */
- int32_t sample = *buffer;
-
- *buffer++ = pcm_volume_sample_24(sample, volume, 0);
-#else
- /* portable version */
- int64_t sample = *buffer;
-
- sample = (sample * volume + pcm_volume_dither() +
- PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
- *buffer++ = PcmClamp<int32_t, int64_t, 32>(sample);
-#endif
- }
-}
-
-static void
-pcm_volume_change_float(float *buffer, const float *end, float volume)
-{
- while (buffer < end) {
- float sample = *buffer;
- sample *= volume;
- *buffer++ = sample;
- }
-}
-
-bool
-pcm_volume(void *buffer, size_t length,
- SampleFormat format,
- int volume)
-{
- if (volume == PCM_VOLUME_1)
- return true;
-
- if (volume <= 0) {
- memset(buffer, 0, length);
- return true;
- }
-
- const void *end = pcm_end_pointer(buffer, length);
- switch (format) {
- case SampleFormat::UNDEFINED:
- case SampleFormat::DSD:
- /* not implemented */
- return false;
-
- case SampleFormat::S8:
- pcm_volume_change_8((int8_t *)buffer, (const int8_t *)end,
- volume);
- return true;
-
- case SampleFormat::S16:
- pcm_volume_change_16((int16_t *)buffer, (const int16_t *)end,
- volume);
- return true;
-
- case SampleFormat::S24_P32:
- pcm_volume_change_24((int32_t *)buffer, (const int32_t *)end,
- volume);
- return true;
-
- case SampleFormat::S32:
- pcm_volume_change_32((int32_t *)buffer, (const int32_t *)end,
- volume);
- return true;
-
- case SampleFormat::FLOAT:
- pcm_volume_change_float((float *)buffer, (const float *)end,
- pcm_volume_to_float(volume));
- return true;
- }
-
- assert(false);
- gcc_unreachable();
-}
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
diff --git a/src/pcm/SoxrResampler.cxx b/src/pcm/SoxrResampler.cxx
new file mode 100644
index 000000000..e82ae1481
--- /dev/null
+++ b/src/pcm/SoxrResampler.cxx
@@ -0,0 +1,95 @@
+/*
+ * 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 "SoxrResampler.hxx"
+#include "AudioFormat.hxx"
+#include "util/ASCII.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
+
+#include <soxr.h>
+
+#include <assert.h>
+
+static constexpr Domain soxr_domain("soxr");
+
+AudioFormat
+SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate,
+ Error &error)
+{
+ assert(af.IsValid());
+ assert(audio_valid_sample_rate(new_sample_rate));
+
+ soxr_error_t e;
+ soxr = soxr_create(af.sample_rate, new_sample_rate,
+ af.channels, &e,
+ nullptr, nullptr, nullptr);
+ if (soxr == nullptr) {
+ error.Format(soxr_domain,
+ "soxr initialization has failed: %s", e);
+ return AudioFormat::Undefined();
+ }
+
+ FormatDebug(soxr_domain, "soxr engine '%s'", soxr_engine(soxr));
+
+ channels = af.channels;
+
+ ratio = float(new_sample_rate) / float(af.sample_rate);
+ FormatDebug(soxr_domain,
+ "samplerate conversion ratio to %.2lf",
+ ratio);
+
+ /* libsoxr works with floating point samples */
+ af.format = SampleFormat::FLOAT;
+
+ AudioFormat result = af;
+ result.sample_rate = new_sample_rate;
+ return result;
+}
+
+void
+SoxrPcmResampler::Close()
+{
+ soxr_delete(soxr);
+}
+
+ConstBuffer<void>
+SoxrPcmResampler::Resample(ConstBuffer<void> src, Error &error)
+{
+ const size_t frame_size = channels * sizeof(float);
+ assert(src.size % frame_size == 0);
+
+ const size_t n_frames = src.size / frame_size;
+
+ const size_t o_frames = size_t(n_frames * ratio + 0.5);
+
+ float *output_buffer = (float *)buffer.Get(o_frames * frame_size);
+
+ size_t i_done, o_done;
+ soxr_error_t e = soxr_process(soxr, src.data, n_frames, &i_done,
+ output_buffer, o_frames, &o_done);
+ if (e != nullptr) {
+ error.Format(soxr_domain, "soxr error: %s", e);
+ return nullptr;
+ }
+
+ return { output_buffer, o_done * frame_size };
+}
diff --git a/src/pcm/SoxrResampler.hxx b/src/pcm/SoxrResampler.hxx
new file mode 100644
index 000000000..69c173741
--- /dev/null
+++ b/src/pcm/SoxrResampler.hxx
@@ -0,0 +1,47 @@
+/*
+ * 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_SOXR_RESAMPLER_HXX
+#define MPD_PCM_SOXR_RESAMPLER_HXX
+
+#include "Resampler.hxx"
+#include "PcmBuffer.hxx"
+
+struct AudioFormat;
+
+/**
+ * A resampler using soxr.
+ */
+class SoxrPcmResampler final : public PcmResampler {
+ struct soxr *soxr;
+
+ unsigned channels;
+ float ratio;
+
+ 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/Traits.hxx b/src/pcm/Traits.hxx
new file mode 100644
index 000000000..4e68191f6
--- /dev/null
+++ b/src/pcm/Traits.hxx
@@ -0,0 +1,153 @@
+/*
+ * 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_TRAITS_HXX
+#define MPD_PCM_TRAITS_HXX
+
+#include "check.h"
+#include "AudioFormat.hxx"
+
+#include <stdint.h>
+#include <stddef.h>
+
+/**
+ * This template describes the specified #SampleFormat. This is an
+ * empty prototype; the specializations contain the real definitions.
+ * See SampleTraits<uint8_t> for more documentation.
+ */
+template<SampleFormat F>
+struct SampleTraits {};
+
+template<>
+struct SampleTraits<SampleFormat::S8> {
+ /**
+ * The type used for one sample value.
+ */
+ typedef int8_t value_type;
+
+ /**
+ * A writable pointer.
+ */
+ typedef value_type *pointer_type;
+
+ /**
+ * A read-only pointer.
+ */
+ typedef const value_type *const_pointer_type;
+
+ /**
+ * A "long" type that is large and accurate enough for adding
+ * two values without risking an (integer) overflow or
+ * (floating point) precision loss.
+ */
+ typedef int sum_type;
+
+ /**
+ * A "long" type that is large and accurate enough for
+ * arithmetic without risking an (integer) overflow or
+ * (floating point) precision loss.
+ */
+ typedef int_least16_t long_type;
+
+ /**
+ * The size of one sample in bytes.
+ */
+ static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
+
+ /**
+ * The integer bit depth of one sample. This attribute may
+ * not exist if this is not an integer sample format.
+ */
+ static constexpr unsigned BITS = sizeof(value_type) * 8;
+
+ /**
+ * The minimum sample value.
+ */
+ static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
+
+ /**
+ * The maximum sample value.
+ */
+ static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
+};
+
+template<>
+struct SampleTraits<SampleFormat::S16> {
+ typedef int16_t value_type;
+ typedef value_type *pointer_type;
+ typedef const value_type *const_pointer_type;
+
+ typedef int_least32_t sum_type;
+ typedef int_least32_t long_type;
+
+ static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
+ static constexpr unsigned BITS = sizeof(value_type) * 8;
+
+ static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
+ static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
+};
+
+template<>
+struct SampleTraits<SampleFormat::S32> {
+ typedef int32_t value_type;
+ typedef value_type *pointer_type;
+ typedef const value_type *const_pointer_type;
+
+ typedef int_least64_t sum_type;
+ typedef int_least64_t long_type;
+
+ static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
+ static constexpr unsigned BITS = sizeof(value_type) * 8;
+
+ static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
+ static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
+};
+
+template<>
+struct SampleTraits<SampleFormat::S24_P32> {
+ typedef int32_t value_type;
+ typedef value_type *pointer_type;
+ typedef const value_type *const_pointer_type;
+
+ typedef int_least32_t sum_type;
+ typedef int_least64_t long_type;
+
+ static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
+ static constexpr unsigned BITS = 24;
+
+ static constexpr value_type MIN = -(sum_type(1) << (BITS - 1));
+ static constexpr value_type MAX = (sum_type(1) << (BITS - 1)) - 1;
+};
+
+template<>
+struct SampleTraits<SampleFormat::FLOAT> {
+ typedef float value_type;
+ typedef value_type *pointer_type;
+ typedef const value_type *const_pointer_type;
+
+ typedef float sum_type;
+ typedef float long_type;
+
+ static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
+
+ static constexpr value_type MIN = -1;
+ static constexpr value_type MAX = 1;
+};
+
+#endif
diff --git a/src/pcm/Volume.cxx b/src/pcm/Volume.cxx
new file mode 100644
index 000000000..539ef1238
--- /dev/null
+++ b/src/pcm/Volume.cxx
@@ -0,0 +1,232 @@
+/*
+ * 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 "Volume.hxx"
+#include "Domain.hxx"
+#include "PcmUtils.hxx"
+#include "Traits.hxx"
+#include "util/ConstBuffer.hxx"
+#include "util/Error.hxx"
+
+#include <stdint.h>
+#include <string.h>
+
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static inline typename Traits::value_type
+pcm_volume_sample(typename Traits::value_type _sample,
+ int volume)
+{
+ typename Traits::long_type sample(_sample);
+
+ sample = (sample * volume + pcm_volume_dither() +
+ PCM_VOLUME_1S / 2)
+ >> PCM_VOLUME_BITS;
+
+ return PcmClamp<F, Traits>(sample);
+}
+
+#ifdef __i386__
+
+/**
+ * Optimized volume function for i386. Use the EDX:EAX 2*32 bit
+ * multiplication result instead of emulating 64 bit multiplication.
+ */
+static inline int32_t
+pcm_volume_sample_24(int32_t sample, int32_t volume, gcc_unused int32_t dither)
+{
+ int32_t result;
+
+ asm(/* edx:eax = sample * volume */
+ "imul %2\n"
+
+ /* "add %3, %1\n" dithering disabled for now, because we
+ have no overflow check - is dithering really important
+ here? */
+
+ /* eax = edx:eax / PCM_VOLUME_1 */
+ "sal $22, %%edx\n"
+ "shr $10, %1\n"
+ "or %%edx, %1\n"
+
+ : "=a"(result)
+ : "0"(sample), "r"(volume) /* , "r"(dither) */
+ : "edx"
+ );
+
+ return result;
+}
+
+template<>
+inline int32_t
+pcm_volume_sample<SampleFormat::S24_P32,
+ SampleTraits<SampleFormat::S24_P32>>(int32_t sample,
+ int volume)
+{
+ return pcm_volume_sample_24(sample, volume, pcm_volume_dither());
+}
+
+template<>
+inline int32_t
+pcm_volume_sample<SampleFormat::S32,
+ SampleTraits<SampleFormat::S32>>(int32_t sample, int volume)
+{
+ return pcm_volume_sample_24(sample, volume, pcm_volume_dither());
+}
+
+#endif
+
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static void
+pcm_volume_change(typename Traits::pointer_type dest,
+ typename Traits::const_pointer_type src,
+ typename Traits::const_pointer_type end,
+ int volume)
+{
+ while (src < end) {
+ const auto sample = *src++;
+ *dest++ = pcm_volume_sample<F, Traits>(sample, volume);
+ }
+}
+
+static void
+pcm_volume_change_8(int8_t *dest, const int8_t *src, const int8_t *end,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S8>(dest, src, end, volume);
+}
+
+static void
+pcm_volume_change_16(int16_t *dest, const int16_t *src, const int16_t *end,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S16>(dest, src, end, volume);
+}
+
+static void
+pcm_volume_change_24(int32_t *dest, const int32_t *src, const int32_t *end,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S24_P32>(dest, src, end, volume);
+}
+
+static void
+pcm_volume_change_32(int32_t *dest, const int32_t *src, const int32_t *end,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S32>(dest, src, end, volume);
+}
+
+static void
+pcm_volume_change_float(float *dest, const float *src, const float *end,
+ float volume)
+{
+ while (src < end) {
+ float sample = *src++;
+ sample *= volume;
+ *dest++ = sample;
+ }
+}
+
+bool
+PcmVolume::Open(SampleFormat _format, Error &error)
+{
+ assert(format == SampleFormat::UNDEFINED);
+
+ switch (_format) {
+ case SampleFormat::UNDEFINED:
+ case SampleFormat::DSD:
+ error.Format(pcm_domain,
+ "Software volume for %s is not implemented",
+ sample_format_to_string(_format));
+ return false;
+
+ case SampleFormat::S8:
+ case SampleFormat::S16:
+ case SampleFormat::S24_P32:
+ case SampleFormat::S32:
+ case SampleFormat::FLOAT:
+ break;
+ }
+
+ format = _format;
+ return true;
+}
+
+ConstBuffer<void>
+PcmVolume::Apply(ConstBuffer<void> src)
+{
+ if (volume == PCM_VOLUME_1)
+ return src;
+
+ void *data = buffer.Get(src.size);
+
+ if (volume == 0) {
+ /* optimized special case: 0% volume = memset(0) */
+ /* TODO: is this valid for all sample formats? What
+ about floating point? */
+ memset(data, 0, src.size);
+ return { data, src.size };
+ }
+
+ const void *end = pcm_end_pointer(src.data, src.size);
+ switch (format) {
+ case SampleFormat::UNDEFINED:
+ case SampleFormat::DSD:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S8:
+ pcm_volume_change_8((int8_t *)data,
+ (const int8_t *)src.data,
+ (const int8_t *)end,
+ volume);
+ break;
+
+ case SampleFormat::S16:
+ pcm_volume_change_16((int16_t *)data,
+ (const int16_t *)src.data,
+ (const int16_t *)end,
+ volume);
+ break;
+
+ case SampleFormat::S24_P32:
+ pcm_volume_change_24((int32_t *)data,
+ (const int32_t *)src.data,
+ (const int32_t *)end,
+ volume);
+ break;
+
+ case SampleFormat::S32:
+ pcm_volume_change_32((int32_t *)data,
+ (const int32_t *)src.data,
+ (const int32_t *)end,
+ volume);
+ break;
+
+ case SampleFormat::FLOAT:
+ pcm_volume_change_float((float *)data,
+ (const float *)src.data,
+ (const float *)end,
+ pcm_volume_to_float(volume));
+ break;
+ }
+
+ return { data, src.size };
+}
diff --git a/src/pcm/PcmVolume.hxx b/src/pcm/Volume.hxx
index 8cd82acf7..c31aafb6e 100644
--- a/src/pcm/PcmVolume.hxx
+++ b/src/pcm/Volume.hxx
@@ -22,14 +22,28 @@
#include "PcmPrng.hxx"
#include "AudioFormat.hxx"
+#include "PcmBuffer.hxx"
#include <stdint.h>
#include <stddef.h>
-enum {
- /** this value means "100% volume" */
- PCM_VOLUME_1 = 1024,
-};
+#ifndef NDEBUG
+#include <assert.h>
+#endif
+
+class Error;
+template<typename T> struct ConstBuffer;
+
+/**
+ * Number of fractional bits for a fixed-point volume value.
+ */
+static constexpr unsigned PCM_VOLUME_BITS = 10;
+
+/**
+ * This value means "100% volume".
+ */
+static constexpr unsigned PCM_VOLUME_1 = 1024;
+static constexpr int PCM_VOLUME_1S = PCM_VOLUME_1;
struct AudioFormat;
@@ -65,17 +79,66 @@ pcm_volume_dither(void)
}
/**
- * Adjust the volume of the specified PCM buffer.
- *
- * @param buffer the PCM buffer
- * @param length the length of the PCM buffer
- * @param format the sample format of the PCM buffer
- * @param volume the volume between 0 and #PCM_VOLUME_1
- * @return true on success, false if the audio format is not supported
+ * A class that converts samples from one format to another.
*/
-bool
-pcm_volume(void *buffer, size_t length,
- SampleFormat format,
- int volume);
+class PcmVolume {
+ SampleFormat format;
+
+ unsigned volume;
+
+ PcmBuffer buffer;
+
+public:
+ PcmVolume()
+ :volume(PCM_VOLUME_1) {
+#ifndef NDEBUG
+ format = SampleFormat::UNDEFINED;
+#endif
+ }
+
+#ifndef NDEBUG
+ ~PcmVolume() {
+ assert(format == SampleFormat::UNDEFINED);
+ }
+#endif
+
+ unsigned GetVolume() const {
+ return volume;
+ }
+
+ /**
+ * @param _volume the volume level in the range
+ * [0..#PCM_VOLUME_1]; may be bigger than #PCM_VOLUME_1, but
+ * then it will most likely clip a lot
+ */
+ void SetVolume(unsigned _volume) {
+ volume = _volume;
+ }
+
+ /**
+ * Opens the object, prepare for Apply().
+ *
+ * @param format the sample format
+ * @param error location to store the error
+ * @return true on success
+ */
+ bool Open(SampleFormat format, Error &error);
+
+ /**
+ * Closes the object. After that, you may call Open() again.
+ */
+ void Close() {
+#ifndef NDEBUG
+ assert(format != SampleFormat::UNDEFINED);
+ format = SampleFormat::UNDEFINED;
+#endif
+ }
+
+ /**
+ * Apply the volume level.
+ */
+ gcc_pure
+ ConstBuffer<void> Apply(ConstBuffer<void> src);
+};
#endif