1 use rand::rngs::SmallRng;
2 use rand::SeedableRng;
3 use rand_distr::{Distribution, Normal, Triangular, Uniform};
4 use std::fmt;
5 
6 const NUM_CHANNELS: usize = 2;
7 
8 // Dithering lowers digital-to-analog conversion ("requantization") error,
9 // linearizing output, lowering distortion and replacing it with a constant,
10 // fixed noise level, which is more pleasant to the ear than the distortion.
11 //
12 // Guidance:
13 //
14 //  * On S24, S24_3 and S24, the default is to use triangular dithering.
15 //    Depending on personal preference you may use Gaussian dithering instead;
16 //    it's not as good objectively, but it may be preferred subjectively if
17 //    you are looking for a more "analog" sound akin to tape hiss.
18 //
19 //  * Advanced users who know that they have a DAC without noise shaping have
20 //    a third option: high-passed dithering, which is like triangular dithering
21 //    except that it moves dithering noise up in frequency where it is less
22 //    audible. Note: 99% of DACs are of delta-sigma design with noise shaping,
23 //    so unless you have a multibit / R2R DAC, or otherwise know what you are
24 //    doing, this is not for you.
25 //
26 //  * Don't dither or shape noise on S32 or F32. On F32 it's not supported
27 //    anyway (there are no integer conversions and so no rounding errors) and
28 //    on S32 the noise level is so far down that it is simply inaudible even
29 //    after volume normalisation and control.
30 //
31 pub trait Ditherer {
new() -> Self where Self: Sized32     fn new() -> Self
33     where
34         Self: Sized;
name(&self) -> &'static str35     fn name(&self) -> &'static str;
noise(&mut self) -> f6436     fn noise(&mut self) -> f64;
37 }
38 
39 impl fmt::Display for dyn Ditherer {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result40     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41         write!(f, "{}", self.name())
42     }
43 }
44 
create_rng() -> SmallRng45 fn create_rng() -> SmallRng {
46     SmallRng::from_entropy()
47 }
48 
49 pub struct TriangularDitherer {
50     cached_rng: SmallRng,
51     distribution: Triangular<f64>,
52 }
53 
54 impl Ditherer for TriangularDitherer {
new() -> Self55     fn new() -> Self {
56         Self {
57             cached_rng: create_rng(),
58             // 2 LSB peak-to-peak needed to linearize the response:
59             distribution: Triangular::new(-1.0, 1.0, 0.0).unwrap(),
60         }
61     }
62 
name(&self) -> &'static str63     fn name(&self) -> &'static str {
64         Self::NAME
65     }
66 
noise(&mut self) -> f6467     fn noise(&mut self) -> f64 {
68         self.distribution.sample(&mut self.cached_rng)
69     }
70 }
71 
72 impl TriangularDitherer {
73     pub const NAME: &'static str = "tpdf";
74 }
75 
76 pub struct GaussianDitherer {
77     cached_rng: SmallRng,
78     distribution: Normal<f64>,
79 }
80 
81 impl Ditherer for GaussianDitherer {
new() -> Self82     fn new() -> Self {
83         Self {
84             cached_rng: create_rng(),
85             // 1/2 LSB RMS needed to linearize the response:
86             distribution: Normal::new(0.0, 0.5).unwrap(),
87         }
88     }
89 
name(&self) -> &'static str90     fn name(&self) -> &'static str {
91         Self::NAME
92     }
93 
noise(&mut self) -> f6494     fn noise(&mut self) -> f64 {
95         self.distribution.sample(&mut self.cached_rng)
96     }
97 }
98 
99 impl GaussianDitherer {
100     pub const NAME: &'static str = "gpdf";
101 }
102 
103 pub struct HighPassDitherer {
104     active_channel: usize,
105     previous_noises: [f64; NUM_CHANNELS],
106     cached_rng: SmallRng,
107     distribution: Uniform<f64>,
108 }
109 
110 impl Ditherer for HighPassDitherer {
new() -> Self111     fn new() -> Self {
112         Self {
113             active_channel: 0,
114             previous_noises: [0.0; NUM_CHANNELS],
115             cached_rng: create_rng(),
116             distribution: Uniform::new_inclusive(-0.5, 0.5), // 1 LSB +/- 1 LSB (previous) = 2 LSB
117         }
118     }
119 
name(&self) -> &'static str120     fn name(&self) -> &'static str {
121         Self::NAME
122     }
123 
noise(&mut self) -> f64124     fn noise(&mut self) -> f64 {
125         let new_noise = self.distribution.sample(&mut self.cached_rng);
126         let high_passed_noise = new_noise - self.previous_noises[self.active_channel];
127         self.previous_noises[self.active_channel] = new_noise;
128         self.active_channel ^= 1;
129         high_passed_noise
130     }
131 }
132 
133 impl HighPassDitherer {
134     pub const NAME: &'static str = "tpdf_hp";
135 }
136 
mk_ditherer<D: Ditherer + 'static>() -> Box<dyn Ditherer>137 pub fn mk_ditherer<D: Ditherer + 'static>() -> Box<dyn Ditherer> {
138     Box::new(D::new())
139 }
140 
141 pub type DithererBuilder = fn() -> Box<dyn Ditherer>;
142 
find_ditherer(name: Option<String>) -> Option<DithererBuilder>143 pub fn find_ditherer(name: Option<String>) -> Option<DithererBuilder> {
144     match name.as_deref() {
145         Some(TriangularDitherer::NAME) => Some(mk_ditherer::<TriangularDitherer>),
146         Some(GaussianDitherer::NAME) => Some(mk_ditherer::<GaussianDitherer>),
147         Some(HighPassDitherer::NAME) => Some(mk_ditherer::<HighPassDitherer>),
148         _ => None,
149     }
150 }
151