You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.6 KiB
136 lines
3.6 KiB
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
|
|
}
|
|
}
|
|
|
|
|
|
|