#![allow(
dead_code,
clippy::precedence,
clippy::type_complexity,
clippy::float_cmp,
clippy::len_zero,
clippy::double_neg,
clippy::many_single_char_names,
clippy::manual_range_contains
)]
use fundsp::fft::*;
use fundsp::prelude32::*;
use num_complex::Complex32;
fn is_equal_response(x: Complex32, y: Complex32) -> bool {
let tolerance = 2.0e-4;
let norm = (x - y).norm();
norm <= max(1.0, max(x.norm(), y.norm())) * tolerance
}
fn test_response<X>(mut filter: X)
where
X: AudioUnit,
{
assert!(filter.inputs() == 1 && filter.outputs() == 1);
filter.allocate();
let length = 0x8000;
let sample_rate = DEFAULT_SR;
filter.reset();
filter.set_sample_rate(sample_rate);
let mut input = 1.0;
let mut buffer = Vec::with_capacity(length);
for _i in 0..length / 2 {
filter.filter_mono(0.0);
}
for _i in 0..length {
let x = filter.filter_mono(input);
buffer.push(x);
input = 0.0;
}
let spectrum = real_fft(&mut buffer);
let mut f = 10.0;
while f <= 22_000.0 {
let i = round(f * length as f64 / sample_rate) as usize;
if i >= spectrum.len() {
break;
}
let f_i = i as f64 / length as f64 * sample_rate;
let reported = filter.response(0, f_i).unwrap();
let reported = Complex32::new(reported.re as f32, reported.im as f32);
let response = spectrum[i];
if !is_equal_response(reported, response) {
eprintln!(
"{} Hz reported ({}, {}) measured ({}, {})",
f_i,
reported.norm(),
reported.arg(),
response.norm(),
response.arg(),
);
panic!()
}
if f < 1000.0 {
f += 10.0;
} else {
f += 100.0;
}
}
}
#[test]
fn test_responses() {
test_response(bell_hz(500.0, 1.0, 2.0) * 0.5);
test_response(lowshelf_hz(2000.0, 10.0, 5.0));
test_response(highshelf_hz(2000.0, 10.0, 5.0));
test_response(peak_hz(5000.0, 1.0));
test_response(allpass_hz(500.0, 5.0));
test_response(notch_hz(1000.0, 1.0));
test_response(lowpass_hz(50.0, 1.0));
test_response(highpass_hz(5000.0, 1.0));
test_response(bandpass_hz(100.0, 1.0));
test_response(highpass_hz(500.0, 1.0) & bandpass_hz(500.0, 2.0));
test_response(pinkpass() * dc(2.0));
test_response(follow(0.0002));
test_response(follow(0.01));
test_response(delay(0.0));
test_response(delay(0.0001));
test_response(delay(0.0001) >> delay(0.0002));
test_response(dcblock());
test_response(dcblock_hz(100.0) & follow(0.001));
test_response(lowpole_hz(1000.0));
test_response(split() >> (lowpole_hz(100.0) + lowpole_hz(190.0)));
test_response(lowpole_hz(10000.0));
test_response(resonator_hz(300.0, 20.0));
test_response(butterpass_hz(200.0));
test_response(butterpass_hz(1000.0));
test_response(butterpass_hz(500.0) & bell_hz(2000.0, 10.0, 5.0));
test_response(butterpass_hz(6000.0) >> lowpass_hz(500.0, 3.0));
test_response(pass() & tick());
test_response(pass() * 0.25 & tick() * 0.5 & tick() >> tick() * 0.25);
test_response(tick() & lowshelf_hz(500.0, 2.0, 0.1));
test_response(allpole_delay(0.5) & allpole_delay(1.3) & allpole_delay(0.1));
test_response(highpole_hz(5000.0) & highpole_hz(500.0) & highpole_hz(2000.0));
test_response(
(delay(0.001) ^ delay(0.002)) >> reverse() >> (delay(0.003) | delay(0.007)) >> join(),
);
test_response(
(butterpass_hz(15000.0) ^ allpass_hz(10000.0, 10.0)) >> lowpole_hz(500.0) + pass(),
);
test_response(
(resonator_hz(12000.0, 500.0) ^ lowpass_hz(3000.0, 0.5))
>> pass() + highshelf_hz(3000.0, 0.5, 4.0),
);
test_response(split() >> multipass::<U32>() >> join());
test_response(
split()
>> stacki::<U8, _, _>(|i| {
resonator_hz(1000.0 + 1000.0 * i as f32, 100.0 + 100.0 * i as f32)
})
>> join(),
);
test_response(branchf::<U5, _, _>(|t| resonator_hz(xerp(100.0, 20000.0, t), 10.0)) >> join());
test_response(pipei::<U4, _, _>(|i| {
bell_hz(
1000.0 + 1000.0 * i as f32,
(i + 1) as f32,
db_amp((i + 6) as f32),
)
}));
test_response(
split() >> stacki::<U5, _, _>(|i| lowpole_hz(1000.0 + 1000.0 + i as f32)) >> join(),
);
test_response(busi::<U7, _, _>(|i| {
lowpass_hz(1000.0 + 1000.0 * rnd1(i) as f32, 1.0 + 1.0 * rnd2(i) as f32)
}));
test_response(
split::<U3>()
>> multisplit::<U3, U3>()
>> sumf::<U9, _, _>(|f| highshelf_hz(f * 10.0 + 10.0, 1.0 + f, 2.0 + f)),
);
test_response(1.0 - pan(0.5) >> join());
test_response(0.5 * pan(0.0) >> join());
test_response(pan(0.5) - 1.0 >> join());
test_response(pan(-1.0) * 0.5 >> multijoin::<U1, U2>());
let tmp = shared(0.0);
test_response(fir((0.5, 0.5)) | timer(&tmp));
test_response(fir((0.25, 0.5, 0.25)) >> monitor(&tmp, Meter::Sample));
test_response(fir((0.4, 0.3, 0.2, 0.1)));
test_response(morph_hz(1000.0, 1.0, 0.5));
test_response(morph_hz(2000.0, 2.0, -0.5));
test_response((1.0 + pass() | dc((1000.0, 0.5, 0.5))) >> morph());
test_response((pass() | dc((500.0, 2.0, -1.0))) >> morph());
test_response(biquad(0.0, 0.17149, 0.29287, 0.58574, 0.29287));
test_response(biquad(0.033717, 0.171773, 1.059253, -0.035714, 0.181952));
test_response(pass() + 1.0 >> lowpass_hz(1000.0, 1.0));
test_response((pass() | dc(1.0)) >> rotate(0.5, 1.0) >> (pass() | sink()));
test_response((dc(2.0) | pass()) >> rotate(-0.1, 0.5) >> (pass() | sink()));
let mut biq = biquad_bank();
biq.set(Setting::biquad(0.05, 0.1, 0.3, 0.1, 0.15).index(3));
test_response(
(multizero::<U3>() | pass() | multizero::<U4>())
>> biq
>> (multisink::<U3>() | pass() | multisink::<U4>()),
);
let mut net1 = Net::new(1, 1);
net1.chain(Box::new(lowpole_hz(1500.0)));
test_response(net1);
let mut net2 = Net::new(1, 1);
net2.chain(Box::new(lowpole_hz(500.0)));
net2.chain(Box::new(lowpole_hz(2500.0)));
test_response(net2);
let mut net3 = Net::new(1, 1);
net3.chain(Box::new(highpole_hz(1500.0)));
let mut net4 = Net::new(1, 1);
net4.chain(Box::new(highpole_hz(500.0)));
test_response(net3 >> net4);
let mut net5 = Net::new(1, 1);
net5.chain(Box::new(highpole_hz(1500.0)));
let mut net6 = Net::new(1, 1);
net6.chain(Box::new(highpole_hz(500.0)));
test_response(net5 & net6 & pass());
let mut net7 = Net::new(1, 1);
let id7 = net7.push(Box::new(highpass_hz(1000.0, 1.0)));
net7.connect_input(0, id7, 0);
net7.connect_output(id7, 0, 0);
test_response(net7);
let mut net8 = Net::new(1, 1);
net8.chain(Box::new(highpole_hz(1500.0)));
test_response(Net::wrap(Box::new(zero())) + net8);
let mut net9 = Net::new(1, 1);
net9.chain(Box::new(highpole_hz(2000.0)));
test_response(Net::wrap(Box::new(dc(1.0))) - net9);
let mut neta = Net::new(1, 1);
neta.chain(Box::new(notch_hz(2500.0, 2.0)));
test_response(Net::wrap(Box::new(dc(2.0))) * neta);
let mut netb = Net::new(1, 1);
netb.chain(Box::new(notch_hz(2500.0, 1.0)));
test_response(netb * 2.0 >> lowpass_hz(1500.0, 1.0));
let mut netc = Net::new(1, 1);
netc.chain(Box::new(highpass_hz(5500.0, 1.0)));
test_response(netc >> highpass_hz(2500.0, 1.0) + 1.0);
let mut netd = Net::new(1, 1);
netd.chain(Box::new(lowpass_hz(5000.0, 1.0)));
test_response((netd ^ highpass_hz(3000.0, 1.0)) >> (pass() + pass()));
let mut nete = Net::new(1, 1);
nete.chain(Box::new(notch_hz(5000.0, 1.0)));
test_response((nete.clone() ^ peak_hz(3000.0, 1.0)) >> (Net::wrap(Box::new(pass())) + pass()));
let mut netf = Net::new(1, 1);
netf.chain(Box::new(notch_hz(2000.0, 1.0)));
test_response(
(netf.clone() ^ pass() ^ peak_hz(1000.0, 1.0))
>> (Net::wrap(Box::new(pass())) + pass() + pass()),
);
let mut netg = Net::new(1, 1);
netg.chain(Box::new(notch_hz(2000.0, 1.0)));
test_response(
(netg ^ pass() ^ pass())
>> (Net::wrap(Box::new(pass())) | pass() | pinkpass())
>> (Net::wrap(Box::new(pinkpass())) + pass() + pass()),
);
}
#[test]
fn test_allpass() {
let length = 0x8000;
let allpasses: [Box<dyn AudioUnit>; 12] = [
Box::new(pass()),
Box::new(tick()),
Box::new(allpole_delay(0.5)),
Box::new(allpole_delay(0.8)),
Box::new(delay(0.0001)),
Box::new(delay(0.001)),
Box::new(allpass_hz(1000.0, 1.0)),
Box::new(allpass_hz(2000.0, 2.0)),
Box::new(allnest_c(0.5, pass())),
Box::new(allnest_c(0.6, tick())),
Box::new(allnest_c(0.7, allpole_delay(0.5))),
Box::new(allnest_c(-0.6, allpass_hz(3000.0, 3.0))),
];
let impulse = Wave::render(DEFAULT_SR, 1.0 / DEFAULT_SR, &mut (impulse::<U1>()));
for mut node in allpasses {
let mut response = impulse.filter(length as f64 / DEFAULT_SR, &mut *node);
let mut data = response.remove_channel(0);
let spectrum = real_fft(&mut data);
let tolerance = 1.0e-5;
for s in &spectrum[1..] {
let norm = s.norm();
assert!(norm >= 1.0 - tolerance && norm <= 1.0 + tolerance);
}
}
}