diff options
Diffstat (limited to 'src/pcm')
-rw-r--r-- | src/pcm/FormatConverter.cxx | 104 | ||||
-rw-r--r-- | src/pcm/FormatConverter.hxx | 84 | ||||
-rw-r--r-- | src/pcm/PcmConvert.cxx | 108 | ||||
-rw-r--r-- | src/pcm/PcmConvert.hxx | 15 |
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); }; |