use super::signal::{Rack, Real, Signal};
use approx::relative_eq;
#[derive(Copy, Clone, Debug)]
pub struct ExpInterp {
low: Real,
mid: Real,
high: Real,
a: Real,
b: Real,
c: Real,
linear: bool,
}
impl ExpInterp {
pub fn new(low: Real, mid: Real, high: Real) -> Self {
let mut exp_interp = ExpInterp {
low,
mid,
high,
a: 0.0,
b: 0.0,
c: 0.0,
linear: true,
};
if relative_eq!(high - mid, mid - low) {
exp_interp
} else {
exp_interp.update(low, mid, high);
exp_interp
}
}
pub fn update(&mut self, low: Real, mid: Real, high: Real) {
if relative_eq!(high - mid, mid - low) {
self.low = low;
self.mid = mid;
self.high = high;
self.linear = true;
} else {
self.b = (mid - low) * (mid - low) / (high - 2.0 * mid + low);
self.a = low - self.b;
self.c = 2.0 * ((high - mid) / (mid - low)).ln();
self.linear = false;
}
}
pub fn interp(&self, x: Real) -> Real {
if self.linear {
self.low + (self.high - self.low) * x
} else {
self.a + self.b * (self.c * x).exp()
}
}
pub fn interp_inv(&self, y: Real) -> Real {
if self.linear {
(y - self.low) / (self.high - self.low)
} else {
((y - self.a) / self.b).ln() / self.c
}
}
}
pub fn signals<T>(module: &mut T, start: u32, end: u32, sample_rate: Real) -> Vec<(f32, f32)>
where
T: Signal,
{
let rack = Rack::new(vec![]);
let mut result = vec![];
for i in start..=end {
result.push((
i as f32 / sample_rate as f32,
module.signal(&rack, sample_rate) as f32,
));
}
result
}
#[derive(Clone)]
pub struct RingBuffer<T> {
buffer: Vec<T>,
pub read_pos: Real,
pub write_pos: usize,
}
impl<T> RingBuffer<T>
where
T: Clone + Default,
{
pub fn new(read_pos: Real, write_pos: usize) -> Self {
assert!(
read_pos.trunc() as usize <= write_pos,
"Read position must be <= write postion"
);
RingBuffer {
buffer: vec![Default::default(); write_pos + 3],
read_pos,
write_pos,
}
}
pub fn push(&mut self, v: T) {
let n = self.buffer.len();
self.write_pos = (self.write_pos + 1) % n;
self.read_pos = (self.read_pos + 1.0) % n as Real;
self.buffer[self.write_pos] = v;
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn resize(&mut self, size: usize) {
self.buffer.resize_with(size, Default::default);
}
pub fn set_read_pos(&mut self, rp: Real) {
self.read_pos = rp % self.buffer.len() as Real;
}
pub fn set_write_pos(&mut self, wp: usize) {
self.write_pos = wp % self.buffer.len();
}
}
impl<T> RingBuffer<T>
where
T: Copy + Default,
{
pub fn get(&self) -> T {
self.buffer[self.read_pos.trunc() as usize]
}
pub fn get_offset(&self, offset: i32) -> T {
let n = self.buffer.len() as i32;
let mut offset = offset;
while offset < 0 {
offset += n;
}
let i = (self.read_pos.trunc() as usize + offset as usize) % n as usize;
self.buffer[i]
}
}
impl RingBuffer<Real> {
pub fn get_linear(&self) -> Real {
let f = self.read_pos - self.read_pos.trunc();
(1.0 - f) * self.get() + f * self.get_offset(1)
}
pub fn get_cubic(&self) -> Real {
let v0 = self.get_offset(-1);
let v1 = self.get();
let v2 = self.get_offset(1);
let v3 = self.get_offset(2);
let f = self.read_pos - self.read_pos.trunc();
let a1 = 0.5 * (v2 - v0);
let a2 = v0 - 2.5 * v1 + 2.0 * v2 - 0.5 * v3;
let a3 = 0.5 * (v3 - v0) + 1.5 * (v1 - v2);
a3 * f * f * f + a2 * f * f + a1 * f + v1
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::relative_eq;
fn trunc4(x: Real) -> i32 {
(10_000.0 * x + 0.5) as i32
}
#[test]
fn linear_interp() {
let ie = ExpInterp::new(0.0, 0.5, 1.0);
assert!(relative_eq!(ie.interp(0.0), 0.0));
assert!(relative_eq!(ie.interp(0.5), 0.5));
assert!(relative_eq!(ie.interp(0.75), 0.75));
assert!(relative_eq!(ie.interp(1.0), 1.0));
}
#[test]
fn exp_interp() {
let ie = ExpInterp::new(0.0, 0.4, 1.0);
let result = trunc4(ie.interp(0.0));
assert_eq!(result, 0, "interp returned {}, epxected 0", result);
let result = trunc4(ie.interp(0.5));
assert_eq!(result, 4_000, "interp returned {}, epxected 4,000", result);
let result = trunc4(ie.interp(0.75));
assert_eq!(result, 6_697, "interp returned {}, epxected 6,697", result);
let result = trunc4(ie.interp(1.0));
assert_eq!(
result, 10_000,
"interp returned {}, epxected 10,1000",
result
);
}
#[test]
fn linear_interp_inv() {
let ie = ExpInterp::new(0.0, 0.5, 1.0);
assert!(relative_eq!(ie.interp_inv(0.0), 0.0));
assert!(relative_eq!(ie.interp_inv(0.5), 0.5));
assert!(relative_eq!(ie.interp_inv(0.75), 0.75));
assert!(relative_eq!(ie.interp_inv(1.0), 1.0));
}
#[test]
fn exp_interp_inv() {
let ie = ExpInterp::new(0.0, 0.4, 1.0);
let result = trunc4(ie.interp_inv(0.0));
assert_eq!(result, 0, "interp returned {}, epxected 0", result);
let result = trunc4(ie.interp_inv(0.4));
assert_eq!(result, 5_000, "interp returned {}, epxected 4,000", result);
let result = trunc4(ie.interp_inv(0.6697));
assert_eq!(result, 7_500, "interp returned {}, epxected 7,500", result);
let result = trunc4(ie.interp_inv(1.0));
assert_eq!(
result, 10_000,
"interp returned {}, epxected 10,1000",
result
);
}
#[test]
fn ring_buffer() {
let mut rb = RingBuffer::<Real>::new(0.5, 5);
let result = rb.get();
assert_eq!(result, 0.0, "get returned {}, expected 0.0", result);
for i in 0..=6 {
rb.push(i as Real);
}
let result = rb.get();
assert_eq!(result, 1.0, "get returned {}, expected 0.0", result);
let result = rb.get_linear();
assert_eq!(result, 1.5, "get_linear returned {}, expected 0.0", result);
let result = rb.get_cubic();
assert_eq!(result, 1.5, "get_cubic returned {}, expected 0.0", result);
}
#[test]
fn ring_buffer_resize() {
let mut rb = RingBuffer::<Real>::new(0.5, 5);
rb.resize(10);
for i in 0..=6 {
rb.push(i as Real);
}
let result = rb.get_linear();
assert_eq!(result, 1.5, "get_linear returned {}, expected 0.0", result);
let result = rb.get_cubic();
assert_eq!(result, 1.5, "get_cubic returned {}, expected 0.0", result);
}
}