rill_analog_effects/
op_amp.rs1#[derive(Debug, Clone)]
4pub struct OperationalAmplifier {
5 gain: f64,
6 slew_rate: f64,
7 bandwidth: f64,
8 voltage_rails: (f64, f64),
9 output_voltage: f64,
10 internal_state: f64,
11}
12
13impl OperationalAmplifier {
14 pub fn new(gain: f64, slew_rate: f64, bandwidth: f64) -> Self {
20 Self {
21 gain,
22 slew_rate: slew_rate * 1e6,
23 bandwidth,
24 voltage_rails: (-15.0, 15.0),
25 output_voltage: 0.0,
26 internal_state: 0.0,
27 }
28 }
29
30 pub fn set_voltage_rails(&mut self, negative: f64, positive: f64) {
32 self.voltage_rails = (negative, positive);
33 }
34
35 pub fn process(&mut self, input: f64, dt: f64) -> f64 {
40 let ideal_output = input * self.gain;
41
42 let max_change = self.slew_rate * dt;
43 let output_change = ideal_output - self.internal_state;
44 let limited_change = output_change.clamp(-max_change, max_change);
45 self.internal_state += limited_change;
46
47 let pole_frequency = self.bandwidth / self.gain;
48 let alpha = 1.0 / (1.0 + 2.0 * std::f64::consts::PI * pole_frequency * dt);
49 self.output_voltage = alpha * self.internal_state + (1.0 - alpha) * ideal_output;
50
51 self.output_voltage = self
52 .output_voltage
53 .clamp(self.voltage_rails.0, self.voltage_rails.1);
54
55 self.output_voltage
56 }
57
58 pub fn reset(&mut self) {
60 self.output_voltage = 0.0;
61 self.internal_state = 0.0;
62 }
63
64 pub fn output_voltage(&self) -> f64 {
66 self.output_voltage
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_op_amp_dc_gain() {
76 let mut op = OperationalAmplifier::new(10.0, 0.5, 1e6);
77 let output = op.process(0.5, 1.0 / 44100.0);
78 assert!(output > 0.0);
79 }
80
81 #[test]
82 fn test_op_amp_rail_clamp() {
83 let mut op = OperationalAmplifier::new(100.0, 100.0, 1e6);
84 let output = op.process(1.0, 1.0 / 44100.0);
85 assert!(output <= 15.0);
86 assert!(output >= -15.0);
87 }
88
89 #[test]
90 fn test_op_amp_reset() {
91 let mut op = OperationalAmplifier::new(10.0, 0.5, 1e6);
92 op.process(0.5, 1.0 / 44100.0);
93 op.reset();
94 assert_eq!(op.output_voltage(), 0.0);
95 }
96}