1 /* SPDX-License-Identifier: BSD-3-Clause */ 2 /* SPDX-FileCopyrightText: OpenMPT Project Developers and Contributors */ 3 4 5 #pragma once 6 7 #include "openmpt/all/BuildSettings.hpp" 8 9 #include "mpt/base/macros.hpp" 10 #include "mpt/random/engine.hpp" 11 #include "mpt/random/default_engines.hpp" 12 #include "mpt/random/random.hpp" 13 #include "openmpt/base/Types.hpp" 14 #include "openmpt/soundbase/MixSample.hpp" 15 #include "openmpt/soundbase/MixSampleConvert.hpp" 16 17 18 OPENMPT_NAMESPACE_BEGIN 19 20 21 template <int ditherdepth = 1, bool triangular = false, bool shaped = true> 22 struct Dither_SimpleImpl 23 { 24 public: 25 using prng_type = mpt::fast_engine; 26 template <typename Trd> prng_initDither_SimpleImpl27 static prng_type prng_init(Trd &rd) 28 { 29 return mpt::make_prng<prng_type>(rd); 30 } 31 32 private: 33 int32 error = 0; 34 35 public: 36 template <uint32 targetbits, typename Trng> processDither_SimpleImpl37 MPT_FORCEINLINE MixSampleInt process(MixSampleInt sample, Trng &prng) 38 { 39 if constexpr(targetbits == 0) 40 { 41 MPT_UNREFERENCED_PARAMETER(prng); 42 return sample; 43 } else 44 { 45 static_assert(sizeof(MixSampleInt) == 4); 46 constexpr int rshift = (32 - targetbits) - MixSampleIntTraits::mix_headroom_bits; 47 if constexpr(rshift <= 1) 48 { 49 MPT_UNREFERENCED_PARAMETER(prng); 50 // nothing to dither 51 return sample; 52 } else 53 { 54 constexpr int rshiftpositive = (rshift > 1) ? rshift : 1; // work-around warnings about negative shift with C++14 compilers 55 constexpr int round_mask = ~((1 << rshiftpositive) - 1); 56 constexpr int round_offset = 1 << (rshiftpositive - 1); 57 constexpr int noise_bits = rshiftpositive + (ditherdepth - 1); 58 constexpr int noise_bias = (1 << (noise_bits - 1)); 59 int32 e = error; 60 unsigned int unoise = 0; 61 if constexpr(triangular) 62 { 63 unoise = (mpt::random<unsigned int>(prng, noise_bits) + mpt::random<unsigned int>(prng, noise_bits)) >> 1; 64 } else 65 { 66 unoise = mpt::random<unsigned int>(prng, noise_bits); 67 } 68 int noise = static_cast<int>(unoise) - noise_bias; // un-bias 69 int val = sample; 70 if constexpr(shaped) 71 { 72 val += (e >> 1); 73 } 74 int rounded = (val + noise + round_offset) & round_mask; 75 e = val - rounded; 76 sample = rounded; 77 error = e; 78 return sample; 79 } 80 } 81 } 82 template <uint32 targetbits, typename Trng> processDither_SimpleImpl83 MPT_FORCEINLINE MixSampleFloat process(MixSampleFloat sample, Trng &prng) 84 { 85 return mix_sample_cast<MixSampleFloat>(process<targetbits>(mix_sample_cast<MixSampleInt>(sample), prng)); 86 } 87 }; 88 89 using Dither_Simple = Dither_SimpleImpl<>; 90 91 92 OPENMPT_NAMESPACE_END 93