aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Makefile.am1
-rw-r--r--src/pcm/ChannelsConverter.cxx115
-rw-r--r--src/pcm/ChannelsConverter.hxx83
-rw-r--r--src/pcm/PcmConvert.cxx85
-rw-r--r--src/pcm/PcmConvert.hxx5
5 files changed, 223 insertions, 66 deletions
diff --git a/Makefile.am b/Makefile.am
index b74d5fbb7..2d50d0e6b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -338,6 +338,7 @@ libpcm_a_SOURCES = \
src/pcm/PcmPack.cxx src/pcm/PcmPack.hxx \
src/pcm/PcmFormat.cxx src/pcm/PcmFormat.hxx \
src/pcm/FormatConverter.cxx src/pcm/FormatConverter.hxx \
+ src/pcm/ChannelsConverter.cxx src/pcm/ChannelsConverter.hxx \
src/pcm/PcmResample.cxx src/pcm/PcmResample.hxx \
src/pcm/PcmResampleFallback.cxx \
src/pcm/PcmResampleInternal.hxx \
diff --git a/src/pcm/ChannelsConverter.cxx b/src/pcm/ChannelsConverter.cxx
new file mode 100644
index 000000000..5895fb15d
--- /dev/null
+++ b/src/pcm/ChannelsConverter.cxx
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "ChannelsConverter.hxx"
+#include "PcmChannels.hxx"
+#include "PcmConvert.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_convert_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, Error &error)
+{
+ const void *result = nullptr;
+ size_t result_size = 0;
+
+ switch (format) {
+ case SampleFormat::UNDEFINED:
+ case SampleFormat::S8:
+ case SampleFormat::DSD:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S16:
+ result = pcm_convert_channels_16(buffer, dest_channels,
+ src_channels,
+ (const int16_t *)src.data,
+ src.size, &result_size);
+ break;
+
+ case SampleFormat::S24_P32:
+ result = pcm_convert_channels_24(buffer, dest_channels,
+ src_channels,
+ (const int32_t *)src.data,
+ src.size, &result_size);
+ break;
+
+ case SampleFormat::S32:
+ result = pcm_convert_channels_32(buffer, dest_channels,
+ src_channels,
+ (const int32_t *)src.data,
+ src.size, &result_size);
+ break;
+
+ case SampleFormat::FLOAT:
+ result = pcm_convert_channels_float(buffer, dest_channels,
+ src_channels,
+ (const float *)src.data,
+ src.size, &result_size);
+ break;
+ }
+
+ if (result == nullptr) {
+ error.Format(pcm_convert_domain,
+ "Conversion from %u to %u channels "
+ "is not implemented",
+ src_channels, dest_channels);
+ return nullptr;
+ }
+
+ return { result, result_size };
+}
diff --git a/src/pcm/ChannelsConverter.hxx b/src/pcm/ChannelsConverter.hxx
new file mode 100644
index 000000000..4311b9671
--- /dev/null
+++ b/src/pcm/ChannelsConverter.hxx
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_CHANNELS_CONVERTER_HXX
+#define MPD_PCM_CHANNELS_CONVERTER_HXX
+
+#include "check.h"
+#include "AudioFormat.hxx"
+#include "PcmBuffer.hxx"
+
+#ifndef NDEBUG
+#include <assert.h>
+#endif
+
+class Error;
+template<typename T> struct ConstBuffer;
+
+/**
+ * A class that converts samples from one format to another.
+ */
+class PcmChannelsConverter {
+ SampleFormat format;
+ unsigned src_channels, dest_channels;
+
+ PcmBuffer buffer;
+
+public:
+#ifndef NDEBUG
+ PcmChannelsConverter()
+ :format(SampleFormat::UNDEFINED) {}
+
+ ~PcmChannelsConverter() {
+ assert(format == SampleFormat::UNDEFINED);
+ }
+#endif
+
+ /**
+ * Opens the object, prepare for Convert().
+ *
+ * @param format the sample format
+ * @param src_channels the number of source channels
+ * @param dest_channels the number of destination channels
+ * @param error location to store the error
+ * @return true on success
+ */
+ bool Open(SampleFormat format,
+ unsigned src_channels, unsigned dest_channels,
+ Error &error);
+
+ /**
+ * Closes the object. After that, you may call Open() again.
+ */
+ void Close();
+
+ /**
+ * Convert a block of PCM data.
+ *
+ * @param src the input buffer
+ * @param error location to store the error
+ * @return the destination buffer on success,
+ * ConstBuffer::Null() on error
+ */
+ gcc_pure
+ ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error);
+};
+
+#endif
diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx
index eb9c0e052..bc289460d 100644
--- a/src/pcm/PcmConvert.cxx
+++ b/src/pcm/PcmConvert.cxx
@@ -19,7 +19,6 @@
#include "config.h"
#include "PcmConvert.hxx"
-#include "PcmChannels.hxx"
#include "AudioFormat.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
@@ -72,12 +71,22 @@ PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format,
return false;
format.format = dest_format.format;
+ if (format.channels != dest_format.channels &&
+ !channels_converter.Open(format.format, format.channels,
+ dest_format.channels, error)) {
+ format_converter.Close();
+ return false;
+ }
+
return true;
}
void
PcmConvert::Close()
{
+ if (src_format.channels != dest_format.channels)
+ channels_converter.Close();
+
if (src_format.format != dest_format.format)
format_converter.Close();
@@ -96,25 +105,11 @@ PcmConvert::Convert16(ConstBuffer<int16_t> src, AudioFormat format,
{
assert(format.format == SampleFormat::S16);
assert(dest_format.format == SampleFormat::S16);
+ assert(format.channels == dest_format.channels);
auto buf = src.data;
size_t len = src.size * sizeof(*src.data);
- if (format.channels != dest_format.channels) {
- buf = pcm_convert_channels_16(channels_buffer,
- dest_format.channels,
- format.channels,
- buf, len, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- format.channels,
- dest_format.channels);
- return nullptr;
- }
- }
-
if (format.sample_rate != dest_format.sample_rate) {
buf = resampler.Resample16(dest_format.channels,
format.sample_rate, buf, len,
@@ -133,25 +128,11 @@ PcmConvert::Convert24(ConstBuffer<int32_t> src, AudioFormat format,
{
assert(format.format == SampleFormat::S24_P32);
assert(dest_format.format == SampleFormat::S24_P32);
+ assert(format.channels == dest_format.channels);
auto buf = src.data;
size_t len = src.size * sizeof(*src.data);
- if (format.channels != dest_format.channels) {
- buf = pcm_convert_channels_24(channels_buffer,
- dest_format.channels,
- format.channels,
- buf, len, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- format.channels,
- dest_format.channels);
- return nullptr;
- }
- }
-
if (format.sample_rate != dest_format.sample_rate) {
buf = resampler.Resample24(dest_format.channels,
format.sample_rate, buf, len,
@@ -170,25 +151,11 @@ PcmConvert::Convert32(ConstBuffer<int32_t> src, AudioFormat format,
{
assert(format.format == SampleFormat::S32);
assert(dest_format.format == SampleFormat::S32);
+ assert(format.channels == dest_format.channels);
auto buf = src.data;
size_t len = src.size * sizeof(*src.data);
- if (format.channels != dest_format.channels) {
- buf = pcm_convert_channels_32(channels_buffer,
- dest_format.channels,
- format.channels,
- buf, len, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- format.channels,
- dest_format.channels);
- return nullptr;
- }
- }
-
if (format.sample_rate != dest_format.sample_rate) {
buf = resampler.Resample32(dest_format.channels,
format.sample_rate, buf, len,
@@ -207,27 +174,11 @@ PcmConvert::ConvertFloat(ConstBuffer<float> src, AudioFormat format,
{
assert(format.format == SampleFormat::FLOAT);
assert(dest_format.format == SampleFormat::FLOAT);
+ assert(format.channels == dest_format.channels);
auto buffer = src.data;
size_t size = src.size * sizeof(*src.data);
- /* convert channels */
-
- if (format.channels != dest_format.channels) {
- buffer = pcm_convert_channels_float(channels_buffer,
- dest_format.channels,
- format.channels,
- buffer, size, &size);
- if (buffer == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %u to %u channels "
- "is not implemented",
- format.channels,
- dest_format.channels);
- return nullptr;
- }
- }
-
/* resample with float, because this is the best format for
libsamplerate */
@@ -274,6 +225,14 @@ PcmConvert::Convert(const void *src, size_t src_size,
format.format = dest_format.format;
}
+ if (format.channels != dest_format.channels) {
+ buffer = channels_converter.Convert(buffer, error);
+ if (buffer.IsNull())
+ return nullptr;
+
+ format.channels = dest_format.channels;
+ }
+
switch (dest_format.format) {
case SampleFormat::S16:
buffer = Convert16(ConstBuffer<int16_t>::FromVoid(buffer),
diff --git a/src/pcm/PcmConvert.hxx b/src/pcm/PcmConvert.hxx
index d78d72214..d6e113915 100644
--- a/src/pcm/PcmConvert.hxx
+++ b/src/pcm/PcmConvert.hxx
@@ -24,6 +24,7 @@
#include "PcmResample.hxx"
#include "PcmBuffer.hxx"
#include "FormatConverter.hxx"
+#include "ChannelsConverter.hxx"
#include "AudioFormat.hxx"
#include <stddef.h>
@@ -43,9 +44,7 @@ class PcmConvert {
PcmResampler resampler;
PcmFormatConverter format_converter;
-
- /** the buffer for converting the channel count */
- PcmBuffer channels_buffer;
+ PcmChannelsConverter channels_converter;
AudioFormat src_format, dest_format;