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/FloatConvert.hxx64
-rw-r--r--src/pcm/FormatConverter.cxx89
-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/Neon.hxx93
-rw-r--r--src/pcm/PcmBuffer.cxx3
-rw-r--r--src/pcm/PcmBuffer.hxx8
-rw-r--r--src/pcm/PcmChannels.cxx297
-rw-r--r--src/pcm/PcmChannels.hxx35
-rw-r--r--src/pcm/PcmConvert.cxx334
-rw-r--r--src/pcm/PcmConvert.hxx67
-rw-r--r--src/pcm/PcmDither.cxx76
-rw-r--r--src/pcm/PcmDither.hxx46
-rw-r--r--src/pcm/PcmDsd.cxx24
-rw-r--r--src/pcm/PcmDsd.hxx12
-rw-r--r--src/pcm/PcmDsdUsb.cxx4
-rw-r--r--src/pcm/PcmDsdUsb.hxx2
-rw-r--r--src/pcm/PcmExport.cxx2
-rw-r--r--src/pcm/PcmExport.hxx2
-rw-r--r--src/pcm/PcmFormat.cxx532
-rw-r--r--src/pcm/PcmFormat.hxx33
-rw-r--r--src/pcm/PcmMix.cxx119
-rw-r--r--src/pcm/PcmMix.hxx6
-rw-r--r--src/pcm/PcmPack.cxx2
-rw-r--r--src/pcm/PcmPack.hxx2
-rw-r--r--src/pcm/PcmPrng.hxx4
-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.hxx48
-rw-r--r--src/pcm/PcmVolume.cxx188
-rw-r--r--src/pcm/PcmVolume.hxx81
-rw-r--r--src/pcm/Resampler.hxx74
-rw-r--r--src/pcm/ShiftConvert.hxx69
-rw-r--r--src/pcm/SoxrResampler.cxx162
-rw-r--r--src/pcm/SoxrResampler.hxx50
-rw-r--r--src/pcm/Traits.hxx153
-rw-r--r--src/pcm/Volume.cxx189
-rw-r--r--src/pcm/Volume.hxx130
52 files changed, 2738 insertions, 2095 deletions
diff --git a/src/pcm/ChannelsConverter.cxx b/src/pcm/ChannelsConverter.cxx
new file mode 100644
index 000000000..f93f4f677
--- /dev/null
+++ b/src/pcm/ChannelsConverter.cxx
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003-2014 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..1374f9f5d
--- /dev/null
+++ b/src/pcm/ChannelsConverter.hxx
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003-2014 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..f6aec3f95
--- /dev/null
+++ b/src/pcm/ConfiguredResampler.cxx
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2003-2014 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 "config/ConfigGlobal.hxx"
+#include "config/ConfigOption.hxx"
+#include "config/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 (memcmp(converter, "soxr", 4) == 0) {
+ selected_resampler = SelectedResampler::SOXR;
+ return pcm_resample_soxr_global_init(converter, error);
+ }
+#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..2b14b381e
--- /dev/null
+++ b/src/pcm/ConfiguredResampler.hxx
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2003-2014 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..ecd5c22a4
--- /dev/null
+++ b/src/pcm/Domain.cxx
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2003-2014 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..781d5c71b
--- /dev/null
+++ b/src/pcm/Domain.hxx
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2003-2014 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..bd3f20d86
--- /dev/null
+++ b/src/pcm/FallbackResampler.cxx
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2003-2014 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[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[src_pos];
+ dest_buffer[dest_pos++] = src[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).ToVoid();
+}
+
+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..0b7f9d57b
--- /dev/null
+++ b/src/pcm/FallbackResampler.hxx
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2003-2014 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/FloatConvert.hxx b/src/pcm/FloatConvert.hxx
new file mode 100644
index 000000000..93e867159
--- /dev/null
+++ b/src/pcm/FloatConvert.hxx
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003-2014 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_FLOAT_CONVERT_HXX
+#define MPD_PCM_FLOAT_CONVERT_HXX
+
+#include "Traits.hxx"
+
+/**
+ * Convert from float to an integer sample format.
+ */
+template<SampleFormat F, class Traits=SampleTraits<F>>
+struct FloatToIntegerSampleConvert {
+ typedef SampleTraits<SampleFormat::FLOAT> SrcTraits;
+ typedef Traits DstTraits;
+
+ typedef typename SrcTraits::value_type SV;
+ typedef typename SrcTraits::long_type SL;
+ typedef typename DstTraits::value_type DV;
+
+ static constexpr SV factor = 1 << (DstTraits::BITS - 1);
+
+ gcc_const
+ static DV Convert(SV src) {
+ return PcmClamp<F, Traits>(SL(src * factor));
+ }
+};
+
+/**
+ * Convert from an integer sample format to float.
+ */
+template<SampleFormat F, class Traits=SampleTraits<F>>
+struct IntegerToFloatSampleConvert {
+ typedef SampleTraits<SampleFormat::FLOAT> DstTraits;
+ typedef Traits SrcTraits;
+
+ typedef typename SrcTraits::value_type SV;
+ typedef typename DstTraits::value_type DV;
+
+ static constexpr DV factor = 0.5 / (1 << (SrcTraits::BITS - 2));
+
+ gcc_const
+ static DV Convert(SV src) {
+ return DV(src) * factor;
+ }
+};
+
+#endif
diff --git a/src/pcm/FormatConverter.cxx b/src/pcm/FormatConverter.cxx
new file mode 100644
index 000000000..64e2d8594
--- /dev/null
+++ b/src/pcm/FormatConverter.cxx
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2003-2014 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)
+{
+ switch (dest_format) {
+ case SampleFormat::UNDEFINED:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S8:
+ case SampleFormat::DSD:
+ 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;
+
+ case SampleFormat::S16:
+ return pcm_convert_to_16(buffer, dither,
+ src_format,
+ src).ToVoid();
+
+ case SampleFormat::S24_P32:
+ return pcm_convert_to_24(buffer,
+ src_format,
+ src).ToVoid();
+
+ case SampleFormat::S32:
+ return pcm_convert_to_32(buffer,
+ src_format,
+ src).ToVoid();
+
+ case SampleFormat::FLOAT:
+ return pcm_convert_to_float(buffer,
+ src_format,
+ src).ToVoid();
+ }
+
+ assert(false);
+ gcc_unreachable();
+}
diff --git a/src/pcm/FormatConverter.hxx b/src/pcm/FormatConverter.hxx
new file mode 100644
index 000000000..3d8b6fb75
--- /dev/null
+++ b/src/pcm/FormatConverter.hxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2014 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..0f5fe0271
--- /dev/null
+++ b/src/pcm/GlueResampler.cxx
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2003-2014 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..aff07823e
--- /dev/null
+++ b/src/pcm/GlueResampler.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2014 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..8b22f1e32
--- /dev/null
+++ b/src/pcm/LibsamplerateResampler.cxx
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2003-2014 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..86d74d95a
--- /dev/null
+++ b/src/pcm/LibsamplerateResampler.hxx
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2003-2014 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/Neon.hxx b/src/pcm/Neon.hxx
new file mode 100644
index 000000000..7109778ab
--- /dev/null
+++ b/src/pcm/Neon.hxx
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2003-2014 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_NEON_HXX
+#define MPD_PCM_NEON_HXX
+
+#include "Traits.hxx"
+
+#include <arm_neon.h>
+
+/**
+ * Call a NEON intrinsic for each element in the vector.
+ *
+ * @param func the NEON intrinsic
+ * @param result the vector variable that gets assigned the result
+ * @param vector the input vector
+ */
+#define neon_x4_u(func, result, vector) do { \
+ result.val[0] = func(vector.val[0]); \
+ result.val[1] = func(vector.val[1]); \
+ result.val[2] = func(vector.val[2]); \
+ result.val[3] = func(vector.val[3]); \
+} while (0)
+
+/**
+ * Call a NEON intrinsic for each element in the vector.
+ *
+ * @param func the NEON intrinsic
+ * @param result the vector variable that gets assigned the result
+ * @param vector the input vector
+ */
+#define neon_x4_b(func, result, vector, ...) do { \
+ result.val[0] = func(vector.val[0], __VA_ARGS__); \
+ result.val[1] = func(vector.val[1], __VA_ARGS__); \
+ result.val[2] = func(vector.val[2], __VA_ARGS__); \
+ result.val[3] = func(vector.val[3], __VA_ARGS__); \
+} while (0)
+
+/**
+ * Convert floating point samples to 16 bit signed integer using ARM NEON.
+ */
+struct NeonFloatTo16 {
+ static constexpr SampleFormat src_format = SampleFormat::FLOAT;
+ static constexpr SampleFormat dst_format = SampleFormat::S16;
+ typedef SampleTraits<src_format> SrcTraits;
+ typedef SampleTraits<dst_format> DstTraits;
+
+ typedef typename SrcTraits::value_type SV;
+ typedef typename DstTraits::value_type DV;
+
+ static constexpr size_t BLOCK_SIZE = 16;
+
+ void Convert(int16_t *dst, const float *src, const size_t n) const {
+ for (unsigned i = 0; i < n / BLOCK_SIZE;
+ ++i, src += BLOCK_SIZE, dst += BLOCK_SIZE) {
+ /* load 16 float samples into 4 quad
+ registers */
+ float32x4x4_t value = vld4q_f32(src);
+
+ /* convert to 32 bit integer */
+ int32x4x4_t ivalue;
+ neon_x4_b(vcvtq_n_s32_f32, ivalue, value,
+ 30);
+
+ /* convert to 16 bit integer with saturation
+ and rounding */
+ int16x4x4_t nvalue;
+ neon_x4_b(vqrshrn_n_s32, nvalue, ivalue,
+ 30 - DstTraits::BITS + 1);
+
+ /* store result */
+ vst4_s16(dst, nvalue);
+ }
+ }
+};
+
+#endif
diff --git a/src/pcm/PcmBuffer.cxx b/src/pcm/PcmBuffer.cxx
index 578c579be..7bba2de47 100644
--- a/src/pcm/PcmBuffer.cxx
+++ b/src/pcm/PcmBuffer.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..f56a85985 100644
--- a/src/pcm/PcmBuffer.hxx
+++ b/src/pcm/PcmBuffer.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..276f31045 100644
--- a/src/pcm/PcmChannels.cxx
+++ b/src/pcm/PcmChannels.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..6ad093c3b 100644
--- a/src/pcm/PcmChannels.hxx
+++ b/src/pcm/PcmChannels.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..ad79e60fc 100644
--- a/src/pcm/PcmConvert.cxx
+++ b/src/pcm/PcmConvert.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..7d05b3bb2 100644
--- a/src/pcm/PcmConvert.hxx
+++ b/src/pcm/PcmConvert.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..7b2a9e900 100644
--- a/src/pcm/PcmDither.cxx
+++ b/src/pcm/PcmDither.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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,59 @@ PcmDither::Dither24To16(int_fast32_t sample)
error[0] = sample - output;
- return (int16_t)(output >> scale_bits);
+ return output >> scale_bits;
}
-void
-PcmDither::Dither24To16(int16_t *dest, const int32_t *src,
- const int32_t *src_end)
+template<typename ST, unsigned SBITS, unsigned DBITS>
+inline ST
+PcmDither::DitherShift(ST sample)
+{
+ static_assert(sizeof(ST) * 8 > SBITS, "Source type too small");
+ static_assert(SBITS > DBITS, "Non-positive scale_bits");
+
+ static constexpr ST MIN = -(ST(1) << (SBITS - 1));
+ static constexpr ST MAX = (ST(1) << (SBITS - 1)) - 1;
+
+ return Dither<ST, MIN, MAX, SBITS - DBITS>(sample);
+}
+
+template<typename ST, typename DT>
+inline typename DT::value_type
+PcmDither::DitherConvert(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);
+}
+
+template<typename ST, typename DT>
+inline void
+PcmDither::DitherConvert(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++ = DitherConvert<ST, DT>(*src++);
}
-inline int16_t
-PcmDither::Dither32To16(int_fast32_t sample)
+inline 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;
+ DitherConvert<ST, DT>(dest, src, src_end);
}
-void
+inline 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;
+ DitherConvert<ST, DT>(dest, src, src_end);
}
diff --git a/src/pcm/PcmDither.hxx b/src/pcm/PcmDither.hxx
index 106382307..54b0f7315 100644
--- a/src/pcm/PcmDither.hxx
+++ b/src/pcm/PcmDither.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,6 +22,8 @@
#include <stdint.h>
+enum class SampleFormat : uint8_t;
+
class PcmDither {
int32_t error[3];
int32_t random;
@@ -30,6 +32,18 @@ public:
constexpr PcmDither()
:error{0, 0, 0}, random(0) {}
+ /**
+ * Shift the given sample by #SBITS-#DBITS to the right, and
+ * apply dithering.
+ *
+ * @param ST the input sample type
+ * @param SBITS the input bit width
+ * @param DBITS the output bit width
+ * @param sample the input sample value
+ */
+ template<typename ST, unsigned SBITS, unsigned DBITS>
+ ST DitherShift(ST sample);
+
void Dither24To16(int16_t *dest, const int32_t *src,
const int32_t *src_end);
@@ -37,8 +51,34 @@ public:
const int32_t *src_end);
private:
- int16_t Dither24To16(int_fast32_t sample);
- int16_t Dither32To16(int_fast32_t sample);
+ /**
+ * Shift the given sample by #scale_bits to the right, and
+ * apply dithering.
+ *
+ * @param T the input sample type
+ * @param MIN the minimum input sample value
+ * @param MAX the maximum input sample value
+ * @param scale_bits the number of bits to be discarded
+ * @param sample the input sample value
+ */
+ template<typename T, T MIN, T MAX, unsigned scale_bits>
+ T Dither(T sample);
+
+ /**
+ * Convert the given sample from one sample format to another,
+ * discarding bits.
+ *
+ * @param ST the input #SampleTraits class
+ * @param ST the output #SampleTraits class
+ * @param sample the input sample value
+ */
+ template<typename ST, typename DT>
+ typename DT::value_type DitherConvert(typename ST::value_type sample);
+
+ template<typename ST, typename DT>
+ void DitherConvert(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..ee549658d 100644
--- a/src/pcm/PcmDsd.cxx
+++ b/src/pcm/PcmDsd.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..cb3ef1fd6 100644
--- a/src/pcm/PcmDsd.hxx
+++ b/src/pcm/PcmDsd.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..1bfb4206f 100644
--- a/src/pcm/PcmDsdUsb.cxx
+++ b/src/pcm/PcmDsdUsb.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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/PcmDsdUsb.hxx b/src/pcm/PcmDsdUsb.hxx
index 3b7121465..bb31d2d45 100644
--- a/src/pcm/PcmDsdUsb.hxx
+++ b/src/pcm/PcmDsdUsb.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm/PcmExport.cxx b/src/pcm/PcmExport.cxx
index f6ce1e661..fc3ef57ca 100644
--- a/src/pcm/PcmExport.cxx
+++ b/src/pcm/PcmExport.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm/PcmExport.hxx b/src/pcm/PcmExport.hxx
index bd18c0534..61ac14c6f 100644
--- a/src/pcm/PcmExport.hxx
+++ b/src/pcm/PcmExport.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm/PcmFormat.cxx b/src/pcm/PcmFormat.cxx
index 4565c71c6..4cabc05a0 100644
--- a/src/pcm/PcmFormat.cxx
+++ b/src/pcm/PcmFormat.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,171 +19,150 @@
#include "config.h"
#include "PcmFormat.hxx"
-#include "PcmDither.hxx"
#include "PcmBuffer.hxx"
#include "PcmUtils.hxx"
+#include "Traits.hxx"
+#include "FloatConvert.hxx"
+#include "ShiftConvert.hxx"
+#include "util/ConstBuffer.hxx"
-#include <type_traits>
+#include "PcmDither.cxx" // including the .cxx file to get inlined templates
-template<SampleFormat F>
-struct SampleTraits {};
+/**
+ * Wrapper for a class that converts one sample at a time into one
+ * that converts a buffer at a time.
+ */
+template<typename C>
+struct PerSampleConvert : C {
+ typedef typename C::SrcTraits SrcTraits;
+ typedef typename C::DstTraits DstTraits;
+
+ void Convert(typename DstTraits::pointer_type gcc_restrict out,
+ typename SrcTraits::const_pointer_type gcc_restrict in,
+ size_t n) const {
+ for (size_t i = 0; i != n; ++i)
+ out[i] = C::Convert(in[i]);
+ }
+};
-template<>
-struct SampleTraits<SampleFormat::S8> {
- typedef int8_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
+struct Convert8To16
+ : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
+ SampleFormat::S16>> {};
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = sizeof(value_type) * 8;
-};
+struct Convert24To16 {
+ typedef SampleTraits<SampleFormat::S24_P32> SrcTraits;
+ typedef SampleTraits<SampleFormat::S16> DstTraits;
-template<>
-struct SampleTraits<SampleFormat::S16> {
- typedef int16_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
+ PcmDither &dither;
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = sizeof(value_type) * 8;
+ Convert24To16(PcmDither &_dither):dither(_dither) {}
+
+ void Convert(int16_t *out, const int32_t *in, size_t n) {
+ dither.Dither24To16(out, in, in + n);
+ }
};
-template<>
-struct SampleTraits<SampleFormat::S32> {
- typedef int32_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
+struct Convert32To16 {
+ typedef SampleTraits<SampleFormat::S32> SrcTraits;
+ typedef SampleTraits<SampleFormat::S16> DstTraits;
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = sizeof(value_type) * 8;
-};
+ PcmDither &dither;
-template<>
-struct SampleTraits<SampleFormat::S24_P32> {
- typedef int32_t value_type;
- typedef value_type *pointer_type;
- typedef const value_type *const_pointer_type;
+ Convert32To16(PcmDither &_dither):dither(_dither) {}
- static constexpr size_t SAMPLE_SIZE = sizeof(value_type);
- static constexpr unsigned BITS = 24;
+ void Convert(int16_t *out, const int32_t *in, size_t n) {
+ dither.Dither32To16(out, in, in + n);
+ }
};
-static void
-pcm_convert_8_to_16(int16_t *out, const int8_t *in, const int8_t *in_end)
-{
- while (in < in_end) {
- *out++ = *in++ << 8;
- }
-}
+template<SampleFormat F, class Traits=SampleTraits<F>>
+struct PortableFloatToInteger
+ : PerSampleConvert<FloatToIntegerSampleConvert<F, Traits>> {};
-static void
-pcm_convert_24_to_16(PcmDither &dither,
- int16_t *out, const int32_t *in, const int32_t *in_end)
-{
- dither.Dither24To16(out, in, in_end);
-}
+template<SampleFormat F, class Traits=SampleTraits<F>>
+struct FloatToInteger : PortableFloatToInteger<F, Traits> {};
-static void
-pcm_convert_32_to_16(PcmDither &dither,
- int16_t *out, const int32_t *in, const int32_t *in_end)
-{
- dither.Dither32To16(out, in, in_end);
-}
+/**
+ * A template class that attempts to use the "optimized" algorithm for
+ * large portions of the buffer, and calls the "portable" algorithm"
+ * for the rest when the last block is not full.
+ */
+template<typename Optimized, typename Portable>
+class GlueOptimizedConvert : Optimized, Portable {
+public:
+ typedef typename Portable::SrcTraits SrcTraits;
+ typedef typename Portable::DstTraits DstTraits;
+
+ void Convert(typename DstTraits::pointer_type out,
+ typename SrcTraits::const_pointer_type in,
+ size_t n) const {
+ Optimized::Convert(out, in, n);
+
+ /* use the "portable" algorithm for the trailing
+ samples */
+ size_t remaining = n % Optimized::BLOCK_SIZE;
+ size_t done = n - remaining;
+ Portable::Convert(out + done, in + done, remaining);
+ }
+};
-template<SampleFormat F, class Traits=SampleTraits<F>>
-static void
-ConvertFromFloat(typename Traits::pointer_type dest,
- const float *src, const float *end)
-{
- constexpr auto bits = Traits::BITS;
+#ifdef __ARM_NEON__
+#include "Neon.hxx"
- const float factor = 1 << (bits - 1);
+template<>
+struct FloatToInteger<SampleFormat::S16, SampleTraits<SampleFormat::S16>>
+ : GlueOptimizedConvert<NeonFloatTo16,
+ PortableFloatToInteger<SampleFormat::S16>> {};
- while (src != end) {
- int sample(*src++ * factor);
- *dest++ = PcmClamp<typename Traits::value_type, int, bits>(sample);
- }
-}
+#endif
-template<SampleFormat F, class Traits=SampleTraits<F>>
-static void
-ConvertFromFloat(typename Traits::pointer_type dest,
- const float *src, size_t size)
+template<class C>
+static ConstBuffer<typename C::DstTraits::value_type>
+AllocateConvert(PcmBuffer &buffer, C convert,
+ ConstBuffer<typename C::SrcTraits::value_type> src)
{
- ConvertFromFloat<F, Traits>(dest, src,
- pcm_end_pointer(src, size));
+ auto dest = buffer.GetT<typename C::DstTraits::value_type>(src.size);
+ convert.Convert(dest, src.data, src.size);
+ return { dest, src.size };
}
template<SampleFormat F, class Traits=SampleTraits<F>>
-static typename Traits::pointer_type
-AllocateFromFloat(PcmBuffer &buffer, const float *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<typename Traits::value_type>
+AllocateFromFloat(PcmBuffer &buffer, ConstBuffer<float> src)
{
- constexpr size_t src_sample_size = sizeof(*src);
- assert(src_size % src_sample_size == 0);
-
- const size_t num_samples = src_size / src_sample_size;
- *dest_size_r = num_samples * sizeof(typename Traits::value_type);
- auto dest = (typename Traits::pointer_type)buffer.Get(*dest_size_r);
- ConvertFromFloat<F, Traits>(dest, src, src_size);
- return dest;
+ return AllocateConvert(buffer, FloatToInteger<F, Traits>(), src);
}
-static int16_t *
-pcm_allocate_8_to_16(PcmBuffer &buffer,
- const int8_t *src, size_t src_size, size_t *dest_size_r)
+static ConstBuffer<int16_t>
+pcm_allocate_8_to_16(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{
- int16_t *dest;
- *dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = (int16_t *)buffer.Get(*dest_size_r);
- pcm_convert_8_to_16(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert8To16(), src);
}
-static int16_t *
+static ConstBuffer<int16_t>
pcm_allocate_24p32_to_16(PcmBuffer &buffer, PcmDither &dither,
- const int32_t *src, size_t src_size,
- size_t *dest_size_r)
+ ConstBuffer<int32_t> src)
{
- int16_t *dest;
- *dest_size_r = src_size / 2;
- assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = (int16_t *)buffer.Get(*dest_size_r);
- pcm_convert_24_to_16(dither, dest, src,
- pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert24To16(dither), src);
}
-static int16_t *
+static ConstBuffer<int16_t>
pcm_allocate_32_to_16(PcmBuffer &buffer, PcmDither &dither,
- const int32_t *src, size_t src_size,
- size_t *dest_size_r)
+ ConstBuffer<int32_t> src)
{
- int16_t *dest;
- *dest_size_r = src_size / 2;
- assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = (int16_t *)buffer.Get(*dest_size_r);
- pcm_convert_32_to_16(dither, dest, src,
- pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert32To16(dither), src);
}
-static int16_t *
-pcm_allocate_float_to_16(PcmBuffer &buffer,
- const float *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<int16_t>
+pcm_allocate_float_to_16(PcmBuffer &buffer, ConstBuffer<float> src)
{
- return AllocateFromFloat<SampleFormat::S16>(buffer, src, src_size,
- dest_size_r);
+ return AllocateFromFloat<SampleFormat::S16>(buffer, src);
}
-const int16_t *
+ConstBuffer<int16_t>
pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r)
+ SampleFormat src_format, ConstBuffer<void> src)
{
- assert(src_size % sample_format_size(src_format) == 0);
-
switch (src_format) {
case SampleFormat::UNDEFINED:
case SampleFormat::DSD:
@@ -191,104 +170,67 @@ pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither,
case SampleFormat::S8:
return pcm_allocate_8_to_16(buffer,
- (const int8_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int8_t>::FromVoid(src));
case SampleFormat::S16:
- *dest_size_r = src_size;
- return (const int16_t *)src;
+ return ConstBuffer<int16_t>::FromVoid(src);
case SampleFormat::S24_P32:
return pcm_allocate_24p32_to_16(buffer, dither,
- (const int32_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int32_t>::FromVoid(src));
case SampleFormat::S32:
return pcm_allocate_32_to_16(buffer, dither,
- (const int32_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int32_t>::FromVoid(src));
case SampleFormat::FLOAT:
return pcm_allocate_float_to_16(buffer,
- (const float *)src, src_size,
- dest_size_r);
+ ConstBuffer<float>::FromVoid(src));
}
return nullptr;
}
-static void
-pcm_convert_8_to_24(int32_t *out, const int8_t *in, const int8_t *in_end)
-{
- while (in < in_end)
- *out++ = *in++ << 16;
-}
+struct Convert8To24
+ : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
+ SampleFormat::S24_P32>> {};
-static void
-pcm_convert_16_to_24(int32_t *out, const int16_t *in, const int16_t *in_end)
-{
- while (in < in_end)
- *out++ = *in++ << 8;
-}
+struct Convert16To24
+ : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S16,
+ SampleFormat::S24_P32>> {};
-static void
-pcm_convert_32_to_24(int32_t *gcc_restrict out,
- const int32_t *gcc_restrict in,
- const int32_t *gcc_restrict in_end)
+static ConstBuffer<int32_t>
+pcm_allocate_8_to_24(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{
- while (in < in_end)
- *out++ = *in++ >> 8;
+ return AllocateConvert(buffer, Convert8To24(), src);
}
-static int32_t *
-pcm_allocate_8_to_24(PcmBuffer &buffer,
- const int8_t *src, size_t src_size, size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_16_to_24(PcmBuffer &buffer, ConstBuffer<int16_t> src)
{
- int32_t *dest;
- *dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = (int32_t *)buffer.Get(*dest_size_r);
- pcm_convert_8_to_24(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert16To24(), src);
}
-static int32_t *
-pcm_allocate_16_to_24(PcmBuffer &buffer,
- const int16_t *src, size_t src_size, size_t *dest_size_r)
-{
- int32_t *dest;
- *dest_size_r = src_size * 2;
- assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = (int32_t *)buffer.Get(*dest_size_r);
- pcm_convert_16_to_24(dest, src, pcm_end_pointer(src, src_size));
- return dest;
-}
+struct Convert32To24
+ : PerSampleConvert<RightShiftSampleConvert<SampleFormat::S32,
+ SampleFormat::S24_P32>> {};
-static int32_t *
-pcm_allocate_32_to_24(PcmBuffer &buffer,
- const int32_t *src, size_t src_size, size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_32_to_24(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{
- *dest_size_r = src_size;
- int32_t *dest = (int32_t *)buffer.Get(*dest_size_r);
- pcm_convert_32_to_24(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert32To24(), src);
}
-static int32_t *
-pcm_allocate_float_to_24(PcmBuffer &buffer,
- const float *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_float_to_24(PcmBuffer &buffer, ConstBuffer<float> src)
{
- return AllocateFromFloat<SampleFormat::S24_P32>(buffer, src, src_size,
- dest_size_r);
+ return AllocateFromFloat<SampleFormat::S24_P32>(buffer, src);
}
-const int32_t *
+ConstBuffer<int32_t>
pcm_convert_to_24(PcmBuffer &buffer,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r)
+ SampleFormat src_format, ConstBuffer<void> src)
{
- assert(src_size % sample_format_size(src_format) == 0);
-
switch (src_format) {
case SampleFormat::UNDEFINED:
case SampleFormat::DSD:
@@ -296,110 +238,67 @@ pcm_convert_to_24(PcmBuffer &buffer,
case SampleFormat::S8:
return pcm_allocate_8_to_24(buffer,
- (const int8_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int8_t>::FromVoid(src));
case SampleFormat::S16:
return pcm_allocate_16_to_24(buffer,
- (const int16_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int16_t>::FromVoid(src));
case SampleFormat::S24_P32:
- *dest_size_r = src_size;
- return (const int32_t *)src;
+ return ConstBuffer<int32_t>::FromVoid(src);
case SampleFormat::S32:
return pcm_allocate_32_to_24(buffer,
- (const int32_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int32_t>::FromVoid(src));
case SampleFormat::FLOAT:
return pcm_allocate_float_to_24(buffer,
- (const float *)src, src_size,
- dest_size_r);
+ ConstBuffer<float>::FromVoid(src));
}
return nullptr;
}
-static void
-pcm_convert_8_to_32(int32_t *out, const int8_t *in, const int8_t *in_end)
-{
- while (in < in_end)
- *out++ = *in++ << 24;
-}
+struct Convert8To32
+ : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S8,
+ SampleFormat::S32>> {};
-static void
-pcm_convert_16_to_32(int32_t *out, const int16_t *in, const int16_t *in_end)
-{
- while (in < in_end)
- *out++ = *in++ << 16;
-}
+struct Convert16To32
+ : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S16,
+ SampleFormat::S32>> {};
-static void
-pcm_convert_24_to_32(int32_t *gcc_restrict out,
- const int32_t *gcc_restrict in,
- const int32_t *gcc_restrict in_end)
-{
- while (in < in_end)
- *out++ = *in++ << 8;
-}
+struct Convert24To32
+ : PerSampleConvert<LeftShiftSampleConvert<SampleFormat::S24_P32,
+ SampleFormat::S32>> {};
-static int32_t *
-pcm_allocate_8_to_32(PcmBuffer &buffer,
- const int8_t *src, size_t src_size, size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_8_to_32(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{
- int32_t *dest;
- *dest_size_r = src_size / sizeof(*src) * sizeof(*dest);
- dest = (int32_t *)buffer.Get(*dest_size_r);
- pcm_convert_8_to_32(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert8To32(), src);
}
-static int32_t *
-pcm_allocate_16_to_32(PcmBuffer &buffer,
- const int16_t *src, size_t src_size, size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_16_to_32(PcmBuffer &buffer, ConstBuffer<int16_t> src)
{
- int32_t *dest;
- *dest_size_r = src_size * 2;
- assert(*dest_size_r == src_size / sizeof(*src) * sizeof(*dest));
- dest = (int32_t *)buffer.Get(*dest_size_r);
- pcm_convert_16_to_32(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert16To32(), src);
}
-static int32_t *
-pcm_allocate_24p32_to_32(PcmBuffer &buffer,
- const int32_t *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_24p32_to_32(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{
- *dest_size_r = src_size;
- int32_t *dest = (int32_t *)buffer.Get(*dest_size_r);
- pcm_convert_24_to_32(dest, src, pcm_end_pointer(src, src_size));
- return dest;
+ return AllocateConvert(buffer, Convert24To32(), src);
}
-static int32_t *
-pcm_allocate_float_to_32(PcmBuffer &buffer,
- const float *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<int32_t>
+pcm_allocate_float_to_32(PcmBuffer &buffer, ConstBuffer<float> src)
{
- /* convert to S24_P32 first */
- int32_t *dest = pcm_allocate_float_to_24(buffer, src, src_size,
- dest_size_r);
-
- /* convert to 32 bit in-place */
- pcm_convert_24_to_32(dest, dest, pcm_end_pointer(dest, *dest_size_r));
- return dest;
+ return AllocateFromFloat<SampleFormat::S32>(buffer, src);
}
-const int32_t *
+ConstBuffer<int32_t>
pcm_convert_to_32(PcmBuffer &buffer,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r)
+ SampleFormat src_format, ConstBuffer<void> src)
{
- assert(src_size % sample_format_size(src_format) == 0);
-
switch (src_format) {
case SampleFormat::UNDEFINED:
case SampleFormat::DSD:
@@ -407,108 +306,66 @@ pcm_convert_to_32(PcmBuffer &buffer,
case SampleFormat::S8:
return pcm_allocate_8_to_32(buffer,
- (const int8_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int8_t>::FromVoid(src));
case SampleFormat::S16:
return pcm_allocate_16_to_32(buffer,
- (const int16_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int16_t>::FromVoid(src));
case SampleFormat::S24_P32:
return pcm_allocate_24p32_to_32(buffer,
- (const int32_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int32_t>::FromVoid(src));
case SampleFormat::S32:
- *dest_size_r = src_size;
- return (const int32_t *)src;
+ return ConstBuffer<int32_t>::FromVoid(src);
case SampleFormat::FLOAT:
return pcm_allocate_float_to_32(buffer,
- (const float *)src, src_size,
- dest_size_r);
+ ConstBuffer<float>::FromVoid(src));
}
return nullptr;
}
-template<SampleFormat F, class Traits=SampleTraits<F>>
-static void
-ConvertToFloat(float *dest,
- typename Traits::const_pointer_type src,
- typename Traits::const_pointer_type end)
-{
- constexpr float factor = 0.5 / (1 << (Traits::BITS - 2));
- while (src != end)
- *dest++ = float(*src++) * factor;
+struct Convert8ToFloat
+ : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S8>> {};
-}
+struct Convert16ToFloat
+ : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S16>> {};
-template<SampleFormat F, class Traits=SampleTraits<F>>
-static void
-ConvertToFloat(float *dest,
- typename Traits::const_pointer_type src, size_t size)
-{
- ConvertToFloat<F, Traits>(dest, src, pcm_end_pointer(src, size));
-}
+struct Convert24ToFloat
+ : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S24_P32>> {};
-template<SampleFormat F, class Traits=SampleTraits<F>>
-static float *
-AllocateToFloat(PcmBuffer &buffer,
- typename Traits::const_pointer_type src, size_t src_size,
- size_t *dest_size_r)
-{
- constexpr size_t src_sample_size = Traits::SAMPLE_SIZE;
- assert(src_size % src_sample_size == 0);
-
- const size_t num_samples = src_size / src_sample_size;
- *dest_size_r = num_samples * sizeof(float);
- float *dest = (float *)buffer.Get(*dest_size_r);
- ConvertToFloat<F, Traits>(dest, src, src_size);
- return dest;
-}
+struct Convert32ToFloat
+ : PerSampleConvert<IntegerToFloatSampleConvert<SampleFormat::S32>> {};
-static float *
-pcm_allocate_8_to_float(PcmBuffer &buffer,
- const int8_t *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<float>
+pcm_allocate_8_to_float(PcmBuffer &buffer, ConstBuffer<int8_t> src)
{
- return AllocateToFloat<SampleFormat::S8>(buffer, src, src_size,
- dest_size_r);
+ return AllocateConvert(buffer, Convert8ToFloat(), src);
}
-static float *
-pcm_allocate_16_to_float(PcmBuffer &buffer,
- const int16_t *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<float>
+pcm_allocate_16_to_float(PcmBuffer &buffer, ConstBuffer<int16_t> src)
{
- return AllocateToFloat<SampleFormat::S16>(buffer, src, src_size,
- dest_size_r);
+ return AllocateConvert(buffer, Convert16ToFloat(), src);
}
-static float *
-pcm_allocate_24p32_to_float(PcmBuffer &buffer,
- const int32_t *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<float>
+pcm_allocate_24p32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{
- return AllocateToFloat<SampleFormat::S24_P32>(buffer, src, src_size,
- dest_size_r);
+ return AllocateConvert(buffer, Convert24ToFloat(), src);
}
-static float *
-pcm_allocate_32_to_float(PcmBuffer &buffer,
- const int32_t *src, size_t src_size,
- size_t *dest_size_r)
+static ConstBuffer<float>
+pcm_allocate_32_to_float(PcmBuffer &buffer, ConstBuffer<int32_t> src)
{
- return AllocateToFloat<SampleFormat::S32>(buffer, src, src_size,
- dest_size_r);
+ return AllocateConvert(buffer, Convert32ToFloat(), src);
}
-const float *
+ConstBuffer<float>
pcm_convert_to_float(PcmBuffer &buffer,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r)
+ SampleFormat src_format, ConstBuffer<void> src)
{
switch (src_format) {
case SampleFormat::UNDEFINED:
@@ -517,27 +374,22 @@ pcm_convert_to_float(PcmBuffer &buffer,
case SampleFormat::S8:
return pcm_allocate_8_to_float(buffer,
- (const int8_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int8_t>::FromVoid(src));
case SampleFormat::S16:
return pcm_allocate_16_to_float(buffer,
- (const int16_t *)src, src_size,
- dest_size_r);
-
- case SampleFormat::S24_P32:
- return pcm_allocate_24p32_to_float(buffer,
- (const int32_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int16_t>::FromVoid(src));
case SampleFormat::S32:
return pcm_allocate_32_to_float(buffer,
- (const int32_t *)src, src_size,
- dest_size_r);
+ ConstBuffer<int32_t>::FromVoid(src));
+
+ case SampleFormat::S24_P32:
+ return pcm_allocate_24p32_to_float(buffer,
+ ConstBuffer<int32_t>::FromVoid(src));
case SampleFormat::FLOAT:
- *dest_size_r = src_size;
- return (const float *)src;
+ return ConstBuffer<float>::FromVoid(src);
}
return nullptr;
diff --git a/src/pcm/PcmFormat.hxx b/src/pcm/PcmFormat.hxx
index cc44d6dd5..da182e771 100644
--- a/src/pcm/PcmFormat.hxx
+++ b/src/pcm/PcmFormat.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <stddef.h>
+template<typename T> struct ConstBuffer;
class PcmBuffer;
class PcmDither;
@@ -36,14 +37,12 @@ class PcmDither;
* @param dither a pcm_dither object for 24-to-16 conversion
* @param bits the number of in the source buffer
* @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const int16_t *
+gcc_pure
+ConstBuffer<int16_t>
pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r);
+ SampleFormat src_format, ConstBuffer<void> src);
/**
* Converts PCM samples to 24 bit (32 bit alignment).
@@ -51,14 +50,12 @@ pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither,
* @param buffer a PcmBuffer object
* @param bits the number of in the source buffer
* @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const int32_t *
+gcc_pure
+ConstBuffer<int32_t>
pcm_convert_to_24(PcmBuffer &buffer,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r);
+ SampleFormat src_format, ConstBuffer<void> src);
/**
* Converts PCM samples to 32 bit.
@@ -66,14 +63,12 @@ pcm_convert_to_24(PcmBuffer &buffer,
* @param buffer a PcmBuffer object
* @param bits the number of in the source buffer
* @param src the source PCM buffer
- * @param src_size the size of #src in bytes
- * @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const int32_t *
+gcc_pure
+ConstBuffer<int32_t>
pcm_convert_to_32(PcmBuffer &buffer,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r);
+ SampleFormat src_format, ConstBuffer<void> src);
/**
* Converts PCM samples to 32 bit floating point.
@@ -85,9 +80,9 @@ pcm_convert_to_32(PcmBuffer &buffer,
* @param dest_size_r returns the number of bytes of the destination buffer
* @return the destination buffer
*/
-const float *
+gcc_pure
+ConstBuffer<float>
pcm_convert_to_float(PcmBuffer &buffer,
- SampleFormat src_format, const void *src,
- size_t src_size, size_t *dest_size_r);
+ SampleFormat src_format, ConstBuffer<void> src);
#endif
diff --git a/src/pcm/PcmMix.cxx b/src/pcm/PcmMix.cxx
index 001794061..d21b5f04b 100644
--- a/src/pcm/PcmMix.cxx
+++ b/src/pcm/PcmMix.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -19,42 +19,56 @@
#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 "PcmDither.cxx" // including the .cxx file to get inlined templates
+
+#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(PcmDither &dither,
+ typename Traits::value_type _a, typename Traits::value_type _b,
+ int volume1, int volume2)
{
- U a(_a), b(_b);
-
- U c = ((a * volume1 + b * volume2) +
- pcm_volume_dither() + PCM_VOLUME_1 / 2)
- / PCM_VOLUME_1;
+ typename Traits::long_type a(_a), b(_b);
+ typename Traits::long_type c(a * volume1 + b * volume2);
- return PcmClamp<T, U, bits>(c);
+ return dither.DitherShift<typename Traits::long_type,
+ Traits::BITS + PCM_VOLUME_BITS,
+ Traits::BITS>(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(PcmDither &dither,
+ 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>(dither, 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)
+PcmAddVolumeVoid(PcmDither &dither,
+ 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>(dither,
+ typename Traits::pointer_type(a),
+ typename Traits::const_pointer_type(b),
+ size / sample_size,
+ volume1, volume2);
}
static void
@@ -72,7 +86,7 @@ pcm_add_vol_float(float *buffer1, const float *buffer2,
}
static bool
-pcm_add_vol(void *buffer1, const void *buffer2, size_t size,
+pcm_add_vol(PcmDither &dither, void *buffer1, const void *buffer2, size_t size,
int vol1, int vol2,
SampleFormat format)
{
@@ -83,23 +97,27 @@ 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>(dither,
+ buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::S16:
- PcmAddVolumeVoid<int16_t, int32_t, 16>(buffer1, buffer2, size,
- vol1, vol2);
+ PcmAddVolumeVoid<SampleFormat::S16>(dither,
+ 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>(dither,
+ buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::S32:
- PcmAddVolumeVoid<int32_t, int64_t, 32>(buffer1, buffer2, size,
- vol1, vol2);
+ PcmAddVolumeVoid<SampleFormat::S32>(dither,
+ buffer1, buffer2, size,
+ vol1, vol2);
return true;
case SampleFormat::FLOAT:
@@ -114,30 +132,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 +185,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:
@@ -188,10 +211,9 @@ pcm_add(void *buffer1, const void *buffer2, size_t size,
}
bool
-pcm_mix(void *buffer1, const void *buffer2, size_t size,
+pcm_mix(PcmDither &dither, 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 +224,9 @@ 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(dither, buffer1, buffer2, size,
+ vol1, PCM_VOLUME_1S - vol1, format);
}
diff --git a/src/pcm/PcmMix.hxx b/src/pcm/PcmMix.hxx
index 637c88f8a..4e22a33f1 100644
--- a/src/pcm/PcmMix.hxx
+++ b/src/pcm/PcmMix.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,8 @@
#include <stddef.h>
+class PcmDither;
+
/*
* Linearly mixes two PCM buffers. Both must have the same length and
* the same audio format. The formula is:
@@ -44,7 +46,7 @@
*/
gcc_warn_unused_result
bool
-pcm_mix(void *buffer1, const void *buffer2, size_t size,
+pcm_mix(PcmDither &dither, void *buffer1, const void *buffer2, size_t size,
SampleFormat format, float portion1);
#endif
diff --git a/src/pcm/PcmPack.cxx b/src/pcm/PcmPack.cxx
index 8920eb288..7a3379ad0 100644
--- a/src/pcm/PcmPack.cxx
+++ b/src/pcm/PcmPack.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm/PcmPack.hxx b/src/pcm/PcmPack.hxx
index aed011767..271a3cd25 100644
--- a/src/pcm/PcmPack.hxx
+++ b/src/pcm/PcmPack.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm/PcmPrng.hxx b/src/pcm/PcmPrng.hxx
index 0c823250d..5233caba6 100644
--- a/src/pcm/PcmPrng.hxx
+++ b/src/pcm/PcmPrng.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..23870a729 100644
--- a/src/pcm/PcmUtils.hxx
+++ b/src/pcm/PcmUtils.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -26,53 +26,31 @@
#include <stdint.h>
-/**
- * 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"
- * pointer for use in loops.
- */
-template<typename T>
-static inline const T *
-pcm_end_pointer(const T *p, size_t size)
-{
- return (const T *)((const uint8_t *)p + size);
-}
+enum class SampleFormat : uint8_t;
+template<SampleFormat F> struct SampleTraits;
/**
* 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/PcmVolume.hxx b/src/pcm/PcmVolume.hxx
deleted file mode 100644
index 8cd82acf7..000000000
--- a/src/pcm/PcmVolume.hxx
+++ /dev/null
@@ -1,81 +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_VOLUME_HXX
-#define MPD_PCM_VOLUME_HXX
-
-#include "PcmPrng.hxx"
-#include "AudioFormat.hxx"
-
-#include <stdint.h>
-#include <stddef.h>
-
-enum {
- /** this value means "100% volume" */
- PCM_VOLUME_1 = 1024,
-};
-
-struct AudioFormat;
-
-/**
- * Converts a float value (0.0 = silence, 1.0 = 100% volume) to an
- * integer volume value (1000 = 100%).
- */
-static inline int
-pcm_float_to_volume(float volume)
-{
- return volume * PCM_VOLUME_1 + 0.5;
-}
-
-static inline float
-pcm_volume_to_float(int volume)
-{
- return (float)volume / (float)PCM_VOLUME_1;
-}
-
-/**
- * Returns the next volume dithering number, between -511 and +511.
- * This number is taken from a global PRNG, see pcm_prng().
- */
-static inline int
-pcm_volume_dither(void)
-{
- static unsigned long state;
- uint32_t r;
-
- r = state = pcm_prng(state);
-
- return (r & 511) - ((r >> 9) & 511);
-}
-
-/**
- * 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
- */
-bool
-pcm_volume(void *buffer, size_t length,
- SampleFormat format,
- int volume);
-
-#endif
diff --git a/src/pcm/Resampler.hxx b/src/pcm/Resampler.hxx
new file mode 100644
index 000000000..9b6ccbbc7
--- /dev/null
+++ b/src/pcm/Resampler.hxx
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2003-2014 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/ShiftConvert.hxx b/src/pcm/ShiftConvert.hxx
new file mode 100644
index 000000000..92f96b7ba
--- /dev/null
+++ b/src/pcm/ShiftConvert.hxx
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2003-2014 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_SHIFT_CONVERT_HXX
+#define MPD_PCM_SHIFT_CONVERT_HXX
+
+#include "Traits.hxx"
+
+/**
+ * Convert from one integer sample format to another by shifting bits
+ * to the left.
+ */
+template<SampleFormat SF, SampleFormat DF,
+ class ST=SampleTraits<SF>,
+ class DT=SampleTraits<DF>>
+struct LeftShiftSampleConvert {
+ typedef ST SrcTraits;
+ typedef DT DstTraits;
+
+ typedef typename SrcTraits::value_type SV;
+ typedef typename DstTraits::value_type DV;
+
+ static_assert(SrcTraits::BITS < DstTraits::BITS,
+ "Source format must be smaller than destination format");
+
+ constexpr static DV Convert(SV src) {
+ return DV(src) << (DstTraits::BITS - SrcTraits::BITS);
+ }
+};
+
+/**
+ * Convert from one integer sample format to another by shifting bits
+ * to the right.
+ */
+template<SampleFormat SF, SampleFormat DF,
+ class ST=SampleTraits<SF>,
+ class DT=SampleTraits<DF>>
+struct RightShiftSampleConvert {
+ typedef ST SrcTraits;
+ typedef DT DstTraits;
+
+ typedef typename SrcTraits::value_type SV;
+ typedef typename DstTraits::value_type DV;
+
+ static_assert(SrcTraits::BITS > DstTraits::BITS,
+ "Source format must be smaller than destination format");
+
+ constexpr static DV Convert(SV src) {
+ return src >> (SrcTraits::BITS - DstTraits::BITS);
+ }
+};
+
+#endif
diff --git a/src/pcm/SoxrResampler.cxx b/src/pcm/SoxrResampler.cxx
new file mode 100644
index 000000000..2508e816e
--- /dev/null
+++ b/src/pcm/SoxrResampler.cxx
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2003-2014 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");
+
+static unsigned long soxr_quality_recipe = SOXR_HQ;
+
+static const char *
+soxr_quality_name(unsigned long recipe)
+{
+ switch (recipe) {
+ case SOXR_VHQ:
+ return "Very High Quality";
+ case SOXR_HQ:
+ return "High Quality";
+ case SOXR_MQ:
+ return "Medium Quality";
+ case SOXR_LQ:
+ return "Low Quality";
+ case SOXR_QQ:
+ return "Quick";
+ }
+
+ gcc_unreachable();
+}
+
+static bool
+soxr_parse_converter(const char *converter)
+{
+ assert(converter != nullptr);
+
+ assert(memcmp(converter, "soxr", 4) == 0);
+ if (converter[4] == '\0')
+ return true;
+ if (converter[4] != ' ')
+ return false;
+
+ // converter example is "soxr very high", we want the "very high" part
+ const char *quality = converter + 5;
+ if (strcmp(quality, "very high") == 0)
+ soxr_quality_recipe = SOXR_VHQ;
+ else if (strcmp(quality, "high") == 0)
+ soxr_quality_recipe = SOXR_HQ;
+ else if (strcmp(quality, "medium") == 0)
+ soxr_quality_recipe = SOXR_MQ;
+ else if (strcmp(quality, "low") == 0)
+ soxr_quality_recipe = SOXR_LQ;
+ else if (strcmp(quality, "quick") == 0)
+ soxr_quality_recipe = SOXR_QQ;
+ else
+ return false;
+
+ return true;
+}
+
+bool
+pcm_resample_soxr_global_init(const char *converter, Error &error)
+{
+ if (!soxr_parse_converter(converter)) {
+ error.Format(soxr_domain,
+ "unknown samplerate converter '%s'", converter);
+ return false;
+ }
+
+ FormatDebug(soxr_domain,
+ "soxr converter '%s'",
+ soxr_quality_name(soxr_quality_recipe));
+
+ return true;
+}
+
+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_quality_spec_t quality = soxr_quality_spec(soxr_quality_recipe, 0);
+ soxr = soxr_create(af.sample_rate, new_sample_rate,
+ af.channels, &e,
+ nullptr, &quality, 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..efe9bd5cb
--- /dev/null
+++ b/src/pcm/SoxrResampler.hxx
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2003-2014 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;
+};
+
+bool
+pcm_resample_soxr_global_init(const char *converter, Error &error);
+
+#endif
diff --git a/src/pcm/Traits.hxx b/src/pcm/Traits.hxx
new file mode 100644
index 000000000..97259ac73
--- /dev/null
+++ b/src/pcm/Traits.hxx
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2003-2014 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_least32_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..b12d8fd41
--- /dev/null
+++ b/src/pcm/Volume.cxx
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2003-2014 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 "PcmDither.cxx" // including the .cxx file to get inlined templates
+
+#include <stdint.h>
+#include <string.h>
+
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static inline typename Traits::value_type
+pcm_volume_sample(PcmDither &dither,
+ typename Traits::value_type _sample,
+ int volume)
+{
+ typename Traits::long_type sample(_sample);
+
+ return dither.DitherShift<typename Traits::long_type,
+ Traits::BITS + PCM_VOLUME_BITS,
+ Traits::BITS>(sample * volume);
+}
+
+template<SampleFormat F, class Traits=SampleTraits<F>>
+static void
+pcm_volume_change(PcmDither &dither,
+ typename Traits::pointer_type dest,
+ typename Traits::const_pointer_type src,
+ size_t n,
+ int volume)
+{
+ for (size_t i = 0; i != n; ++i)
+ dest[i] = pcm_volume_sample<F, Traits>(dither, src[i], volume);
+}
+
+static void
+pcm_volume_change_8(PcmDither &dither,
+ int8_t *dest, const int8_t *src, size_t n,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S8>(dither, dest, src, n, volume);
+}
+
+static void
+pcm_volume_change_16(PcmDither &dither,
+ int16_t *dest, const int16_t *src, size_t n,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S16>(dither, dest, src, n, volume);
+}
+
+static void
+pcm_volume_change_24(PcmDither &dither,
+ int32_t *dest, const int32_t *src, size_t n,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S24_P32>(dither, dest, src, n,
+ volume);
+}
+
+static void
+pcm_volume_change_32(PcmDither &dither,
+ int32_t *dest, const int32_t *src, size_t n,
+ int volume)
+{
+ pcm_volume_change<SampleFormat::S32>(dither, dest, src, n, volume);
+}
+
+static void
+pcm_volume_change_float(float *dest, const float *src, size_t n,
+ float volume)
+{
+ for (size_t i = 0; i != n; ++i)
+ dest[i] = src[i] * volume;
+}
+
+bool
+PcmVolume::Open(SampleFormat _format, Error &error)
+{
+ assert(format == SampleFormat::UNDEFINED);
+
+ switch (_format) {
+ case SampleFormat::UNDEFINED:
+ 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;
+
+ case SampleFormat::DSD:
+ // TODO: implement this; currently, it's a no-op
+ 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 };
+ }
+
+ switch (format) {
+ case SampleFormat::UNDEFINED:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S8:
+ pcm_volume_change_8(dither, (int8_t *)data,
+ (const int8_t *)src.data,
+ src.size / sizeof(int8_t),
+ volume);
+ break;
+
+ case SampleFormat::S16:
+ pcm_volume_change_16(dither, (int16_t *)data,
+ (const int16_t *)src.data,
+ src.size / sizeof(int16_t),
+ volume);
+ break;
+
+ case SampleFormat::S24_P32:
+ pcm_volume_change_24(dither, (int32_t *)data,
+ (const int32_t *)src.data,
+ src.size / sizeof(int32_t),
+ volume);
+ break;
+
+ case SampleFormat::S32:
+ pcm_volume_change_32(dither, (int32_t *)data,
+ (const int32_t *)src.data,
+ src.size / sizeof(int32_t),
+ volume);
+ break;
+
+ case SampleFormat::FLOAT:
+ pcm_volume_change_float((float *)data,
+ (const float *)src.data,
+ src.size / sizeof(float),
+ pcm_volume_to_float(volume));
+ break;
+
+ case SampleFormat::DSD:
+ // TODO: implement this; currently, it's a no-op
+ return src;
+ }
+
+ return { data, src.size };
+}
diff --git a/src/pcm/Volume.hxx b/src/pcm/Volume.hxx
new file mode 100644
index 000000000..a156fc72e
--- /dev/null
+++ b/src/pcm/Volume.hxx
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2003-2014 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_VOLUME_HXX
+#define MPD_PCM_VOLUME_HXX
+
+#include "AudioFormat.hxx"
+#include "PcmBuffer.hxx"
+#include "PcmDither.hxx"
+
+#include <stdint.h>
+#include <stddef.h>
+
+#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;
+
+/**
+ * Converts a float value (0.0 = silence, 1.0 = 100% volume) to an
+ * integer volume value (1000 = 100%).
+ */
+static inline int
+pcm_float_to_volume(float volume)
+{
+ return volume * PCM_VOLUME_1 + 0.5;
+}
+
+static inline float
+pcm_volume_to_float(int volume)
+{
+ return (float)volume / (float)PCM_VOLUME_1;
+}
+
+/**
+ * A class that converts samples from one format to another.
+ */
+class PcmVolume {
+ SampleFormat format;
+
+ unsigned volume;
+
+ PcmBuffer buffer;
+ PcmDither dither;
+
+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