#[cfg(test)]
mod tests;
use std::f32::consts::TAU;
use crate::{Oscillator, Waveform};
pub struct PolyBlepOsc {
phase: f32,
phase_increment: f32,
sample_rate: f32,
waveform: Waveform,
}
impl PolyBlepOsc {
pub fn new(sample_rate: f32, frequency: f32, waveform: Waveform) -> Self {
Self {
phase: 0.0,
phase_increment: frequency / sample_rate,
sample_rate,
waveform,
}
}
pub fn poly_blep(&self, phase: f32) -> f32 {
if phase < self.phase_increment {
let t = phase / self.phase_increment;
return 2.0 * t - t * t - 1.0;
}
if phase > 1.0 - self.phase_increment {
let t = (phase - 1.0) / self.phase_increment;
return t * t + 2.0 * t + 1.0;
}
0.0
}
}
impl Oscillator for PolyBlepOsc {
fn set_frequency(&mut self, hz: f32) {
self.phase_increment = hz / self.sample_rate
}
fn set_phase(&mut self, phase: f32) {
self.phase = phase.fract();
}
fn reset(&mut self) {
self.phase = 0.0;
}
fn next_sample(&mut self) -> f32 {
self.phase += self.phase_increment;
if self.phase >= 1.0 {
self.phase -= 1.0;
}
match self.waveform {
Waveform::Sine => (self.phase * TAU).sin(),
Waveform::Saw => {
let naive = 2.0 * self.phase - 1.0;
naive - self.poly_blep(self.phase)
}
Waveform::Square => {
let naive = if self.phase < 0.5 { 1.0 } else { -1.0 };
naive + self.poly_blep(self.phase) - self.poly_blep((self.phase + 0.5).fract())
}
Waveform::Triangle => {
if self.phase < 0.5 {
4.0 * self.phase - 1.0
} else {
-4.0 * self.phase + 3.0
}
}
}
}
}
impl Iterator for PolyBlepOsc {
type Item = f32;
fn next(&mut self) -> Option<f32> {
Some(self.next_sample())
}
}