use crate::dsp::parameter::Parameter;
use crate::sim::components::CircuitComponent;
use crate::sim::wdf::adaptors::SeriesAdaptor;
use crate::sim::wdf::components::{WdfCapacitor, WdfComponent, WdfResistor};
pub struct Resistor {
wdf: WdfResistor,
r_load: f32,
attenuation: f32,
resistance_param: Parameter,
}
impl Resistor {
pub fn new(resistance: f32, r_load: f32) -> Self {
let atten = r_load / (resistance + r_load);
Self {
wdf: WdfResistor::new(resistance),
r_load,
attenuation: atten,
resistance_param: Parameter::new(resistance),
}
}
pub fn set_resistance(&mut self, r: f32) {
self.resistance_param.set(r);
}
}
impl CircuitComponent for Resistor {
fn prepare(&mut self, _sample_rate: f32) {
self.resistance_param.reset(self.wdf.resistance());
self.attenuation = self.r_load / (self.wdf.resistance() + self.r_load);
}
fn process_block(&mut self, input: &[f32], output: &mut [f32]) {
let len = input.len().min(output.len());
for i in 0..len {
self.resistance_param.step();
if !self.resistance_param.is_settled() {
let r = self.resistance_param.value;
self.attenuation = self.r_load / (r + self.r_load);
}
output[i] = input[i] * self.attenuation;
}
}
fn update_parameters(&mut self) {
}
}
pub struct Capacitor {
adaptor: SeriesAdaptor<WdfCapacitor, WdfResistor>,
sample_rate: f32,
}
impl Capacitor {
pub fn new(capacitance: f32, r_load: f32) -> Self {
let cap = WdfCapacitor::new(capacitance);
let res = WdfResistor::new(r_load);
let adaptor = SeriesAdaptor::new(cap, res);
Self {
adaptor,
sample_rate: 44100.0,
}
}
pub fn wdf_capacitor(&self) -> &WdfCapacitor {
&self.adaptor.left
}
}
impl CircuitComponent for Capacitor {
fn prepare(&mut self, sample_rate: f32) {
self.sample_rate = sample_rate;
self.adaptor.left.set_sample_rate(sample_rate);
self.adaptor.left.reset();
self.adaptor.update_impedance();
}
fn process_block(&mut self, input: &[f32], output: &mut [f32]) {
let len = input.len().min(output.len());
for i in 0..len {
let b_tree = self.adaptor.reflected_cached();
let a_root = input[i] - b_tree;
let diff = a_root - (self.adaptor.b_left + self.adaptor.b_right);
let a_r = self.adaptor.b_right + (1.0 - self.adaptor.gamma) * diff;
let v_out = a_r + self.adaptor.b_right;
output[i] = v_out;
self.adaptor.incident(a_root);
}
}
fn update_parameters(&mut self) {}
}
pub struct Inductor {
adaptor: SeriesAdaptor<crate::sim::wdf::components::WdfInductor, WdfResistor>,
sample_rate: f32,
}
impl Inductor {
pub fn new(inductance: f32, r_load: f32) -> Self {
let ind = crate::sim::wdf::components::WdfInductor::new(inductance);
let res = WdfResistor::new(r_load);
let adaptor = SeriesAdaptor::new(ind, res);
Self {
adaptor,
sample_rate: 44100.0,
}
}
}
impl CircuitComponent for Inductor {
fn prepare(&mut self, sample_rate: f32) {
self.sample_rate = sample_rate;
self.adaptor.left.set_sample_rate(sample_rate);
self.adaptor.left.reset();
self.adaptor.update_impedance();
}
fn process_block(&mut self, input: &[f32], output: &mut [f32]) {
let len = input.len().min(output.len());
for i in 0..len {
let b_tree = self.adaptor.reflected_cached();
let a_root = input[i] - b_tree;
let diff = a_root - (self.adaptor.b_left + self.adaptor.b_right);
let a_r = self.adaptor.b_right + (1.0 - self.adaptor.gamma) * diff;
let v_out = a_r + self.adaptor.b_right;
output[i] = v_out;
self.adaptor.incident(a_root);
}
}
fn update_parameters(&mut self) {}
}
#[cfg(test)]
mod tests {
#![allow(clippy::needless_range_loop)]
use super::*;
#[test]
fn test_resistor_attenuation() {
let mut r = Resistor::new(1000.0, 1000.0);
r.prepare(44100.0);
let input = [1.0f32; 64];
let mut output = [0.0f32; 64];
r.process_block(&input, &mut output);
for &s in &output {
assert!((s - 0.5).abs() < 0.01, "Expected 0.5, got {s}");
}
}
#[test]
fn test_capacitor_dc_blocking() {
let mut cap = Capacitor::new(100e-9, 10_000.0); cap.prepare(44100.0);
let n = 22050;
let input = vec![1.0f32; n];
let mut output = vec![0.0f32; n];
cap.process_block(&input, &mut output);
let last_avg: f32 = output[n - 100..].iter().sum::<f32>() / 100.0;
assert!(
last_avg.abs() < 0.5,
"DC should be blocked: last_avg={last_avg}"
);
}
#[test]
fn test_capacitor_ac_passthrough() {
let mut cap = Capacitor::new(1e-6, 10_000.0); cap.prepare(44100.0);
let n = 4410;
let mut input = vec![0.0f32; n];
let mut output = vec![0.0f32; n];
for i in 0..n {
input[i] = (2.0 * std::f32::consts::PI * 1000.0 * i as f32 / 44100.0).sin();
}
cap.process_block(&input, &mut output);
let rms_out: f32 = output[2000..].iter().map(|x| x * x).sum::<f32>() / (n - 2000) as f32;
let rms_in: f32 = input[2000..].iter().map(|x| x * x).sum::<f32>() / (n - 2000) as f32;
let ratio = rms_out.sqrt() / rms_in.sqrt();
assert!(
ratio > 0.1,
"1kHz should pass through coupling cap: ratio={ratio}"
);
}
#[test]
fn test_inductor_lowpass() {
let mut ind = Inductor::new(0.1, 1000.0); ind.prepare(44100.0);
let n = 8820;
let mut input_low = vec![0.0f32; n];
let mut output_low = vec![0.0f32; n];
for i in 0..n {
input_low[i] = (2.0 * std::f32::consts::PI * 100.0 * i as f32 / 44100.0).sin();
}
ind.process_block(&input_low, &mut output_low);
let rms_low_in: f32 = input_low[n / 2..].iter().map(|x| x * x).sum::<f32>();
let rms_low_out: f32 = output_low[n / 2..].iter().map(|x| x * x).sum::<f32>();
ind.prepare(44100.0);
let mut input_high = vec![0.0f32; n];
let mut output_high = vec![0.0f32; n];
for i in 0..n {
input_high[i] = (2.0 * std::f32::consts::PI * 10000.0 * i as f32 / 44100.0).sin();
}
ind.process_block(&input_high, &mut output_high);
let rms_high_in: f32 = input_high[n / 2..].iter().map(|x| x * x).sum::<f32>();
let rms_high_out: f32 = output_high[n / 2..].iter().map(|x| x * x).sum::<f32>();
let ratio_low = rms_low_out / rms_low_in;
let ratio_high = rms_high_out / rms_high_in;
assert!(
ratio_low > ratio_high,
"Inductor should pass low freq better than high: ratio_low={ratio_low}, ratio_high={ratio_high}"
);
}
}