wip audio synthesizer based on the rust crate cpal
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.
beeper/src/beep/osc.rs

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
}
}