1use crate::eq::common::*;
2use scales::prelude::*;
3
4const COEFFS: [[f64; 6]; 7] = [
5 [1f64, 0f64, 0f64, 0f64, 0f64, 0f64],
6 [1.4142f64, 0f64, 0f64, 1f64, 0f64, 0f64],
7 [1f64, 1f64, 0f64, 0f64, 1f64, 0f64],
8 [1.8478f64, 0.7654f64, 0f64, 1f64, 1f64, 0f64],
9 [1f64, 1.6180f64, 0.6180f64, 0f64, 1f64, 1f64],
10 [1.3617f64, 1.3617f64, 0f64, 0.6180f64, 0.6180f64, 0f64],
11 [1.4142f64, 1.4142f64, 0f64, 1f64, 1f64, 0f64],
12];
13
14pub fn plot_eq(eq: &EQ, width: f64, height: f64, invert_y: bool) -> EqGraph {
15 let x_conv = eq.x_to_frequency_converter(width);
16 let y_conv = eq.y_to_gain_converter(height, invert_y);
17
18 let xc = x_conv.clone();
19
20 let fs = (0..width as usize).map(move |x| xc.convert(x as f64));
21
22 let band_curves = eq.bands.iter().map(|(band, a)| (band.plot(fs.clone()), *a));
23 let sum = merge_all(band_curves.clone());
24
25 let band_curves: Vec<(Vec<(X, Y)>, Active)> = band_curves
26 .map(|(curve, active)| (all_to_x_y(curve, &x_conv, &y_conv), active))
27 .collect();
28
29 let sum: Vec<(X, Y)> = if let Some(sum) = sum {
30 all_to_x_y(sum, &x_conv, &y_conv)
31 } else {
32 Vec::new()
33 };
34
35 EqGraph { band_curves, sum }
36}
37
38pub fn plot(
39 eq_band: &EqBand,
40 range: impl Iterator<Item = Frequency> + 'static,
41) -> Box<dyn Iterator<Item = (Frequency, Gain)>> {
42 match eq_band {
43 EqBand::Bell { frequency, gain, q } => plot_bell(range, *frequency, *gain, *q),
44 EqBand::HighShelf { frequency, gain } => plot_high_shelf(range, *frequency, *gain),
45 EqBand::LowShelf { frequency, gain } => plot_low_shelf(range, *frequency, *gain),
46 EqBand::HighPass { frequency, slope } => plot_high_pass(range, *frequency, *slope),
47 EqBand::LowPass { frequency, slope } => plot_low_pass(range, *frequency, *slope),
48 }
49}
50
51fn plot_bell(
52 range: impl Iterator<Item = Frequency> + 'static,
53 frequency: Frequency,
54 gain: Gain,
55 q: Q,
56) -> Box<dyn Iterator<Item = (Frequency, Gain)>> {
57 Box::new(range.map(move |f| (f, calc_bell_gain(f, frequency, gain, q))))
58}
59
60fn calc_bell_gain(f: Frequency, frequency: Frequency, gain: Gain, q: Q) -> Gain {
61 let p = to_power(gain);
62 let pr = to_pr(p);
63
64 let f0 = frequency / f;
65 let f1 = f0.powi(2);
66 let f2 = (1.0 - f1).powi(2);
67 let q2 = (1.0 / q).powi(2);
68
69 let n = f2.powi(2) + (q2 * pr * f1).powi(2) + (f2 * f1 * pr.powi(2) * q2) + (f2 * f1 * q2);
70 let d = (f2 + q2 * f1).powi(2);
71
72 let p_out = if p >= 1.0 {
73 (n / d).sqrt()
74 } else {
75 (d / n).sqrt()
76 };
77
78 to_decibel(p_out)
79}
80
81fn plot_high_shelf(
82 range: impl Iterator<Item = Frequency> + 'static,
83 frequency: Frequency,
84 gain: Gain,
85) -> Box<dyn Iterator<Item = (Frequency, Gain)>> {
86 Box::new(range.map(move |f| (f, calc_high_shelf_gain(f, frequency, gain))))
87}
88
89fn calc_high_shelf_gain(f: Frequency, frequency: Frequency, gain: Gain) -> Gain {
90 let p = to_power(gain);
91
92 let f0 = frequency / f;
93 let f1 = f0.powi(2);
94 let f2 = (1.0 - f1).powi(2);
95
96 let d = (f2 + 2.0 * f1).powi(2);
97 let p_out = if p >= 1.0 {
98 let f3 = (p - f1).powi(2);
99 let n = (f3 * f2) + (4.0 * p * f1 * f1) + (2.0 * p * f1 * f2) + (2.0 * f1 * f3);
100 (n / d).sqrt()
101 } else {
102 let pr = to_pr(p);
103 let f3 = (pr - f1).powi(2);
104 let n = (f2 * f3) + (4.0 * pr * f1 * f1) + (2.0 * f1 * f3) + (2.0 * pr * f1 * f2);
105 (d / n).sqrt()
106 };
107
108 to_decibel(p_out)
109}
110
111fn plot_low_shelf(
112 range: impl Iterator<Item = Frequency> + 'static,
113 frequency: Frequency,
114 gain: Gain,
115) -> Box<dyn Iterator<Item = (Frequency, Gain)>> {
116 Box::new(range.map(move |f| (f, calc_low_shelf_gain(f, frequency, gain))))
117}
118
119fn calc_low_shelf_gain(f: Frequency, frequency: Frequency, gain: Gain) -> Gain {
120 let p = to_power(gain);
121
122 let f0 = frequency / f;
123 let f1 = f0.powi(2);
124 let f2 = (1.0 - f1).powi(2);
125
126 let d = f2 + 2.0 * f1;
127 let p_out = if p >= 1.0 {
128 let n = (1.0 - p * f1).powi(2) + (2.0 * p) * f1;
129 (n / d).sqrt()
130 } else {
131 let pr = to_pr(p);
132 let n = (1.0 - pr * f1).powi(2) + (2.0 * pr) * f1;
133 (d / n).sqrt()
134 };
135
136 to_decibel(p_out)
137}
138
139fn plot_high_pass(
140 range: impl Iterator<Item = Frequency> + 'static,
141 frequency: Frequency,
142 slope: Slope,
143) -> Box<dyn Iterator<Item = (Frequency, Gain)>> {
144 Box::new(range.map(move |f| (f, calc_high_pass_gain(f, frequency, slope))))
145}
146
147fn calc_high_pass_gain(f: Frequency, frequency: Frequency, slope: Slope) -> Gain {
148 let f0 = frequency / f;
149 let f1 = f0.powi(2);
150 let f2 = f1.powi(2);
151 let mut d = 1.0;
152
153 let order = slope / 6;
154 let ord_off = if order == 0 { 1 } else { order };
155
156 for k in 0..(order + 1) / 2 {
157 let a = COEFFS[ord_off - 1][k];
158 let b = COEFFS[ord_off - 1][k + 3];
159 d *= 1.0 + (a.powi(2) - 2.0 * b) * f1 + b.powi(2) * f2;
160 }
161
162 let p_out = (1.0 / d).sqrt();
163
164 to_decibel(p_out)
165}
166
167fn plot_low_pass(
168 range: impl Iterator<Item = Frequency> + 'static,
169 frequency: Frequency,
170 slope: Slope,
171) -> Box<dyn Iterator<Item = (Frequency, Gain)>> {
172 Box::new(range.map(move |f| (f, calc_low_pass_gain(f, frequency, slope))))
173}
174
175fn calc_low_pass_gain(f: Frequency, frequency: Frequency, slope: Slope) -> Gain {
176 let f0 = frequency / f;
177 let f1 = (1.0 / f0).powi(2);
178 let f2 = f1.powi(2);
179 let mut d = 1.0;
180
181 let order = slope / 6;
182 let ord_off = if order == 0 { 1 } else { order };
183
184 for k in 0..(order + 1) / 2 {
185 let a = COEFFS[ord_off - 1][k];
186 let b = COEFFS[ord_off - 1][k + 3];
187 d *= 1.0 + (a.powi(2) - 2.0 * b) * f1 + b.powi(2) * f2;
188 }
189
190 let p_out = (1.0 / d).sqrt();
191
192 to_decibel(p_out)
193}
194
195fn to_power(gain: f64) -> f64 {
196 10f64.powf(gain / 20.0)
197}
198
199fn to_pr(power: f64) -> f64 {
200 if power >= 1.0 {
201 power
202 } else {
203 1.0 / power
204 }
205}
206
207fn to_decibel(power: f64) -> f64 {
208 20.0 * power.log10()
209}
210
211fn merge_all(
212 band_curves: impl Iterator<Item = (Box<dyn Iterator<Item = (Frequency, Gain)>>, Active)>,
213) -> Option<Box<dyn Iterator<Item = (Frequency, Gain)>>> {
214 band_curves.fold(None, |a, b| {
215 if let Some(a) = a {
216 if b.1 {
217 Some(merge(a, b.0))
218 } else {
219 Some(a)
220 }
221 } else if b.1 {
222 Some(b.0)
223 } else {
224 None
225 }
226 })
227}
228
229fn merge(
230 a: Box<dyn std::iter::Iterator<Item = (f64, f64)>>,
231 b: Box<dyn std::iter::Iterator<Item = (f64, f64)>>,
232) -> Box<dyn std::iter::Iterator<Item = (f64, f64)>> {
233 Box::new(
234 a.into_iter()
235 .zip(b.into_iter())
236 .map(|((f, g1), (_, g2))| (f, g1 + g2)),
237 )
238}
239
240fn all_to_x_y(
241 fgs: impl Iterator<Item = (Frequency, Gain)>,
242 fc: &impl Converter<X, Frequency>,
243 gc: &impl Converter<Y, Gain>,
244) -> Vec<(X, Y)> {
245 fgs.map(|(f, g)| to_x_y(f, g, fc, gc)).collect()
246}
247
248fn to_x_y(
249 f: Frequency,
250 g: Gain,
251 fc: &impl Converter<X, Frequency>,
252 gc: &impl Converter<Y, Gain>,
253) -> (X, Y) {
254 let x = fc.convert_back(f);
255 let y = gc.convert_back(g);
256 (x, y)
257}