1use std::f32::consts::PI as PI_32;
6use std::f64::consts::PI;
7use std::iter::zip;
8
9use log::error;
10use malloc_size_of_derive::MallocSizeOf;
11use num_complex::Complex32;
12use num_traits::Zero;
13use realfft::RealFftPlanner;
14
15use crate::oscillator_node::OscillatorType;
16
17const FFT_MAX_SIZE: usize = 8192;
22const FFT_MIN_SIZE: usize = 4096;
23const CENTS_PER_RANGE: u32 = 1200 / 3; #[derive(Clone, Debug, Default, MallocSizeOf)]
26pub struct PeriodicWaveOptions {
27 imag: Vec<f32>,
28 real: Vec<f32>,
29 disable_normalization: bool,
30}
31
32impl PeriodicWaveOptions {
33 pub fn new(imag: Vec<f32>, real: Vec<f32>, disable_normalization: bool) -> Self {
34 Self {
35 imag,
36 real,
37 disable_normalization,
38 }
39 }
40}
41
42#[derive(Clone, Debug, MallocSizeOf)]
43pub struct PeriodicWave {
44 pub imag: Vec<f32>,
45 pub real: Vec<f32>,
46 pub normalize: bool,
47 size: usize,
48 wavetable: Wavetable,
49}
50
51type Wave = Vec<f32>;
52
53#[derive(Clone, Debug, MallocSizeOf)]
54struct Wavetable {
55 max_number_of_partials_in_band_limited_table: usize,
56 normalization_factor: Option<f64>,
57 waves: Vec<Wave>,
58 size: usize,
59}
60
61impl Wavetable {
62 fn new(size: usize, number_of_waves: usize) -> Self {
63 Wavetable {
64 max_number_of_partials_in_band_limited_table: 0,
65 normalization_factor: None,
66 waves: vec![Vec::with_capacity(size); number_of_waves],
67 size,
68 }
69 }
70
71 fn max_number_of_partials(&self) -> usize {
72 self.size / 2
73 }
74
75 fn number_of_partials_for_range_index(&self, index: usize) -> usize {
77 let cents_threshold = index as f64 * CENTS_PER_RANGE as f64;
79
80 let fraction_to_keep = (-cents_threshold / 1200.0).exp2();
83 (fraction_to_keep * self.max_number_of_partials() as f64) as usize
84 }
85
86 fn calculate_unnormalized_wave(
88 &self,
89 real: &[f32],
90 imag: &[f32],
91 frequency: f64,
92 sample_rate: f64,
93 num_partials: usize,
94 ) -> Wave {
95 let coefficients_length = real.len();
96 let mut num_partials = num_partials.min(coefficients_length);
97
98 let nyquist = 0.5 * sample_rate;
100 if !frequency.is_zero() {
101 num_partials = num_partials.min((nyquist / frequency) as usize);
102 }
103
104 let input_length = self.size / 2 + 1;
108 let mut complex_input = vec![Complex32::ZERO; input_length];
109
110 for (i, (a, b)) in zip(real, imag).take(num_partials).enumerate() {
111 complex_input[i] = Complex32::new(*a, -(*b));
112 }
113
114 let mut planner = RealFftPlanner::new();
115 let complex_to_real_fft = planner.plan_fft_inverse(self.size);
116 let mut real_output = complex_to_real_fft.make_output_vec();
117 if complex_to_real_fft
118 .process(&mut complex_input, &mut real_output)
119 .is_ok()
120 {
121 real_output
122 } else {
123 error!(
125 "Error calculating IFFT from periodic wave coefficients. By construction lengths of complex input and real output should be N/2 + 1 and N respectively"
126 );
127 Vec::with_capacity(self.size)
128 }
129 }
130
131 fn insert_wave(
132 &mut self,
133 real: &[f32],
134 imag: &[f32],
135 frequency: f64,
136 sample_rate: f64,
137 normalize: bool,
138 range_index: usize,
139 ) {
140 let mut wave = self.calculate_unnormalized_wave(
141 real,
142 imag,
143 frequency,
144 sample_rate,
145 self.number_of_partials_for_range_index(range_index),
146 );
147 let normalization_factor = if normalize {
148 self.normalization_factor.unwrap_or_else(|| {
150 wave.iter()
151 .map(|x| (*x as f64).abs())
152 .fold(0.0, |acc, f| f.max(acc))
153 })
154 } else {
155 2.0
160 };
161 if !normalization_factor.is_zero() {
162 wave = wave
163 .iter()
164 .map(|x| ((*x as f64) / normalization_factor) as f32)
165 .collect();
166 }
167 self.waves[range_index] = wave;
168 }
169
170 fn get_wave(&self, range_index: usize) -> Option<&Wave> {
171 self.waves.get(range_index)
172 }
173
174 fn has_wave(&self, range_index: usize) -> bool {
175 self.waves
176 .get(range_index)
177 .is_some_and(|wave| !wave.is_empty())
178 }
179}
180
181impl PeriodicWave {
182 pub fn new(options: PeriodicWaveOptions) -> Self {
183 let number_of_components = options.imag.len();
184 let size = if number_of_components <= FFT_MIN_SIZE {
185 FFT_MIN_SIZE
186 } else {
187 number_of_components.next_power_of_two().min(FFT_MAX_SIZE)
188 };
189 let number_of_waves = (3 * size.ilog2()) as usize;
191 let wavetable = Wavetable::new(size, number_of_waves);
192 Self {
193 imag: options.imag,
194 real: options.real,
195 normalize: !options.disable_normalization,
196 size,
197 wavetable,
198 }
199 }
200
201 fn get_or_insert_wave(&mut self, frequency: f64, sample_rate: f64) -> (&Wave, &Wave, f64) {
202 let frequency = frequency.abs();
204 let mut num_partials = self.wavetable.number_of_partials_for_range_index(0);
207 let nyquist = 0.5 * sample_rate;
208 if !frequency.is_zero() {
209 num_partials = num_partials.min((nyquist / frequency) as usize);
210 }
211 if num_partials > self.wavetable.max_number_of_partials_in_band_limited_table {
212 let number_of_waves = self.wavetable.waves.capacity();
213 let mut wavetable = Wavetable::new(self.size, number_of_waves);
214 wavetable.insert_wave(
217 &self.real,
218 &self.imag,
219 frequency,
220 sample_rate,
221 self.normalize,
222 0,
223 );
224 wavetable.max_number_of_partials_in_band_limited_table = num_partials;
225 self.wavetable = wavetable;
226 }
227
228 let number_of_waves = self.wavetable.waves.capacity();
229
230 let min_fundamental_frequency = sample_rate / (self.size as f64);
232 let ratio = if frequency.is_zero() {
233 0.5
234 } else {
235 frequency / min_fundamental_frequency
236 };
237 let cents_above_lowest_frequency = ratio.log2() * 1200.0;
238
239 let mut pitch_range = 1.0 + cents_above_lowest_frequency / CENTS_PER_RANGE as f64;
241 pitch_range = pitch_range.max(0.0);
242 pitch_range = pitch_range.min(self.wavetable.waves.capacity() as f64 - 1.0);
243
244 let higher_data_range_index = pitch_range as usize;
250 let lower_data_range_index = if higher_data_range_index < number_of_waves - 1 {
251 higher_data_range_index + 1
252 } else {
253 higher_data_range_index
254 };
255
256 if !self.wavetable.has_wave(lower_data_range_index) {
258 self.wavetable.insert_wave(
259 &self.real,
260 &self.imag,
261 frequency,
262 sample_rate,
263 self.normalize,
264 lower_data_range_index,
265 );
266 }
267 if !self.wavetable.has_wave(higher_data_range_index) {
268 self.wavetable.insert_wave(
269 &self.real,
270 &self.imag,
271 frequency,
272 sample_rate,
273 self.normalize,
274 higher_data_range_index,
275 );
276 }
277 let lower_wave_data = self
278 .wavetable
279 .get_wave(lower_data_range_index)
280 .expect("Range index is calculated to be within bounds");
281 let higher_wave_data = self
282 .wavetable
283 .get_wave(higher_data_range_index)
284 .expect("Range index is calculated to be within bounds");
285
286 let table_interpolation_factor = lower_data_range_index as f64 - pitch_range;
288 (
289 lower_wave_data,
290 higher_wave_data,
291 table_interpolation_factor,
292 )
293 }
294
295 pub(crate) fn calculate_waveform(
296 &mut self,
297 frequency: f64,
298 sample_rate: f64,
299 phase: f64,
300 ) -> f64 {
301 let position = phase / (2.0 * PI) * self.size as f64;
304 let position_floor = position.floor();
305 let position_interpolation_factor = position - position_floor;
306 let index_mask = self.size - 1;
307 let (lower_wave_data, higher_wave_data, table_interpolation_factor) =
308 self.get_or_insert_wave(frequency, sample_rate);
309 let position_index = position_floor as usize;
310 let lower_position_index = position_index & index_mask;
313 let higher_position_index = (lower_position_index + 1) & index_mask;
314 let lower = (1.0 - position_interpolation_factor) *
317 lower_wave_data[lower_position_index] as f64 +
318 position_interpolation_factor * lower_wave_data[higher_position_index] as f64;
319 let higher = (1.0 - position_interpolation_factor) *
320 higher_wave_data[lower_position_index] as f64 +
321 position_interpolation_factor * higher_wave_data[higher_position_index] as f64;
322 (1.0 - table_interpolation_factor) * lower + table_interpolation_factor * higher
324 }
325
326 pub(crate) fn generate_waveform_coefficients(oscillator_type: OscillatorType) -> PeriodicWave {
327 let mut periodic_wave = PeriodicWave::new(PeriodicWaveOptions::new(vec![], vec![], false));
328 periodic_wave.real = vec![0.0; periodic_wave.size / 2 + 1];
329 periodic_wave.imag = vec![0.0; periodic_wave.size / 2 + 1];
330 match oscillator_type {
331 OscillatorType::Sine => {
333 periodic_wave.imag[1] = 1.0;
334 },
335 OscillatorType::Square => {
336 for n in 1..periodic_wave.imag.len() {
337 periodic_wave.imag[n] =
338 (2.0 / (n as f32 * PI_32)) * (1.0 - (-1.0_f32).powi(n as i32));
339 }
340 },
341 OscillatorType::Sawtooth => {
342 for n in 1..periodic_wave.imag.len() {
343 periodic_wave.imag[n] =
344 (-1.0_f32).powi(n as i32 + 1) * 2.0 / (n as f32 * PI_32);
345 }
346 },
347 OscillatorType::Triangle => {
348 for n in 1..periodic_wave.imag.len() {
349 periodic_wave.imag[n] =
350 (8.0 * (n as f32 * PI_32 / 2.0).sin()) / (PI_32 * n as f32).powi(2);
351 }
352 },
353 OscillatorType::Custom(w) => {
354 return w;
355 },
356 }
357 periodic_wave
358 }
359}