use super::Generator;
use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
use crate::math::{lerp, Transcendental};
use crate::vector::{ScalarVector1, Vector};
use rill_core::traits::{ActionContext, ProcessResult};
pub struct WavetableOscillator<T: Transcendental, const SIZE: usize> {
table: [T; SIZE],
frequency: f32,
amplitude: ScalarVector1<T>,
phase: ScalarVector1<T>,
phase_inc: ScalarVector1<T>,
cubic_interp: bool,
sample_rate: f32,
}
impl<T: Transcendental, const SIZE: usize> WavetableOscillator<T, SIZE> {
pub fn new(table: [T; SIZE], frequency: f32) -> Self {
let mut osc = Self {
table,
frequency,
amplitude: ScalarVector1::splat(T::from_f32(1.0)),
phase: ScalarVector1::splat(T::ZERO),
phase_inc: ScalarVector1::splat(T::ZERO),
cubic_interp: false,
sample_rate: 44100.0,
};
osc.update_phase_inc();
osc
}
pub fn sine(frequency: f32) -> Self {
let mut table = [T::ZERO; SIZE];
for i in 0..SIZE {
let phase = (i as f32 / SIZE as f32) * 2.0 * std::f32::consts::PI;
table[i] = T::from_f32(phase.sin());
}
Self::new(table, frequency)
}
pub fn saw(frequency: f32) -> Self {
let mut table = [T::ZERO; SIZE];
for i in 0..SIZE {
table[i] = T::from_f32(2.0 * i as f32 / SIZE as f32 - 1.0);
}
Self::new(table, frequency)
}
fn update_phase_inc(&mut self) {
self.phase_inc = ScalarVector1::splat(
T::from_f32(self.frequency / self.sample_rate).mul(T::from_f32(SIZE as f32)),
);
}
#[inline(always)]
fn read_linear(&self, idx: T) -> T {
let idx_f = idx.to_f32();
let i0 = idx_f.floor() as usize % SIZE;
let i1 = (i0 + 1) % SIZE;
let frac = T::from_f32(idx_f.fract());
lerp(self.table[i0], self.table[i1], frac)
}
#[inline(always)]
fn read_cubic(&self, idx: T) -> T {
let idx_f = idx.to_f32();
let i = idx_f.floor() as usize;
let i0 = (i + SIZE - 1) % SIZE;
let i1 = i % SIZE;
let i2 = (i + 1) % SIZE;
let i3 = (i + 2) % SIZE;
let frac = T::from_f32(idx_f.fract());
let c0 = self.table[i1];
let c1 = self.table[i2].sub(self.table[i0]).mul(T::from_f32(0.5));
let c2 = self.table[i0]
.sub(self.table[i1])
.mul(T::from_f32(1.5))
.add(self.table[i2].sub(self.table[i3]).mul(T::from_f32(0.5)));
let c3 = self.table[i2]
.sub(self.table[i1])
.mul(T::from_f32(0.5))
.add(self.table[i3].sub(self.table[i0]).mul(T::from_f32(0.5)))
.sub(self.table[i1].sub(self.table[i2]).mul(T::from_f32(1.5)));
let f2 = frac.mul(frac);
let f3 = f2.mul(frac);
c0.add(c1.mul(frac)).add(c2.mul(f2)).add(c3.mul(f3))
}
}
impl<T: Transcendental, const SIZE: usize> Algorithm<T> for WavetableOscillator<T, SIZE> {
fn init(&mut self, sample_rate: f32) {
self.sample_rate = sample_rate;
self.update_phase_inc();
self.phase = ScalarVector1::splat(T::ZERO);
}
fn reset(&mut self) {
self.phase = ScalarVector1::splat(T::ZERO);
}
fn process(&mut self, input: Option<&[T]>, output: &mut [T], _ctx: &ActionContext) -> ProcessResult<()> {
let input = input.unwrap_or(&[]);
let len = input.len().min(output.len());
for i in 0..len {
let _ = input[i];
let sample = if self.cubic_interp {
self.read_cubic(self.phase.extract(0))
} else {
self.read_linear(self.phase.extract(0))
}
.mul(self.amplitude.extract(0));
output[i] = sample;
self.phase = self.phase + self.phase_inc;
while self.phase.extract(0).to_f32() >= SIZE as f32 {
self.phase = self.phase - ScalarVector1::splat(T::from_f32(SIZE as f32));
}
}
Ok(())
}
fn metadata(&self) -> AlgorithmMetadata {
AlgorithmMetadata {
name: "Wavetable Oscillator",
category: AlgorithmCategory::Generator,
description: "Wavetable oscillator with interpolation".to_string(),
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
}
}
}
impl<T: Transcendental, const SIZE: usize> Generator<T> for WavetableOscillator<T, SIZE> {
fn phase(&self) -> T {
self.phase.extract(0).div(T::from_f32(SIZE as f32))
}
fn set_phase(&mut self, phase: T) {
self.phase = ScalarVector1::splat(
phase
.mul(T::from_f32(SIZE as f32))
.clamp(T::ZERO, T::from_f32(SIZE as f32)),
);
}
fn frequency(&self) -> f32 {
self.frequency
}
fn set_frequency(&mut self, freq: f32) {
self.frequency = freq;
self.update_phase_inc();
}
fn amplitude(&self) -> T {
self.amplitude.extract(0)
}
fn set_amplitude(&mut self, amp: T) {
self.amplitude = ScalarVector1::splat(amp.clamp(T::ZERO, T::from_f32(1.0)));
}
}