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/instrument.rs

145 lines
4.1 KiB

use crate::beep::osc::HarmonicOscillator;
use crate::beep::tween::{Tween, GainScale};
use std::time::Duration;
use crate::beep::sample::StereoSample;
use crate::beep::hack::StableClamp;
/// Waveform generator with multiple frequencies / waveforms
#[derive(Clone,Debug)]
pub struct Instrument {
/// Waveforms to combine to produce the final sound
pub waveforms: Vec<HarmonicOscillator>,
/// Balance fader -1..1
balance: Tween,
/// 0-1 left gain
pub(crate) ratio_left : f32,
/// 0-1 right gain
pub(crate) ratio_right : f32,
/// Master gain
gain: Tween,
}
impl Instrument {
pub fn new(mut waveforms: Vec<HarmonicOscillator>, sample_rate: f32) -> Self {
for (n, o) in waveforms.iter_mut().enumerate() {
o.set_sample_rate(sample_rate);
o.set_freq_imm(sample_rate * (n as f32 + 1.0));
}
let mut o = Self {
waveforms,
balance: Tween {
actual: 0.0,
target: 0.0,
min: -1.0,
max: 1.0,
step: None,
sample_rate
},
ratio_left: 0.7,
ratio_right: 0.7,
gain: Tween {
actual: 0.0,
target: 0.0,
step: None,
min: 0.0,
max: 1.0,
sample_rate
}
};
o.update_side_ratios();
o
}
/// Update the pre-computed channel gain variables
pub fn update_side_ratios(&mut self) {
let angle = ((1.0 + self.balance.actual) / 2.0) * std::f32::consts::FRAC_PI_2;
self.ratio_left = angle.sin();
self.ratio_right = angle.cos();
}
/// Get amplitude
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 amplitude over a given time
pub fn fade_amp(&mut self, amp : f32, time: Duration) {
self.gain.fade(amp, time);
}
/// Get balance value (-1..1)
pub fn get_balance(&self) -> f32 {
self.balance.actual
}
/// Set balance (-1..1), change at the end of the current waveform to reduce popping
pub fn set_balance(&mut self, balance : f32) {
self.balance.set_at_crossover(balance);
}
/// Set balance (-1..1) immediate
pub fn set_balance_imm(&mut self, balance : f32) {
self.balance.set_immediate(balance);
}
/// Fade to balance (-1..1) over a given time
pub fn fade_balance(&mut self, balance : f32, time: Duration) {
self.balance.fade(balance, time);
}
pub fn get_peak_amplitude(&self) -> f32 {
self.waveforms.iter()
.fold((0.0), |acc, item| acc + item.gain.actual)
}
/// Get base frequency value (1..22050)
pub fn get_freq(&self) -> f32 {
self.waveforms[0].get_freq() // TODO error checking
}
/// Set base and harmonics frequency (1..22050), change at the end of the current waveform to reduce popping
pub fn set_freq(&mut self, freq : f32) {
let mut f = freq;
self.waveforms.iter_mut().for_each(|w| {
w.set_freq(f);
});
}
/// Set base and harmonics frequency (1..22050) immediate
pub fn set_freq_imm(&mut self, freq : f32) {
let mut f = freq;
self.waveforms.iter_mut().for_each(|w| {
w.set_freq_imm(f);
});
}
/// Fade base and harmonics to frequency (1..22050) over a given time
pub fn fade_freq(&mut self, freq : f32, time: Duration) {
let mut f = freq;
self.waveforms.iter_mut().for_each(|w| {
w.fade_freq(f, time);
});
}
/// Get a raw sample (with panning)
pub fn sample(&mut self) -> f32 {
self.balance.tick();
self.gain.tick();
self.waveforms.iter_mut()
.fold(0.0, |mut acc, item| {
acc + item.sample()
}) * self.gain.actual
}
}