/* * Copyright 2003-2020 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 "PcmFormat.hxx" #include "Buffer.hxx" #include "Traits.hxx" #include "FloatConvert.hxx" #include "ShiftConvert.hxx" #include "util/ConstBuffer.hxx" #include "util/TransformN.hxx" #include "Dither.cxx" // including the .cxx file to get inlined templates /** * Wrapper for a class that converts one sample at a time into one * that converts a buffer at a time. */ template struct PerSampleConvert : C { using SrcTraits = typename C::SrcTraits; using DstTraits = typename C::DstTraits; void Convert(typename DstTraits::pointer gcc_restrict out, typename SrcTraits::const_pointer gcc_restrict in, size_t n) const { transform_n(in, n, out, C::Convert); } }; struct Convert8To16 : PerSampleConvert> {}; struct Convert24To16 { using SrcTraits = SampleTraits; using DstTraits = SampleTraits; PcmDither &dither; explicit Convert24To16(PcmDither &_dither):dither(_dither) {} void Convert(int16_t *out, const int32_t *in, size_t n) { dither.Dither24To16(out, in, in + n); } }; struct Convert32To16 { using SrcTraits = SampleTraits; using DstTraits = SampleTraits; PcmDither &dither; explicit Convert32To16(PcmDither &_dither):dither(_dither) {} void Convert(int16_t *out, const int32_t *in, size_t n) { dither.Dither32To16(out, in, in + n); } }; template> struct PortableFloatToInteger : PerSampleConvert> {}; template> struct FloatToInteger : PortableFloatToInteger {}; /** * A template class that attempts to use the "optimized" algorithm for * large portions of the buffer, and calls the "portable" algorithm" * for the rest when the last block is not full. */ template class GlueOptimizedConvert : Optimized, Portable { public: using SrcTraits = typename Portable::SrcTraits; using DstTraits = typename Portable::DstTraits; void Convert(typename DstTraits::pointer out, typename SrcTraits::const_pointer in, size_t n) const { Optimized::Convert(out, in, n); /* use the "portable" algorithm for the trailing samples */ size_t remaining = n % Optimized::BLOCK_SIZE; size_t done = n - remaining; Portable::Convert(out + done, in + done, remaining); } }; #ifdef __ARM_NEON__ #include "Neon.hxx" template<> struct FloatToInteger> : GlueOptimizedConvert> {}; #endif template static ConstBuffer AllocateConvert(PcmBuffer &buffer, C convert, ConstBuffer src) { auto dest = buffer.GetT(src.size); convert.Convert(dest, src.data, src.size); return { dest, src.size }; } template> static ConstBuffer AllocateFromFloat(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, FloatToInteger(), src); } static ConstBuffer pcm_allocate_8_to_16(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert8To16(), src); } static ConstBuffer pcm_allocate_24p32_to_16(PcmBuffer &buffer, PcmDither &dither, ConstBuffer src) { return AllocateConvert(buffer, Convert24To16(dither), src); } static ConstBuffer pcm_allocate_32_to_16(PcmBuffer &buffer, PcmDither &dither, ConstBuffer src) { return AllocateConvert(buffer, Convert32To16(dither), src); } static ConstBuffer pcm_allocate_float_to_16(PcmBuffer &buffer, ConstBuffer src) { return AllocateFromFloat(buffer, src); } ConstBuffer pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither, SampleFormat src_format, ConstBuffer src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_16(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S16: return ConstBuffer::FromVoid(src); case SampleFormat::S24_P32: return pcm_allocate_24p32_to_16(buffer, dither, ConstBuffer::FromVoid(src)); case SampleFormat::S32: return pcm_allocate_32_to_16(buffer, dither, ConstBuffer::FromVoid(src)); case SampleFormat::FLOAT: return pcm_allocate_float_to_16(buffer, ConstBuffer::FromVoid(src)); } return nullptr; } struct Convert8To24 : PerSampleConvert> {}; struct Convert16To24 : PerSampleConvert> {}; static ConstBuffer pcm_allocate_8_to_24(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert8To24(), src); } static ConstBuffer pcm_allocate_16_to_24(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert16To24(), src); } struct Convert32To24 : PerSampleConvert> {}; static ConstBuffer pcm_allocate_32_to_24(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert32To24(), src); } static ConstBuffer pcm_allocate_float_to_24(PcmBuffer &buffer, ConstBuffer src) { return AllocateFromFloat(buffer, src); } ConstBuffer pcm_convert_to_24(PcmBuffer &buffer, SampleFormat src_format, ConstBuffer src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_24(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S16: return pcm_allocate_16_to_24(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S24_P32: return ConstBuffer::FromVoid(src); case SampleFormat::S32: return pcm_allocate_32_to_24(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::FLOAT: return pcm_allocate_float_to_24(buffer, ConstBuffer::FromVoid(src)); } return nullptr; } struct Convert8To32 : PerSampleConvert> {}; struct Convert16To32 : PerSampleConvert> {}; struct Convert24To32 : PerSampleConvert> {}; static ConstBuffer pcm_allocate_8_to_32(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert8To32(), src); } static ConstBuffer pcm_allocate_16_to_32(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert16To32(), src); } static ConstBuffer pcm_allocate_24p32_to_32(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert24To32(), src); } static ConstBuffer pcm_allocate_float_to_32(PcmBuffer &buffer, ConstBuffer src) { return AllocateFromFloat(buffer, src); } ConstBuffer pcm_convert_to_32(PcmBuffer &buffer, SampleFormat src_format, ConstBuffer src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_32(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S16: return pcm_allocate_16_to_32(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S24_P32: return pcm_allocate_24p32_to_32(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S32: return ConstBuffer::FromVoid(src); case SampleFormat::FLOAT: return pcm_allocate_float_to_32(buffer, ConstBuffer::FromVoid(src)); } return nullptr; } struct Convert8ToFloat : PerSampleConvert> {}; struct Convert16ToFloat : PerSampleConvert> {}; struct Convert24ToFloat : PerSampleConvert> {}; struct Convert32ToFloat : PerSampleConvert> {}; static ConstBuffer pcm_allocate_8_to_float(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert8ToFloat(), src); } static ConstBuffer pcm_allocate_16_to_float(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert16ToFloat(), src); } static ConstBuffer pcm_allocate_24p32_to_float(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert24ToFloat(), src); } static ConstBuffer pcm_allocate_32_to_float(PcmBuffer &buffer, ConstBuffer src) { return AllocateConvert(buffer, Convert32ToFloat(), src); } ConstBuffer pcm_convert_to_float(PcmBuffer &buffer, SampleFormat src_format, ConstBuffer src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_float(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S16: return pcm_allocate_16_to_float(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S32: return pcm_allocate_32_to_float(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::S24_P32: return pcm_allocate_24p32_to_float(buffer, ConstBuffer::FromVoid(src)); case SampleFormat::FLOAT: return ConstBuffer::FromVoid(src); } return nullptr; }