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