use crate::Sample;
use crate::xorrng::XOrShift32Rng;
use std::f64::consts::PI;
pub const TABLE_POWER: u32 = 14;
pub const TABLE_SIZE: usize = 2_usize.pow(TABLE_POWER);
pub const TABLE_HIGH_MASK: u32 = TABLE_SIZE as u32 - 1;
pub const FRACTIONAL_PART: u32 = 65536;
#[derive(Debug, Clone)]
pub struct Wavetable {
buffer: Vec<Sample>, diff_buffer: Vec<Sample>, }
impl Default for Wavetable {
fn default() -> Self {
let buffer = vec![0.0; TABLE_SIZE];
let diff_buffer = vec![0.0; TABLE_SIZE];
Wavetable {
buffer,
diff_buffer,
}
}
}
impl Wavetable {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn update_diff_buffer(&mut self) {
let mut diff_buffer = vec![0.0; self.buffer.len()];
for (i, diff) in diff_buffer.iter_mut().enumerate() {
*diff = self.buffer[(i + 1) % self.buffer.len()] - self.buffer[i];
}
assert_eq!(self.buffer[1] - self.buffer[0], diff_buffer[0]);
assert_eq!(
self.buffer[0] - self.buffer.iter().last().unwrap(),
*diff_buffer.iter().last().unwrap()
);
self.diff_buffer = diff_buffer;
}
pub fn from_buffer(buffer: Vec<Sample>) -> Result<Self, String> {
if buffer.len() != TABLE_SIZE {
return Err(format!(
"Invalid size buffer for a wavetable: {}. Wavetables must be of size {}",
buffer.len(),
TABLE_SIZE,
));
}
let mut w = Self {
buffer,
..Default::default()
};
w.update_diff_buffer();
Ok(w)
}
#[must_use]
pub fn from_closure<F>(f: F) -> Self
where
F: FnOnce(&mut [Sample]),
{
let mut w = Self::default();
f(&mut w.buffer);
w.update_diff_buffer();
w
}
#[must_use]
pub fn sine() -> Self {
let wavetable_size = TABLE_SIZE;
let mut wt = Wavetable::new();
for i in 0..wavetable_size {
wt.buffer[i] = ((i as f64 / TABLE_SIZE as f64) * PI * 2.0).sin() as Sample;
}
wt.update_diff_buffer();
wt
}
#[must_use]
pub fn cosine() -> Self {
let wavetable_size = TABLE_SIZE;
let mut wt = Wavetable::new();
for i in 0..wavetable_size {
wt.buffer[i] = ((i as f64 / TABLE_SIZE as f64) * PI * 2.0).cos() as Sample;
}
wt.update_diff_buffer();
wt
}
#[must_use]
pub fn aliasing_saw() -> Self {
let wavetable_size = TABLE_SIZE;
let mut wt = Wavetable::new();
let per_sample = 2.0 / wavetable_size as Sample;
for i in 0..wavetable_size {
wt.buffer[i] = -1. + per_sample * i as Sample;
}
wt.update_diff_buffer();
wt
}
#[must_use]
pub fn hann_window() -> Self {
let mut wt = Wavetable::new();
wt.fill(0.5);
wt.add_sine(1.0, 0.5, -0.5 * PI as Sample);
wt.update_diff_buffer();
wt
}
#[must_use]
pub fn hamming_window() -> Self {
let mut wt = Wavetable::new();
wt.fill(0.53836);
wt.add_sine(1.0, 0.46164, -0.5 * PI as Sample);
wt.update_diff_buffer();
wt
}
#[must_use]
pub fn sine_window() -> Self {
let mut wt = Wavetable::new();
wt.add_sine(0.5, 1.0, 0.0);
wt.update_diff_buffer();
wt
}
pub fn fill(&mut self, value: Sample) {
for sample in &mut self.buffer {
*sample = value;
}
self.update_diff_buffer();
}
pub fn add_sine(&mut self, freq: Sample, amplitude: Sample, phase: Sample) {
let step = (freq * PI as Sample * 2.0) / TABLE_SIZE as Sample;
let mut phase = phase;
for sample in &mut self.buffer {
*sample += phase.sin() * amplitude;
phase += step;
}
self.update_diff_buffer();
}
pub fn fill_sine(&mut self, num_harmonics: usize, freq: Sample) {
for n in 0..num_harmonics {
let start_phase = 0.0;
let harmonic_amp = match n {
0 => 1.0,
_ => ((num_harmonics - n) as Sample / (num_harmonics) as Sample) * 0.5,
};
for i in 0..TABLE_SIZE {
self.buffer[i] += ((i as Sample / TABLE_SIZE as Sample)
* PI as Sample
* 2.0
* freq
* (n + 1) as Sample
+ start_phase)
.sin()
* harmonic_amp;
}
}
self.update_diff_buffer();
}
pub fn add_aliasing_saw(&mut self, num_harmonics: usize, amp: Sample) {
for i in 0..num_harmonics {
let start_phase = 0.0;
let harmonic_amp = 1.0 / ((i + 1) as Sample * PI as Sample);
for k in 0..self.buffer.len() {
self.buffer[k] += ((k as Sample / self.buffer.len() as Sample
* PI as Sample
* 2.0
* (i + 1) as Sample
+ start_phase)
.sin()
* harmonic_amp)
* amp;
}
}
self.update_diff_buffer();
}
pub fn add_odd_harmonics(&mut self, num_harmonics: usize, amp_falloff: Sample) {
for i in 0..num_harmonics {
let start_phase = match i {
0 => 0.0,
_ => (-1.0 as Sample).powi(i as i32 + 2),
};
let harmonic_amp = 1.0 / ((i * 2 + 1) as Sample).powf(amp_falloff);
for k in 0..self.buffer.len() {
self.buffer[k] += (k as Sample / self.buffer.len() as Sample
* PI as Sample
* 2.0
* ((i * 2) as Sample + 1.0)
+ start_phase)
.sin()
* harmonic_amp;
}
}
self.update_diff_buffer();
}
pub fn add_noise(&mut self, probability: f64, seed: u32) {
let mut xorrng = XOrShift32Rng::new(seed);
for sample in &mut self.buffer {
if xorrng.gen_f64() > probability {
*sample += xorrng.gen_f32() as Sample - 0.5;
if *sample > 1.0 {
*sample -= 1.0;
}
if *sample < -1.0 {
*sample += 1.0;
}
}
}
self.update_diff_buffer();
}
pub fn multiply(&mut self, mult: Sample) {
for sample in &mut self.buffer {
*sample *= mult;
}
self.update_diff_buffer();
}
pub fn normalize(&mut self) {
let mut loudest_sample = 0.0;
for sample in &self.buffer {
if sample.abs() > loudest_sample {
loudest_sample = sample.abs();
}
}
let scaler = 1.0 / loudest_sample;
for sample in &mut self.buffer {
*sample *= scaler;
}
self.update_diff_buffer();
}
#[inline]
#[must_use]
pub fn get_linear_interp(&self, phase: WavetablePhase) -> Sample {
let index = phase.integer_component();
let mix = phase.fractional_component_f32() as Sample;
self.buffer[index] + self.diff_buffer[index] * mix
}
#[inline]
#[must_use]
pub fn get(&self, phase: WavetablePhase) -> Sample {
unsafe { *self.buffer.get_unchecked(phase.integer_component()) }
}
}
#[derive(Debug, Clone, Copy)]
pub struct WavetablePhase(pub u32);
impl WavetablePhase {
#[must_use]
#[inline]
pub fn integer_component(&self) -> usize {
((self.0 >> 16) & TABLE_HIGH_MASK) as usize
}
#[must_use]
#[inline]
pub fn fractional_component(&self) -> u32 {
const FRACTIONAL_MASK: u32 = u16::MAX as u32;
self.0 & FRACTIONAL_MASK
}
#[must_use]
#[inline]
pub fn fractional_component_f32(&self) -> f32 {
const FRACTIONAL_MASK: u32 = u16::MAX as u32;
(self.0 & FRACTIONAL_MASK) as f32 / FRACTIONAL_MASK as f32
}
#[inline]
pub fn increase(&mut self, add: u32) {
self.0 = self.0.wrapping_add(add);
}
}
pub struct PhaseF32(pub f32);
#[allow(missing_docs)]
impl PhaseF32 {
#[inline]
pub fn index_mix(&self) -> (usize, f32) {
const TABLE_SIZE_F32: f32 = TABLE_SIZE as f32;
let value = self.0 * TABLE_SIZE_F32;
(value as usize, value.fract())
}
#[inline]
pub fn increase(&mut self, add: f32) {
self.0 += add;
while self.0 >= 1.0 {
self.0 -= 1.0;
}
}
}