diff options
author | Max Kellermann <max@duempel.org> | 2015-10-27 00:22:22 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2015-10-27 11:44:23 +0100 |
commit | 15e432204e62dd5a1c873af13a679195b9645b0c (patch) | |
tree | a00687f4ac08b273a9416c36681749c42ed9dcbe /src | |
parent | 4b1630e1ec1fe5cbecc013a3e1487d9f43fcdd2f (diff) |
pcm/Order: new library to convert from FLAC to ALSA channel order
This new library is integrated in the PcmExport class and (if enabled)
converts MPD's channel order (= FLAC channel order) to ALSA channel
order.
This fixes:
http://bugs.musicpd.org/view.php?id=3147
and
http://bugs.musicpd.org/view.php?id=3255
Diffstat (limited to 'src')
-rw-r--r-- | src/output/plugins/AlsaOutputPlugin.cxx | 2 | ||||
-rw-r--r-- | src/output/plugins/OssOutputPlugin.cxx | 2 | ||||
-rw-r--r-- | src/pcm/Order.cxx | 135 | ||||
-rw-r--r-- | src/pcm/Order.hxx | 37 | ||||
-rw-r--r-- | src/pcm/PcmExport.cxx | 9 | ||||
-rw-r--r-- | src/pcm/PcmExport.hxx | 18 |
6 files changed, 201 insertions, 2 deletions
diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index 8a7bb9643..0bc5438f1 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -711,7 +711,7 @@ AlsaOutput::SetupOrDop(AudioFormat &audio_format, Error &error) pcm_export->Open(audio_format.format, audio_format.channels, - dop2, shift8, packed, reverse_endian); + true, dop2, shift8, packed, reverse_endian); return true; } diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx index 7f75f4e31..ba86dc079 100644 --- a/src/output/plugins/OssOutputPlugin.cxx +++ b/src/output/plugins/OssOutputPlugin.cxx @@ -537,7 +537,7 @@ oss_probe_sample_format(int fd, SampleFormat sample_format, *oss_format_r = oss_format; #ifdef AFMT_S24_PACKED - pcm_export.Open(sample_format, 0, false, false, + pcm_export.Open(sample_format, 0, true, false, false, oss_format == AFMT_S24_PACKED, oss_format == AFMT_S24_PACKED && !IsLittleEndian()); diff --git a/src/pcm/Order.cxx b/src/pcm/Order.cxx new file mode 100644 index 000000000..470f9d119 --- /dev/null +++ b/src/pcm/Order.cxx @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2003-2015 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 "Order.hxx" +#include "PcmBuffer.hxx" +#include "util/ConstBuffer.hxx" + +template<typename V> +struct TwoPointers { + V *dest; + const V *src; + + TwoPointers<V> &CopyOne() { + *dest++ = *src++; + return *this; + } + + TwoPointers<V> &CopyTwo() { + return CopyOne().CopyOne(); + } + + TwoPointers<V> &SwapTwoPairs() { + *dest++ = src[2]; + *dest++ = src[3]; + *dest++ = src[0]; + *dest++ = src[1]; + src += 4; + return *this; + } + + TwoPointers<V> &ToAlsa51() { + return CopyTwo() // left+right + .SwapTwoPairs(); // center, LFE, surround left+right + } + + TwoPointers<V> &ToAlsa71() { + return ToAlsa51() + .CopyTwo(); // side left+right + } +}; + +template<typename V> +static void +ToAlsaChannelOrder51(V *dest, const V *src, size_t n) +{ + TwoPointers<V> p{dest, src}; + for (size_t i = 0; i != n; ++i) + p.ToAlsa51(); +} + +template<typename V> +static inline ConstBuffer<V> +ToAlsaChannelOrder51(PcmBuffer &buffer, ConstBuffer<V> src) +{ + auto dest = buffer.GetT<V>(src.size); + ToAlsaChannelOrder51(dest, src.data, src.size / 6); + return { dest, src.size }; +} + +template<typename V> +static void +ToAlsaChannelOrder71(V *dest, const V *src, size_t n) +{ + TwoPointers<V> p{dest, src}; + for (size_t i = 0; i != n; ++i) + p.ToAlsa71(); +} + +template<typename V> +static inline ConstBuffer<V> +ToAlsaChannelOrder71(PcmBuffer &buffer, ConstBuffer<V> src) +{ + auto dest = buffer.GetT<V>(src.size); + ToAlsaChannelOrder71(dest, src.data, src.size / 6); + return { dest, src.size }; +} + +template<typename V> +static ConstBuffer<V> +ToAlsaChannelOrderT(PcmBuffer &buffer, ConstBuffer<V> src, unsigned channels) +{ + switch (channels) { + case 6: // 5.1 + return ToAlsaChannelOrder51(buffer, src); + + case 8: // 7.1 + return ToAlsaChannelOrder71(buffer, src); + + default: + return src; + } +} + +ConstBuffer<void> +ToAlsaChannelOrder(PcmBuffer &buffer, ConstBuffer<void> src, + SampleFormat sample_format, unsigned channels) +{ + switch (sample_format) { + case SampleFormat::UNDEFINED: + case SampleFormat::S8: + case SampleFormat::DSD: + return src; + + case SampleFormat::S16: + return ToAlsaChannelOrderT(buffer, + ConstBuffer<int16_t>::FromVoid(src), + channels).ToVoid(); + + case SampleFormat::S24_P32: + case SampleFormat::S32: + case SampleFormat::FLOAT: + return ToAlsaChannelOrderT(buffer, + ConstBuffer<int32_t>::FromVoid(src), + channels).ToVoid(); + } + + gcc_unreachable(); +} diff --git a/src/pcm/Order.hxx b/src/pcm/Order.hxx new file mode 100644 index 000000000..14bfa2382 --- /dev/null +++ b/src/pcm/Order.hxx @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2015 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_ORDER_HXX +#define MPD_PCM_ORDER_HXX + +#include "check.h" +#include "AudioFormat.hxx" + +class PcmBuffer; +template<typename T> struct ConstBuffer; + +/** + * Convert the given buffer from FLAC channel order + * (https://xiph.org/flac/format.html) to ALSA channel order. + */ +ConstBuffer<void> +ToAlsaChannelOrder(PcmBuffer &buffer, ConstBuffer<void> src, + SampleFormat sample_format, unsigned channels); + +#endif diff --git a/src/pcm/PcmExport.cxx b/src/pcm/PcmExport.cxx index af2eb7d9f..0bf4f8be8 100644 --- a/src/pcm/PcmExport.cxx +++ b/src/pcm/PcmExport.cxx @@ -19,6 +19,7 @@ #include "config.h" #include "PcmExport.hxx" +#include "Order.hxx" #include "PcmDop.hxx" #include "PcmPack.hxx" #include "util/ByteReverse.hxx" @@ -28,12 +29,16 @@ void PcmExport::Open(SampleFormat sample_format, unsigned _channels, + bool _alsa_channel_order, bool _dop, bool _shift8, bool _pack, bool _reverse_endian) { assert(audio_valid_sample_format(sample_format)); assert(!_dop || audio_valid_channel_count(_channels)); channels = _channels; + alsa_channel_order = _alsa_channel_order + ? sample_format + : SampleFormat::UNDEFINED; dop = _dop && sample_format == SampleFormat::DSD; if (dop) /* after the conversion to DoP, the DSD @@ -77,6 +82,10 @@ PcmExport::GetFrameSize(const AudioFormat &audio_format) const ConstBuffer<void> PcmExport::Export(ConstBuffer<void> data) { + if (alsa_channel_order != SampleFormat::UNDEFINED) + data = ToAlsaChannelOrder(order_buffer, data, + alsa_channel_order, channels); + if (dop) data = pcm_dsd_to_dop(dop_buffer, channels, ConstBuffer<uint8_t>::FromVoid(data)) diff --git a/src/pcm/PcmExport.hxx b/src/pcm/PcmExport.hxx index 7265ca07d..aafa1cea0 100644 --- a/src/pcm/PcmExport.hxx +++ b/src/pcm/PcmExport.hxx @@ -34,6 +34,13 @@ template<typename T> struct ConstBuffer; */ struct PcmExport { /** + * This buffer is used to reorder channels. + * + * @see #alsa_channel_order + */ + PcmBuffer order_buffer; + + /** * The buffer is used to convert DSD samples to the * DoP format. * @@ -61,6 +68,16 @@ struct PcmExport { uint8_t channels; /** + * Convert the given buffer from FLAC channel order to ALSA + * channel order using ToAlsaChannelOrder()? + * + * If this value is SampleFormat::UNDEFINED, then no channel + * reordering is applied, otherwise this is the input sample + * format. + */ + SampleFormat alsa_channel_order; + + /** * Convert DSD to DSD-over-PCM (DoP)? Input format must be * SampleFormat::DSD and output format must be * SampleFormat::S24_P32. @@ -96,6 +113,7 @@ struct PcmExport { * @param channels the number of channels; ignored unless dop is set */ void Open(SampleFormat sample_format, unsigned channels, + bool _alsa_channel_order, bool dop, bool shift8, bool pack, bool reverse_endian); /** |