use std::f32::consts::TAU;
use super::PolyBlepOsc;
use crate::{Oscillator, Waveform, naive::NaiveOsc};
const EPSILON: f32 = 1e-6;
fn approx_eq(a: f32, b: f32) -> bool {
(a - b).abs() < EPSILON
}
#[test]
fn test_poly_blep_zero_outside_discontinuity() {
let osc = PolyBlepOsc::new(10.0, 1.0, Waveform::Saw);
assert!(approx_eq(osc.poly_blep(0.5), 0.0));
assert!(approx_eq(osc.poly_blep(0.3), 0.0));
assert!(approx_eq(osc.poly_blep(0.7), 0.0));
}
#[test]
fn test_poly_blep_nonzero_near_discontinuity() {
let osc = PolyBlepOsc::new(10.0, 1.0, Waveform::Saw);
let blep_after = osc.poly_blep(0.05);
assert!(blep_after < 0.0);
let blep_before = osc.poly_blep(0.95);
assert!(blep_before > 0.0); }
#[test]
fn test_poly_blep_boundary_values() {
let osc = PolyBlepOsc::new(10.0, 1.0, Waveform::Saw);
assert!(approx_eq(osc.poly_blep(0.0), -1.0));
assert!(approx_eq(osc.poly_blep(0.1), 0.0));
assert!(approx_eq(osc.poly_blep(0.9), 0.0));
}
#[test]
fn test_poly_blep_midpoint_values() {
let osc = PolyBlepOsc::new(10.0, 1.0, Waveform::Saw);
assert!(approx_eq(osc.poly_blep(0.05), -0.25));
assert!(approx_eq(osc.poly_blep(0.95), 0.25));
}
#[test]
fn test_sine_at_known_phases() {
let mut osc = PolyBlepOsc::new(4.0, 1.0, Waveform::Sine);
assert!(approx_eq(osc.next_sample(), 1.0)); assert!(approx_eq(osc.next_sample(), 0.0)); assert!(approx_eq(osc.next_sample(), -1.0)); assert!(approx_eq(osc.next_sample(), 0.0)); }
#[test]
fn test_sine_matches_naive() {
let mut poly = PolyBlepOsc::new(100.0, 5.0, Waveform::Sine);
let mut naive = NaiveOsc::new(100.0, 5.0, Waveform::Sine);
for _ in 0..100 {
assert!(approx_eq(poly.next_sample(), naive.next_sample()));
}
}
#[test]
fn test_saw_output_range() {
let mut osc = PolyBlepOsc::new(44100.0, 440.0, Waveform::Saw);
for _ in 0..1000 {
let sample = osc.next_sample();
assert!(sample >= -1.0 && sample <= 1.0);
}
}
#[test]
fn test_saw_smooths_discontinuity() {
let mut poly = PolyBlepOsc::new(16.0, 1.0, Waveform::Saw);
let mut naive = NaiveOsc::new(16.0, 1.0, Waveform::Saw);
let poly_samples: Vec<f32> = (0..16).map(|_| poly.next_sample()).collect();
let naive_samples: Vec<f32> = (0..16).map(|_| naive.next_sample()).collect();
assert!((poly_samples[8] - naive_samples[8]).abs() < 0.05);
assert!((poly_samples[15] - naive_samples[15]).abs() > 0.1);
}
#[test]
fn test_saw_ramp_direction() {
let mut osc = PolyBlepOsc::new(100.0, 1.0, Waveform::Saw);
for _ in 0..10 {
osc.next_sample();
}
let s1 = osc.next_sample();
let s2 = osc.next_sample();
let s3 = osc.next_sample();
assert!(s2 > s1);
assert!(s3 > s2);
}
#[test]
fn test_square_output_range() {
let mut osc = PolyBlepOsc::new(44100.0, 440.0, Waveform::Square);
for _ in 0..1000 {
let sample = osc.next_sample();
assert!(sample >= -1.0 && sample <= 1.0);
}
}
#[test]
fn test_square_smooths_both_edges() {
let mut poly = PolyBlepOsc::new(20.0, 1.0, Waveform::Square);
let mut naive = NaiveOsc::new(20.0, 1.0, Waveform::Square);
let poly_samples: Vec<f32> = (0..20).map(|_| poly.next_sample()).collect();
let naive_samples: Vec<f32> = (0..20).map(|_| naive.next_sample()).collect();
assert!((poly_samples[19] - naive_samples[19]).abs() > 0.1);
assert!((poly_samples[9] - naive_samples[9]).abs() > 0.1);
}
#[test]
fn test_square_high_low_regions() {
let mut osc = PolyBlepOsc::new(100.0, 1.0, Waveform::Square);
let samples: Vec<f32> = (0..100).map(|_| osc.next_sample()).collect();
let high_count = samples.iter().filter(|&&s| s > 0.5).count();
let low_count = samples.iter().filter(|&&s| s < -0.5).count();
assert!(high_count > 30 && high_count < 70);
assert!(low_count > 30 && low_count < 70);
}
#[test]
fn test_triangle_peaks_at_midpoint() {
let mut osc = PolyBlepOsc::new(4.0, 1.0, Waveform::Triangle);
assert!(approx_eq(osc.next_sample(), 0.0)); assert!(approx_eq(osc.next_sample(), 1.0)); assert!(approx_eq(osc.next_sample(), 0.0)); assert!(approx_eq(osc.next_sample(), -1.0)); }
#[test]
fn test_triangle_matches_naive() {
let mut poly = PolyBlepOsc::new(100.0, 5.0, Waveform::Triangle);
let mut naive = NaiveOsc::new(100.0, 5.0, Waveform::Triangle);
for _ in 0..100 {
assert!(approx_eq(poly.next_sample(), naive.next_sample()));
}
}
#[test]
fn test_set_frequency_changes_increment() {
let mut osc = PolyBlepOsc::new(100.0, 10.0, Waveform::Sine);
osc.next_sample();
osc.set_frequency(20.0); osc.next_sample(); osc.next_sample();
assert!(approx_eq(osc.next_sample(), (0.7 * TAU).sin()));
}
#[test]
fn test_set_phase() {
let mut osc = PolyBlepOsc::new(4.0, 1.0, Waveform::Sine);
osc.set_phase(0.25);
assert!(approx_eq(osc.next_sample(), 0.0)); }
#[test]
fn test_reset_zeros_phase() {
let mut osc = PolyBlepOsc::new(4.0, 1.0, Waveform::Sine);
osc.next_sample();
osc.next_sample();
osc.reset();
assert!(approx_eq(osc.next_sample(), 1.0)); }
#[test]
fn test_fill_buffer() {
let mut osc = PolyBlepOsc::new(4.0, 1.0, Waveform::Sine);
let mut buffer = [0.0f32; 4];
osc.fill(&mut buffer);
assert!(approx_eq(buffer[0], 1.0));
assert!(approx_eq(buffer[1], 0.0));
assert!(approx_eq(buffer[2], -1.0));
assert!(approx_eq(buffer[3], 0.0));
}
#[test]
fn test_iterator() {
let osc = PolyBlepOsc::new(4.0, 1.0, Waveform::Sine);
let samples: Vec<f32> = osc.take(4).collect();
assert!(approx_eq(samples[0], 1.0));
assert!(approx_eq(samples[1], 0.0));
assert!(approx_eq(samples[2], -1.0));
assert!(approx_eq(samples[3], 0.0));
}