diff options
Diffstat (limited to 'src/pcm/PcmDither.cxx')
-rw-r--r-- | src/pcm/PcmDither.cxx | 74 |
1 files changed, 51 insertions, 23 deletions
diff --git a/src/pcm/PcmDither.cxx b/src/pcm/PcmDither.cxx index 98d0d443e..d9d4d28ca 100644 --- a/src/pcm/PcmDither.cxx +++ b/src/pcm/PcmDither.cxx @@ -20,18 +20,14 @@ #include "config.h" #include "PcmDither.hxx" #include "PcmPrng.hxx" +#include "Traits.hxx" -inline int16_t -PcmDither::Dither24To16(int_fast32_t sample) +template<typename T, T MIN, T MAX, unsigned scale_bits> +inline T +PcmDither::Dither(T sample) { - constexpr unsigned from_bits = 24; - constexpr unsigned to_bits = 16; - constexpr unsigned scale_bits = from_bits - to_bits; - constexpr int_fast32_t round = 1 << (scale_bits - 1); - constexpr int_fast32_t mask = (1 << scale_bits) - 1; - constexpr int_fast32_t ONE = 1 << (from_bits - 1); - constexpr int_fast32_t MIN = -ONE; - constexpr int_fast32_t MAX = ONE - 1; + constexpr T round = 1 << (scale_bits - 1); + constexpr T mask = (1 << scale_bits) - 1; sample += error[0] - error[1] + error[2]; @@ -39,9 +35,9 @@ PcmDither::Dither24To16(int_fast32_t sample) error[1] = error[0] / 2; /* round */ - int_fast32_t output = sample + round; + T output = sample + round; - int_fast32_t rnd = pcm_prng(random); + const T rnd = pcm_prng(random); output += (rnd & mask) - (random & mask); random = rnd; @@ -63,27 +59,59 @@ PcmDither::Dither24To16(int_fast32_t sample) error[0] = sample - output; - return (int16_t)(output >> scale_bits); + return output >> scale_bits; } -void -PcmDither::Dither24To16(int16_t *dest, const int32_t *src, - const int32_t *src_end) +template<typename ST, unsigned SBITS, unsigned DBITS> +inline ST +PcmDither::DitherShift(ST sample) +{ + static_assert(sizeof(ST) * 8 > SBITS, "Source type too small"); + static_assert(SBITS > DBITS, "Non-positive scale_bits"); + + static constexpr ST MIN = -(ST(1) << (SBITS - 1)); + static constexpr ST MAX = (ST(1) << (SBITS - 1)) - 1; + + return Dither<ST, MIN, MAX, SBITS - DBITS>(sample); +} + +template<typename ST, typename DT> +inline typename DT::value_type +PcmDither::DitherConvert(typename ST::value_type sample) +{ + static_assert(ST::BITS > DT::BITS, + "Sample formats cannot be dithered"); + + constexpr unsigned scale_bits = ST::BITS - DT::BITS; + + return Dither<typename ST::sum_type, ST::MIN, ST::MAX, + scale_bits>(sample); +} + +template<typename ST, typename DT> +inline void +PcmDither::DitherConvert(typename DT::pointer_type dest, + typename ST::const_pointer_type src, + typename ST::const_pointer_type src_end) { while (src < src_end) - *dest++ = Dither24To16(*src++); + *dest++ = DitherConvert<ST, DT>(*src++); } -inline int16_t -PcmDither::Dither32To16(int_fast32_t sample) +inline void +PcmDither::Dither24To16(int16_t *dest, const int32_t *src, + const int32_t *src_end) { - return Dither24To16(sample >> 8); + typedef SampleTraits<SampleFormat::S24_P32> ST; + typedef SampleTraits<SampleFormat::S16> DT; + DitherConvert<ST, DT>(dest, src, src_end); } -void +inline void PcmDither::Dither32To16(int16_t *dest, const int32_t *src, const int32_t *src_end) { - while (src < src_end) - *dest++ = Dither32To16(*src++); + typedef SampleTraits<SampleFormat::S32> ST; + typedef SampleTraits<SampleFormat::S16> DT; + DitherConvert<ST, DT>(dest, src, src_end); } |