diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/pcm/ChannelsConverter.cxx | 115 | ||||
-rw-r--r-- | src/pcm/ChannelsConverter.hxx | 83 | ||||
-rw-r--r-- | src/pcm/PcmConvert.cxx | 85 | ||||
-rw-r--r-- | src/pcm/PcmConvert.hxx | 5 |
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; |