// SPDX-License-Identifier: GPL-2.0-or-later // Copyright The Music Player Daemon Project #include "PcmFormat.hxx" #include "Buffer.hxx" #include "Traits.hxx" #include "FloatConvert.hxx" #include "ShiftConvert.hxx" #include "util/SpanCast.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 std::span AllocateConvert(PcmBuffer &buffer, C convert, std::span src) { auto dest = buffer.GetT(src.size()); convert.Convert(dest, src.data(), src.size()); return { dest, src.size() }; } template> static std::span AllocateFromFloat(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, FloatToInteger(), src); } static std::span pcm_allocate_8_to_16(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert8To16(), src); } static std::span pcm_allocate_24p32_to_16(PcmBuffer &buffer, PcmDither &dither, std::span src) { return AllocateConvert(buffer, Convert24To16(dither), src); } static std::span pcm_allocate_32_to_16(PcmBuffer &buffer, PcmDither &dither, std::span src) { return AllocateConvert(buffer, Convert32To16(dither), src); } static std::span pcm_allocate_float_to_16(PcmBuffer &buffer, std::span src) { return AllocateFromFloat(buffer, src); } std::span pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither, SampleFormat src_format, std::span src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_16(buffer, FromBytesStrict(src)); case SampleFormat::S16: return FromBytesStrict(src); case SampleFormat::S24_P32: return pcm_allocate_24p32_to_16(buffer, dither, FromBytesStrict(src)); case SampleFormat::S32: return pcm_allocate_32_to_16(buffer, dither, FromBytesStrict(src)); case SampleFormat::FLOAT: return pcm_allocate_float_to_16(buffer, FromBytesStrict(src)); } return {}; } struct Convert8To24 : PerSampleConvert> {}; struct Convert16To24 : PerSampleConvert> {}; static std::span pcm_allocate_8_to_24(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert8To24(), src); } static std::span pcm_allocate_16_to_24(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert16To24(), src); } struct Convert32To24 : PerSampleConvert> {}; static std::span pcm_allocate_32_to_24(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert32To24(), src); } static std::span pcm_allocate_float_to_24(PcmBuffer &buffer, std::span src) { return AllocateFromFloat(buffer, src); } std::span pcm_convert_to_24(PcmBuffer &buffer, SampleFormat src_format, std::span src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_24(buffer, FromBytesStrict(src)); case SampleFormat::S16: return pcm_allocate_16_to_24(buffer, FromBytesStrict(src)); case SampleFormat::S24_P32: return FromBytesStrict(src); case SampleFormat::S32: return pcm_allocate_32_to_24(buffer, FromBytesStrict(src)); case SampleFormat::FLOAT: return pcm_allocate_float_to_24(buffer, FromBytesStrict(src)); } return {}; } struct Convert8To32 : PerSampleConvert> {}; struct Convert16To32 : PerSampleConvert> {}; struct Convert24To32 : PerSampleConvert> {}; static std::span pcm_allocate_8_to_32(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert8To32(), src); } static std::span pcm_allocate_16_to_32(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert16To32(), src); } static std::span pcm_allocate_24p32_to_32(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert24To32(), src); } static std::span pcm_allocate_float_to_32(PcmBuffer &buffer, std::span src) { return AllocateFromFloat(buffer, src); } std::span pcm_convert_to_32(PcmBuffer &buffer, SampleFormat src_format, std::span src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_32(buffer, FromBytesStrict(src)); case SampleFormat::S16: return pcm_allocate_16_to_32(buffer, FromBytesStrict(src)); case SampleFormat::S24_P32: return pcm_allocate_24p32_to_32(buffer, FromBytesStrict(src)); case SampleFormat::S32: return FromBytesStrict(src); case SampleFormat::FLOAT: return pcm_allocate_float_to_32(buffer, FromBytesStrict(src)); } return {}; } struct Convert8ToFloat : PerSampleConvert> {}; struct Convert16ToFloat : PerSampleConvert> {}; struct Convert24ToFloat : PerSampleConvert> {}; struct Convert32ToFloat : PerSampleConvert> {}; static std::span pcm_allocate_8_to_float(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert8ToFloat(), src); } static std::span pcm_allocate_16_to_float(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert16ToFloat(), src); } static std::span pcm_allocate_24p32_to_float(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert24ToFloat(), src); } static std::span pcm_allocate_32_to_float(PcmBuffer &buffer, std::span src) { return AllocateConvert(buffer, Convert32ToFloat(), src); } std::span pcm_convert_to_float(PcmBuffer &buffer, SampleFormat src_format, std::span src) noexcept { switch (src_format) { case SampleFormat::UNDEFINED: case SampleFormat::DSD: break; case SampleFormat::S8: return pcm_allocate_8_to_float(buffer, FromBytesStrict(src)); case SampleFormat::S16: return pcm_allocate_16_to_float(buffer, FromBytesStrict(src)); case SampleFormat::S32: return pcm_allocate_32_to_float(buffer, FromBytesStrict(src)); case SampleFormat::S24_P32: return pcm_allocate_24p32_to_float(buffer, FromBytesStrict(src)); case SampleFormat::FLOAT: return FromBytesStrict(src); } return {}; }