use std::sync::Arc; use std::time::Duration; use parking_lot::RwLock; use crate::beep::tween::{Tween, GainScale}; use crate::beep::sample::StereoSample; #[derive(Clone,Debug)] pub struct HarmonicOscillator { /// Amplitude fader 0..1 pub(crate) gain: Tween, /// Frequency fader 1..22050 freq: Tween, /// Sample rate (Hz) sample_rate: f32, /// Phase increment per sample phase_step: f32, /// Phase 0-TAU phase: f32, /// Harmonic multiple pub harmonic: f32, } impl HarmonicOscillator { /// New oscillator with a frequency and amplitude pub fn new(amp: f32, n: f32) -> Self { let mut o = Self { gain: Tween { actual: amp, target: amp, min: 0.0, max: 1.0, step: None, sample_rate: 0.0, }, freq: Tween { actual: 440.0, target: 440.0, min: 1.0, max: 20000.0, step: None, sample_rate: 0.0, }, phase_step: 0.0, sample_rate: 0.0, phase: 0.0, harmonic: n, }; o.update_step(); o } pub(crate) fn set_sample_rate(&mut self, sample_rate : f32) { self.sample_rate = sample_rate; self.gain.sample_rate = sample_rate; self.freq.sample_rate = sample_rate; self.freq.max = sample_rate / 2.0; self.update_step(); } /// Update the step variable fn update_step(&mut self) { self.phase_step = (self.freq.actual / self.sample_rate) * std::f32::consts::TAU; } /// Get actual amplitude (0..1) pub fn get_amp(&self) -> f32 { self.gain.actual } /// Set amplitude (0..1), change at the end of the current waveform to reduce popping pub fn set_amp(&mut self, amp : f32) { self.gain.set_at_crossover(amp); } /// Set amplitude (0..1) immediate pub fn set_amp_imm(&mut self, amp : f32) { self.gain.set_immediate(amp); } /// Fade to amplitude (0..1) over a given time pub fn fade_amp(&mut self, amp : f32, time: Duration) { self.gain.fade(amp, time); } /// Get frequency value (1..22050) pub fn get_freq(&self) -> f32 { self.freq.actual } /// Set frequency (1..22050), change at the end of the current waveform to reduce popping pub fn set_freq(&mut self, freq : f32) { self.freq.set_at_crossover(freq * self.harmonic); self.update_step(); } /// Set frequency (1..22050) immediate pub fn set_freq_imm(&mut self, freq : f32) { self.freq.set_immediate(freq * self.harmonic); self.update_step(); } /// Fade to frequency (1..22050) over a given time pub fn fade_freq(&mut self, freq : f32, time: Duration) { self.freq.fade(freq * self.harmonic, time); self.update_step(); } /// Take a sample. Mutable to update faders. pub fn sample(&mut self) -> f32 { if self.freq.is_fading() { self.freq.tick(); self.update_step(); } let ph0 = self.phase; self.phase = (self.phase + self.phase_step) % std::f32::consts::TAU; // "zero crossing detection" if self.phase < ph0 { if self.gain.is_change_scheduled_at_crossover() { self.gain.actual = self.gain.target; } if self.freq.is_change_scheduled_at_crossover() { self.freq.actual = self.freq.target; } } self.phase.sin() * self.gain.actual } }