aboutsummaryrefslogtreecommitdiffstats
path: root/src/pcm
diff options
context:
space:
mode:
Diffstat (limited to 'src/pcm')
-rw-r--r--src/pcm/FormatConverter.cxx104
-rw-r--r--src/pcm/FormatConverter.hxx84
-rw-r--r--src/pcm/PcmConvert.cxx108
-rw-r--r--src/pcm/PcmConvert.hxx15
4 files changed, 243 insertions, 68 deletions
diff --git a/src/pcm/FormatConverter.cxx b/src/pcm/FormatConverter.cxx
new file mode 100644
index 000000000..f3f9c8685
--- /dev/null
+++ b/src/pcm/FormatConverter.cxx
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FormatConverter.hxx"
+#include "PcmFormat.hxx"
+#include "PcmConvert.hxx"
+#include "util/ConstBuffer.hxx"
+#include "util/Error.hxx"
+
+#include <assert.h>
+
+bool
+PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format,
+ gcc_unused Error &error)
+{
+ assert(_src_format != SampleFormat::UNDEFINED);
+ assert(_dest_format != SampleFormat::UNDEFINED);
+
+ src_format = _src_format;
+ dest_format = _dest_format;
+ return true;
+}
+
+void
+PcmFormatConverter::Close()
+{
+#ifndef NDEBUG
+ src_format = SampleFormat::UNDEFINED;
+ dest_format = SampleFormat::UNDEFINED;
+#endif
+}
+
+ConstBuffer<void>
+PcmFormatConverter::Convert(ConstBuffer<void> src, Error &error)
+{
+ const void *result = nullptr;
+ size_t result_size = 0;
+
+ switch (dest_format) {
+ case SampleFormat::UNDEFINED:
+ assert(false);
+ gcc_unreachable();
+
+ case SampleFormat::S8:
+ case SampleFormat::DSD:
+ result = nullptr;
+ break;
+
+ case SampleFormat::S16:
+ result = pcm_convert_to_16(buffer, dither,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+
+ case SampleFormat::S24_P32:
+ result = pcm_convert_to_24(buffer,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+
+ case SampleFormat::S32:
+ result = pcm_convert_to_32(buffer,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+
+ case SampleFormat::FLOAT:
+ result = pcm_convert_to_float(buffer,
+ src_format,
+ src.data, src.size,
+ &result_size);
+ break;
+ }
+
+ if (result == nullptr) {
+ error.Format(pcm_convert_domain,
+ "PCM conversion from %s to %s is not implemented",
+ sample_format_to_string(src_format),
+ sample_format_to_string(dest_format));
+ return nullptr;
+ }
+
+ return { result, result_size };
+}
diff --git a/src/pcm/FormatConverter.hxx b/src/pcm/FormatConverter.hxx
new file mode 100644
index 000000000..f5b13a0b0
--- /dev/null
+++ b/src/pcm/FormatConverter.hxx
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_PCM_FORMAT_CONVERTER_HXX
+#define MPD_PCM_FORMAT_CONVERTER_HXX
+
+#include "check.h"
+#include "AudioFormat.hxx"
+#include "PcmBuffer.hxx"
+#include "PcmDither.hxx"
+
+#ifndef NDEBUG
+#include <assert.h>
+#endif
+
+class Error;
+template<typename T> struct ConstBuffer;
+
+/**
+ * A class that converts samples from one format to another.
+ */
+class PcmFormatConverter {
+ SampleFormat src_format, dest_format;
+
+ PcmBuffer buffer;
+ PcmDither dither;
+
+public:
+#ifndef NDEBUG
+ PcmFormatConverter()
+ :src_format(SampleFormat::UNDEFINED),
+ dest_format(SampleFormat::UNDEFINED) {}
+
+ ~PcmFormatConverter() {
+ assert(src_format == SampleFormat::UNDEFINED);
+ assert(dest_format == SampleFormat::UNDEFINED);
+ }
+#endif
+
+ /**
+ * Opens the object, prepare for Convert().
+ *
+ * @param src_format the sample format of incoming data
+ * @param dest_format the sample format of outgoing data
+ * @param error location to store the error
+ * @return true on success
+ */
+ bool Open(SampleFormat src_format, SampleFormat dest_format,
+ Error &error);
+
+ /**
+ * Closes the object. After that, you may call Open() again.
+ */
+ void Close();
+
+ /**
+ * Convert a block of PCM data.
+ *
+ * @param src the input buffer
+ * @param error location to store the error
+ * @return the destination buffer on success,
+ * ConstBuffer::Null() on error
+ */
+ gcc_pure
+ ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error);
+};
+
+#endif
diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx
index e32a4ba4e..eb9c0e052 100644
--- a/src/pcm/PcmConvert.cxx
+++ b/src/pcm/PcmConvert.cxx
@@ -20,11 +20,11 @@
#include "config.h"
#include "PcmConvert.hxx"
#include "PcmChannels.hxx"
-#include "PcmFormat.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>
@@ -53,7 +53,7 @@ PcmConvert::~PcmConvert()
bool
PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format,
- gcc_unused Error &error)
+ Error &error)
{
assert(!src_format.IsValid());
assert(!dest_format.IsValid());
@@ -63,12 +63,24 @@ PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format,
src_format = _src_format;
dest_format = _dest_format;
+ AudioFormat format = src_format;
+ if (format.format == SampleFormat::DSD)
+ format.format = SampleFormat::FLOAT;
+
+ if (format.format != dest_format.format &&
+ !format_converter.Open(format.format, dest_format.format, error))
+ return false;
+ format.format = dest_format.format;
+
return true;
}
void
PcmConvert::Close()
{
+ if (src_format.format != dest_format.format)
+ format_converter.Close();
+
dsd.Reset();
resampler.Reset();
@@ -79,23 +91,14 @@ PcmConvert::Close()
}
inline ConstBuffer<int16_t>
-PcmConvert::Convert16(ConstBuffer<void> src, AudioFormat format, Error &error)
+PcmConvert::Convert16(ConstBuffer<int16_t> src, AudioFormat format,
+ Error &error)
{
- const int16_t *buf;
- size_t len;
-
+ assert(format.format == SampleFormat::S16);
assert(dest_format.format == SampleFormat::S16);
- buf = pcm_convert_to_16(format_buffer, dither,
- format.format,
- src.data, src.size,
- &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to 16 bit is not implemented",
- sample_format_to_string(format.format));
- return nullptr;
- }
+ 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,
@@ -125,22 +128,14 @@ PcmConvert::Convert16(ConstBuffer<void> src, AudioFormat format, Error &error)
}
inline ConstBuffer<int32_t>
-PcmConvert::Convert24(ConstBuffer<void> src, AudioFormat format, Error &error)
+PcmConvert::Convert24(ConstBuffer<int32_t> src, AudioFormat format,
+ Error &error)
{
- const int32_t *buf;
- size_t len;
-
+ assert(format.format == SampleFormat::S24_P32);
assert(dest_format.format == SampleFormat::S24_P32);
- buf = pcm_convert_to_24(format_buffer,
- format.format,
- src.data, src.size, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to 24 bit is not implemented",
- sample_format_to_string(format.format));
- return nullptr;
- }
+ 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,
@@ -170,22 +165,14 @@ PcmConvert::Convert24(ConstBuffer<void> src, AudioFormat format, Error &error)
}
inline ConstBuffer<int32_t>
-PcmConvert::Convert32(ConstBuffer<void> src, AudioFormat format, Error &error)
+PcmConvert::Convert32(ConstBuffer<int32_t> src, AudioFormat format,
+ Error &error)
{
- const int32_t *buf;
- size_t len;
-
+ assert(format.format == SampleFormat::S32);
assert(dest_format.format == SampleFormat::S32);
- buf = pcm_convert_to_32(format_buffer,
- format.format,
- src.data, src.size, &len);
- if (buf == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to 32 bit is not implemented",
- sample_format_to_string(format.format));
- return nullptr;
- }
+ 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,
@@ -215,23 +202,14 @@ PcmConvert::Convert32(ConstBuffer<void> src, AudioFormat format, Error &error)
}
inline ConstBuffer<float>
-PcmConvert::ConvertFloat(ConstBuffer<void> src, AudioFormat format,
+PcmConvert::ConvertFloat(ConstBuffer<float> src, AudioFormat format,
Error &error)
{
+ assert(format.format == SampleFormat::FLOAT);
assert(dest_format.format == SampleFormat::FLOAT);
- /* convert to float now */
-
- size_t size;
- const float *buffer = pcm_convert_to_float(format_buffer,
- format.format,
- src.data, src.size, &size);
- if (buffer == nullptr) {
- error.Format(pcm_convert_domain,
- "Conversion from %s to float is not implemented",
- sample_format_to_string(format.format));
- return nullptr;
- }
+ auto buffer = src.data;
+ size_t size = src.size * sizeof(*src.data);
/* convert channels */
@@ -288,21 +266,33 @@ PcmConvert::Convert(const void *src, size_t src_size,
format.format = SampleFormat::FLOAT;
}
+ if (format.format != dest_format.format) {
+ buffer = format_converter.Convert(buffer, error);
+ if (buffer.IsNull())
+ return nullptr;
+
+ format.format = dest_format.format;
+ }
+
switch (dest_format.format) {
case SampleFormat::S16:
- buffer = Convert16(buffer, format, error).ToVoid();
+ buffer = Convert16(ConstBuffer<int16_t>::FromVoid(buffer),
+ format, error).ToVoid();
break;
case SampleFormat::S24_P32:
- buffer = Convert24(buffer, format, error).ToVoid();
+ buffer = Convert24(ConstBuffer<int32_t>::FromVoid(buffer),
+ format, error).ToVoid();
break;
case SampleFormat::S32:
- buffer = Convert32(buffer, format, error).ToVoid();
+ buffer = Convert32(ConstBuffer<int32_t>::FromVoid(buffer),
+ format, error).ToVoid();
break;
case SampleFormat::FLOAT:
- buffer = ConvertFloat(buffer, format, error).ToVoid();
+ buffer = ConvertFloat(ConstBuffer<float>::FromVoid(buffer),
+ format, error).ToVoid();
break;
default:
diff --git a/src/pcm/PcmConvert.hxx b/src/pcm/PcmConvert.hxx
index 12c4b26f3..d78d72214 100644
--- a/src/pcm/PcmConvert.hxx
+++ b/src/pcm/PcmConvert.hxx
@@ -20,10 +20,10 @@
#ifndef PCM_CONVERT_HXX
#define PCM_CONVERT_HXX
-#include "PcmDither.hxx"
#include "PcmDsd.hxx"
#include "PcmResample.hxx"
#include "PcmBuffer.hxx"
+#include "FormatConverter.hxx"
#include "AudioFormat.hxx"
#include <stddef.h>
@@ -42,10 +42,7 @@ class PcmConvert {
PcmResampler resampler;
- PcmDither dither;
-
- /** the buffer for converting the sample format */
- PcmBuffer format_buffer;
+ PcmFormatConverter format_converter;
/** the buffer for converting the channel count */
PcmBuffer channels_buffer;
@@ -85,16 +82,16 @@ public:
Error &error);
private:
- ConstBuffer<int16_t> Convert16(ConstBuffer<void> src,
+ ConstBuffer<int16_t> Convert16(ConstBuffer<int16_t> src,
AudioFormat format,
Error &error);
- ConstBuffer<int32_t> Convert24(ConstBuffer<void> src,
+ ConstBuffer<int32_t> Convert24(ConstBuffer<int32_t> src,
AudioFormat format,
Error &error);
- ConstBuffer<int32_t> Convert32(ConstBuffer<void> src,
+ ConstBuffer<int32_t> Convert32(ConstBuffer<int32_t> src,
AudioFormat format,
Error &error);
- ConstBuffer<float> ConvertFloat(ConstBuffer<void> src,
+ ConstBuffer<float> ConvertFloat(ConstBuffer<float> src,
AudioFormat format,
Error &error);
};