rill_core_dsp/generators/
basic.rs1use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
4use crate::generators::{Generator, ModulatableGenerator, SyncableGenerator};
5use crate::vector::prelude::*;
6use rill_core::traits::{ActionContext, ProcessResult};
7use rill_core::Transcendental;
8use std::f32::consts::PI;
9
10#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum Waveform {
13 Sine,
15 Saw,
17 Square,
19 Triangle,
21 Pulse(f32), }
24
25impl Waveform {
26 pub fn name(&self) -> &'static str {
28 match self {
29 Waveform::Sine => "Sine",
30 Waveform::Saw => "Saw",
31 Waveform::Square => "Square",
32 Waveform::Triangle => "Triangle",
33 Waveform::Pulse(_) => "Pulse",
34 }
35 }
36
37 pub fn description(&self) -> &'static str {
39 match self {
40 Waveform::Sine => "Pure sine wave - single harmonic",
41 Waveform::Saw => "Sawtooth wave - all harmonics (1/n)",
42 Waveform::Square => "Square wave - odd harmonics (1/n)",
43 Waveform::Triangle => "Triangle wave - odd harmonics (1/n²)",
44 Waveform::Pulse(_) => "Pulse wave with variable width",
45 }
46 }
47}
48
49#[derive(Clone, Copy)]
57pub struct BasicOscillator<T: Transcendental> {
58 waveform: Waveform,
60 frequency: f32,
62 amplitude: ScalarVector1<T>,
64 phase: ScalarVector1<T>,
66 phase_inc: ScalarVector1<T>,
68 sample_rate: f32,
70 periods: u32,
72 fm_amount: ScalarVector1<T>,
74}
75
76impl<T: Transcendental> BasicOscillator<T> {
77 pub fn new(waveform: Waveform, frequency: f32, amplitude: T) -> Self {
84 let mut osc = Self {
85 waveform,
86 frequency,
87 amplitude: ScalarVector1::splat(amplitude),
88 phase: ScalarVector1::splat(T::ZERO),
89 phase_inc: ScalarVector1::splat(T::ZERO),
90 sample_rate: 44100.0,
91 periods: 0,
92 fm_amount: ScalarVector1::splat(T::ZERO),
93 };
94 osc.update_phase_inc();
95 osc
96 }
97
98 #[inline(always)]
100 fn update_phase_inc(&mut self) {
101 self.phase_inc = ScalarVector1::splat(T::from_f32(self.frequency / self.sample_rate));
102 }
103
104 #[inline(always)]
106 fn generate_sine(&self) -> ScalarVector1<T> {
107 let phase_rad = self.phase.mul(&ScalarVector1::splat(T::from_f32(2.0 * PI)));
108 phase_rad.sin().mul(&self.amplitude)
109 }
110
111 #[inline(always)]
113 fn generate_saw_raw(&self) -> ScalarVector1<T> {
114 self.phase
116 .mul(&ScalarVector1::splat(T::from_f32(2.0)))
117 .sub(&ScalarVector1::splat(T::from_f32(1.0)))
118 .mul(&self.amplitude)
119 }
120
121 #[inline(always)]
123 fn generate_saw_bandlimited(&mut self) -> ScalarVector1<T> {
124 let raw = self.generate_saw_raw();
125 let next_phase = self.phase.add(&self.phase_inc).extract(0);
127 let one = T::from_f32(1.0);
128
129 if next_phase >= one {
130 let one_vec = ScalarVector1::splat(one);
132 let t = (one_vec - self.phase) / self.phase_inc;
133 let blep =
135 t * ScalarVector1::splat(T::from_f32(2.0)) - ScalarVector1::splat(T::from_f32(1.0));
136 raw - blep * self.amplitude
137 } else {
138 raw
139 }
140 }
141
142 #[inline(always)]
144 fn generate_square(&self) -> ScalarVector1<T> {
145 let half = T::from_f32(0.5);
146 if self.phase.extract(0) < half {
147 self.amplitude
148 } else {
149 -self.amplitude
150 }
151 }
152
153 #[inline(always)]
155 fn generate_triangle(&self) -> ScalarVector1<T> {
156 let half = ScalarVector1::splat(T::from_f32(0.5));
158 let p = self.phase - half;
159 (p.abs() * ScalarVector1::splat(T::from_f32(4.0)) - ScalarVector1::splat(T::from_f32(1.0)))
160 * self.amplitude
161 }
162
163 #[inline(always)]
165 fn generate_pulse(&self, width: f32) -> ScalarVector1<T> {
166 let width_t = T::from_f32(width.clamp(0.01, 0.99));
167 if self.phase.extract(0) < width_t {
168 self.amplitude
169 } else {
170 -self.amplitude
171 }
172 }
173
174 pub(crate) fn generate(&mut self) -> ScalarVector1<T> {
176 let effective_inc = self.phase_inc + self.fm_amount;
178
179 let output_vec = match self.waveform {
181 Waveform::Sine => self.generate_sine(),
182 Waveform::Saw => self.generate_saw_bandlimited(),
183 Waveform::Square => self.generate_square(),
184 Waveform::Triangle => self.generate_triangle(),
185 Waveform::Pulse(width) => self.generate_pulse(width),
186 };
187
188 self.phase = self.phase + effective_inc;
190 let one = ScalarVector1::splat(T::from_f32(1.0));
191 if self.phase.extract(0) >= one.extract(0) {
192 self.phase = self.phase - one;
193 self.periods += 1;
194 }
195
196 output_vec
197 }
198
199 pub fn reset_phase(&mut self) {
201 self.phase = ScalarVector1::splat(T::ZERO);
202 self.periods = 0;
203 }
204
205 pub fn current_phase(&self) -> T {
207 self.phase.extract(0)
208 }
209
210 pub fn period_count(&self) -> u32 {
212 self.periods
213 }
214
215 pub fn set_pulse_width(&mut self, width: f32) {
217 if let Waveform::Pulse(_) = self.waveform {
218 self.waveform = Waveform::Pulse(width.clamp(0.01, 0.99));
219 }
220 }
221}
222
223impl<T: Transcendental> Algorithm<T> for BasicOscillator<T> {
226 fn init(&mut self, sample_rate: f32) {
227 self.sample_rate = sample_rate;
228 self.update_phase_inc();
229 self.phase = ScalarVector1::splat(T::ZERO);
230 self.periods = 0;
231 }
232
233 fn reset(&mut self) {
234 self.phase = ScalarVector1::splat(T::ZERO);
235 self.periods = 0;
236 self.fm_amount = ScalarVector1::splat(T::ZERO);
237 }
238
239 fn process(
240 &mut self,
241 input: Option<&[T]>,
242 output: &mut [T],
243 _ctx: &ActionContext,
244 ) -> ProcessResult<()> {
245 let input = input.unwrap_or(&[]);
246 for out in output.iter_mut() {
247 *out = self.generate().extract(0);
248 }
249 Ok(())
250 }
251
252 fn metadata(&self) -> AlgorithmMetadata {
253 AlgorithmMetadata {
254 name: self.waveform.name(),
255 category: AlgorithmCategory::Generator,
256 description: self.waveform.description(),
257 author: "Rill",
258 version: env!("CARGO_PKG_VERSION"),
259 }
260 }
261}
262
263impl<T: Transcendental> Generator<T> for BasicOscillator<T> {
266 fn phase(&self) -> T {
267 self.phase.extract(0)
268 }
269
270 fn set_phase(&mut self, phase: T) {
271 let one = T::from_f32(1.0);
272 let zero = T::ZERO;
273 self.phase = ScalarVector1::splat(if phase > one {
274 one
275 } else if phase < zero {
276 zero
277 } else {
278 phase
279 });
280 }
281
282 fn frequency(&self) -> f32 {
283 self.frequency
284 }
285
286 fn set_frequency(&mut self, freq: f32) {
287 self.frequency = freq.max(0.1).min(20000.0);
288 self.update_phase_inc();
289 }
290
291 fn amplitude(&self) -> T {
292 self.amplitude.extract(0)
293 }
294
295 fn set_amplitude(&mut self, amp: T) {
296 let one = T::from_f32(1.0);
297 let zero = T::ZERO;
298 self.amplitude = ScalarVector1::splat(if amp > one {
299 one
300 } else if amp < zero {
301 zero
302 } else {
303 amp
304 });
305 }
306}
307
308impl<T: Transcendental> SyncableGenerator<T> for BasicOscillator<T> {
311 fn sync(&mut self, reset: bool) {
312 if reset {
313 self.phase = ScalarVector1::splat(T::ZERO);
314 }
315 }
316
317 fn periods(&self) -> u32 {
318 self.periods
319 }
320}
321
322impl<T: Transcendental> ModulatableGenerator<T> for BasicOscillator<T> {
325 fn modulate_frequency(&mut self, amount: T) {
326 self.fm_amount = ScalarVector1::splat(amount);
327 }
328
329 fn modulation_index(&self) -> T {
330 self.fm_amount.extract(0)
331 }
332
333 fn set_modulation_index(&mut self, index: T) {
334 self.fm_amount = ScalarVector1::splat(index);
335 }
336}
337
338#[cfg(test)]
341mod tests {
342 use super::*;
343 use float_cmp::approx_eq;
344
345 #[test]
346 fn test_sine_oscillator() {
347 let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
348 osc.init(44100.0);
349
350 let mut output = [0.0f32; 1];
352 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
353 let ctx = rill_core::traits::ActionContext::new(&tick);
354 osc.process(None, &mut output, &ctx).unwrap();
355 let sample1 = output[0];
356 assert!(approx_eq!(f32, sample1, 0.0, epsilon = 1e-6));
357
358 osc.process(None, &mut output, &ctx).unwrap();
360 let sample2 = output[0];
361 assert!(sample2 != 0.0);
362 assert!(sample2 >= -0.5 && sample2 <= 0.5);
363 }
364
365 #[test]
366 fn test_saw_oscillator() {
367 let mut osc = BasicOscillator::<f32>::new(Waveform::Saw, 440.0, 0.5);
368 osc.init(44100.0);
369
370 let mut output = [0.0f32; 1];
371 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
372 let ctx = rill_core::traits::ActionContext::new(&tick);
373 osc.process(None, &mut output, &ctx).unwrap();
374 let sample = output[0];
375 assert!(sample >= -0.5 && sample <= 0.5);
376 }
377
378 #[test]
379 fn test_square_oscillator() {
380 let mut osc = BasicOscillator::<f32>::new(Waveform::Square, 440.0, 0.5);
381 osc.init(44100.0);
382
383 let mut output = [0.0f32; 1];
384 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
385 let ctx = rill_core::traits::ActionContext::new(&tick);
386 osc.process(None, &mut output, &ctx).unwrap();
387 let sample = output[0];
388 assert!(sample == 0.5 || sample == -0.5);
389 }
390
391 #[test]
392 fn test_triangle_oscillator() {
393 let mut osc = BasicOscillator::<f32>::new(Waveform::Triangle, 440.0, 0.5);
394 osc.init(44100.0);
395
396 let mut output = [0.0f32; 1];
397 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
398 let ctx = rill_core::traits::ActionContext::new(&tick);
399 osc.process(None, &mut output, &ctx).unwrap();
400 let sample = output[0];
401 assert!(sample >= -0.5 && sample <= 0.5);
402 }
403
404 #[test]
405 fn test_pulse_oscillator() {
406 let mut osc = BasicOscillator::<f32>::new(Waveform::Pulse(0.25), 440.0, 0.5);
407 osc.init(44100.0);
408
409 let mut output = [0.0f32; 1];
410 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
411 let ctx = rill_core::traits::ActionContext::new(&tick);
412 osc.process(None, &mut output, &ctx).unwrap();
413 let sample = output[0];
414 assert!(sample == 0.5); }
416
417 #[test]
418 fn test_frequency_change() {
419 let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
420 osc.init(44100.0);
421
422 assert_eq!(osc.frequency(), 440.0);
423
424 osc.set_frequency(880.0);
425 assert_eq!(osc.frequency(), 880.0);
426 }
427
428 #[test]
429 fn test_amplitude_change() {
430 let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
431 osc.init(44100.0);
432
433 assert_eq!(osc.amplitude(), 0.5);
434
435 osc.set_amplitude(0.8);
436 assert_eq!(osc.amplitude(), 0.8);
437 }
438
439 #[test]
440 fn test_phase_manipulation() {
441 let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 1.0);
442 osc.init(44100.0);
443
444 osc.set_phase(0.25); let mut output = [0.0f32; 1];
446 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
447 let ctx = rill_core::traits::ActionContext::new(&tick);
448 osc.process(None, &mut output, &ctx).unwrap();
449 let sample = output[0];
450 assert!(approx_eq!(f32, sample, 1.0, epsilon = 1e-4)); }
452
453 #[test]
454 fn test_fm_modulation() {
455 let mut osc = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 1.0);
456 osc.init(44100.0);
457
458 osc.modulate_frequency(0.5);
459 assert_eq!(osc.modulation_index(), 0.5);
460
461 let mut output = [0.0f32; 1];
463 let tick = rill_core::time::ClockTick::new(0, 1, 44100.0);
464 let ctx = rill_core::traits::ActionContext::new(&tick);
465 osc.process(None, &mut output, &ctx).unwrap();
466 let sample = output[0];
467 assert!(sample >= -1.0 && sample <= 1.0);
468 }
469
470 #[test]
471 fn test_clone_copy() {
472 let osc1 = BasicOscillator::<f32>::new(Waveform::Sine, 440.0, 0.5);
473 let osc2 = osc1; let osc3 = osc1.clone(); assert_eq!(osc1.frequency(), osc2.frequency());
477 assert_eq!(osc1.frequency(), osc3.frequency());
478 }
479}