From 15e432204e62dd5a1c873af13a679195b9645b0c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 27 Oct 2015 00:22:22 +0100 Subject: 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 --- src/pcm/Order.cxx | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/pcm/Order.hxx | 37 ++++++++++++++ src/pcm/PcmExport.cxx | 9 ++++ src/pcm/PcmExport.hxx | 18 +++++++ 4 files changed, 199 insertions(+) create mode 100644 src/pcm/Order.cxx create mode 100644 src/pcm/Order.hxx (limited to 'src/pcm') 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 +struct TwoPointers { + V *dest; + const V *src; + + TwoPointers &CopyOne() { + *dest++ = *src++; + return *this; + } + + TwoPointers &CopyTwo() { + return CopyOne().CopyOne(); + } + + TwoPointers &SwapTwoPairs() { + *dest++ = src[2]; + *dest++ = src[3]; + *dest++ = src[0]; + *dest++ = src[1]; + src += 4; + return *this; + } + + TwoPointers &ToAlsa51() { + return CopyTwo() // left+right + .SwapTwoPairs(); // center, LFE, surround left+right + } + + TwoPointers &ToAlsa71() { + return ToAlsa51() + .CopyTwo(); // side left+right + } +}; + +template +static void +ToAlsaChannelOrder51(V *dest, const V *src, size_t n) +{ + TwoPointers p{dest, src}; + for (size_t i = 0; i != n; ++i) + p.ToAlsa51(); +} + +template +static inline ConstBuffer +ToAlsaChannelOrder51(PcmBuffer &buffer, ConstBuffer src) +{ + auto dest = buffer.GetT(src.size); + ToAlsaChannelOrder51(dest, src.data, src.size / 6); + return { dest, src.size }; +} + +template +static void +ToAlsaChannelOrder71(V *dest, const V *src, size_t n) +{ + TwoPointers p{dest, src}; + for (size_t i = 0; i != n; ++i) + p.ToAlsa71(); +} + +template +static inline ConstBuffer +ToAlsaChannelOrder71(PcmBuffer &buffer, ConstBuffer src) +{ + auto dest = buffer.GetT(src.size); + ToAlsaChannelOrder71(dest, src.data, src.size / 6); + return { dest, src.size }; +} + +template +static ConstBuffer +ToAlsaChannelOrderT(PcmBuffer &buffer, ConstBuffer 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 +ToAlsaChannelOrder(PcmBuffer &buffer, ConstBuffer 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::FromVoid(src), + channels).ToVoid(); + + case SampleFormat::S24_P32: + case SampleFormat::S32: + case SampleFormat::FLOAT: + return ToAlsaChannelOrderT(buffer, + ConstBuffer::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 struct ConstBuffer; + +/** + * Convert the given buffer from FLAC channel order + * (https://xiph.org/flac/format.html) to ALSA channel order. + */ +ConstBuffer +ToAlsaChannelOrder(PcmBuffer &buffer, ConstBuffer 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 PcmExport::Export(ConstBuffer 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::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 @@ -33,6 +33,13 @@ template struct ConstBuffer; * representation which are not supported by the pcm_convert library. */ 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. @@ -60,6 +67,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 @@ -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); /** -- cgit v1.2.3