use randomize::{formulas, PCG32};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{cell::RefCell, f32::consts::PI};
const PI2: f32 = PI * 2.0;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum DutyCycle {
Eight,
Quarter,
Third,
Half,
}
impl DutyCycle {
pub fn to_frac(self) -> f32 {
match self {
DutyCycle::Eight => 0.125,
DutyCycle::Quarter => 0.25,
DutyCycle::Third => 0.33,
DutyCycle::Half => 0.5,
}
}
}
impl Default for DutyCycle {
fn default() -> Self {
DutyCycle::Half
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum OscillatorType {
Sine,
Saw,
Triangle,
Square,
Noise,
}
impl OscillatorType {
pub(crate) fn build_lut(
self,
frequency: usize,
duty_cycle: DutyCycle,
sample_rate: usize,
) -> Vec<f32> {
let buffer_size = sample_rate * 2;
match self {
OscillatorType::Sine => {
let mult = frequency as f32 * PI2 / sample_rate as f32;
(0..buffer_size)
.map(|index| (index as f32 * mult).sin())
.collect()
}
OscillatorType::Saw => (0..buffer_size)
.map(|index| {
1.0 - ((index as f32 / sample_rate as f32 * frequency as f32) % 1.0) * 2.0
})
.collect(),
OscillatorType::Triangle => (0..buffer_size)
.map(|index| {
let slope = (index as f32 / sample_rate as f32 * frequency as f32) % 1.0 * 2.0;
if slope < 1.0 {
-1.0 + slope * 2.0
} else {
3.0 - slope * 2.0
}
})
.collect(),
OscillatorType::Square => (0..buffer_size)
.map(|index| {
if (index as f32 / sample_rate as f32 * frequency as f32) % 1.0
< duty_cycle.to_frac()
{
1.0
} else {
-1.0
}
})
.collect(),
OscillatorType::Noise => {
let mut pcg = PCG32::seed(frequency as u64, 5);
(0..buffer_size)
.map(|_| formulas::f32_closed_neg_pos(pcg.next_u32()))
.collect()
}
}
}
}
#[derive(Debug)]
pub(crate) struct Oscillator {
lut: RefCell<Vec<f32>>,
sample_rate: usize,
}
impl Oscillator {
pub(crate) fn new(lut: RefCell<Vec<f32>>, sample_rate: usize) -> Self {
Self { lut, sample_rate }
}
pub(crate) fn generate(&mut self, output: &mut [f32], offset: usize) {
let rotating_index = offset % self.sample_rate;
output
.iter_mut()
.zip(self.lut.borrow()[rotating_index..].iter())
.for_each(|(old, new)| *old += *new);
}
}