1use crate::port::{GraphModule, PortDef, PortSpec, PortValues, SignalKind};
12use alloc::vec;
13
14pub struct MidSideEncode {
16 spec: PortSpec,
17}
18
19impl MidSideEncode {
20 pub fn new() -> Self {
21 Self {
22 spec: PortSpec {
23 inputs: vec![
24 PortDef::new(0, "left", SignalKind::Audio),
25 PortDef::new(1, "right", SignalKind::Audio),
26 ],
27 outputs: vec![
28 PortDef::new(10, "mid", SignalKind::Audio),
29 PortDef::new(11, "side", SignalKind::Audio),
30 ],
31 },
32 }
33 }
34}
35
36impl Default for MidSideEncode {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl GraphModule for MidSideEncode {
43 fn port_spec(&self) -> &PortSpec {
44 &self.spec
45 }
46
47 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
48 let left = inputs.get_or(0, 0.0);
49 let right = inputs.get_or(1, 0.0);
50 outputs.set(10, (left + right) * 0.5);
51 outputs.set(11, (left - right) * 0.5);
52 }
53
54 fn reset(&mut self) {}
55
56 fn set_sample_rate(&mut self, _: f64) {}
57
58 fn type_id(&self) -> &'static str {
59 "mid_side_encode"
60 }
61}
62
63pub struct MidSideDecode {
68 spec: PortSpec,
69}
70
71impl MidSideDecode {
72 pub fn new() -> Self {
73 Self {
74 spec: PortSpec {
75 inputs: vec![
76 PortDef::new(0, "mid", SignalKind::Audio),
77 PortDef::new(1, "side", SignalKind::Audio),
78 PortDef::new(2, "width", SignalKind::CvUnipolar)
79 .with_default(1.0)
80 .with_attenuverter(),
81 ],
82 outputs: vec![
83 PortDef::new(10, "left", SignalKind::Audio),
84 PortDef::new(11, "right", SignalKind::Audio),
85 ],
86 },
87 }
88 }
89}
90
91impl Default for MidSideDecode {
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97impl GraphModule for MidSideDecode {
98 fn port_spec(&self) -> &PortSpec {
99 &self.spec
100 }
101
102 fn tick(&mut self, inputs: &PortValues, outputs: &mut PortValues) {
103 let mid = inputs.get_or(0, 0.0);
104 let side = inputs.get_or(1, 0.0);
105 let width = inputs.get_or(2, 1.0).clamp(0.0, 2.0);
106 let scaled = side * width;
107 outputs.set(10, mid + scaled);
108 outputs.set(11, mid - scaled);
109 }
110
111 fn reset(&mut self) {}
112
113 fn set_sample_rate(&mut self, _: f64) {}
114
115 fn type_id(&self) -> &'static str {
116 "mid_side_decode"
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 fn encode(l: f64, r: f64) -> (f64, f64) {
125 let mut m = MidSideEncode::new();
126 let mut inputs = PortValues::new();
127 let mut outputs = PortValues::new();
128 inputs.set(0, l);
129 inputs.set(1, r);
130 m.tick(&inputs, &mut outputs);
131 (outputs.get(10).unwrap(), outputs.get(11).unwrap())
132 }
133
134 fn decode(mid: f64, side: f64, width: f64) -> (f64, f64) {
135 let mut d = MidSideDecode::new();
136 let mut inputs = PortValues::new();
137 let mut outputs = PortValues::new();
138 inputs.set(0, mid);
139 inputs.set(1, side);
140 inputs.set(2, width);
141 d.tick(&inputs, &mut outputs);
142 (outputs.get(10).unwrap(), outputs.get(11).unwrap())
143 }
144
145 #[test]
146 fn test_encode_decode_identity() {
147 for &(l, r) in &[(1.0, -1.0), (0.3, 0.7), (-2.5, 4.1), (0.0, 0.0)] {
148 let (mid, side) = encode(l, r);
149 let (dl, dr) = decode(mid, side, 1.0);
150 assert!((dl - l).abs() < 1e-12, "L identity failed: {dl} != {l}");
151 assert!((dr - r).abs() < 1e-12, "R identity failed: {dr} != {r}");
152 }
153 }
154
155 #[test]
156 fn test_width_zero_is_mono() {
157 let (mid, side) = encode(1.0, -1.0);
158 let (dl, dr) = decode(mid, side, 0.0);
159 assert!((dl - dr).abs() < 1e-12, "width 0 must be mono");
160 assert!((dl - mid).abs() < 1e-12, "mono value must equal mid");
161 }
162
163 #[test]
164 fn test_mono_input_has_zero_side() {
165 let (_mid, side) = encode(0.5, 0.5);
166 assert!(side.abs() < 1e-12, "mono input must have zero side");
167 }
168
169 #[test]
170 fn test_width_clamped_and_doubled() {
171 let (mid, side) = encode(1.0, 0.0); let (dl, dr) = decode(mid, side, 2.0);
173 assert!((dl - 1.5).abs() < 1e-12);
175 assert!((dr + 0.5).abs() < 1e-12);
176 let (dl2, _dr2) = decode(mid, side, 5.0);
178 assert!((dl2 - 1.5).abs() < 1e-12);
179 }
180
181 #[test]
182 fn test_type_ids() {
183 assert_eq!(MidSideEncode::default().type_id(), "mid_side_encode");
184 assert_eq!(MidSideDecode::default().type_id(), "mid_side_decode");
185 }
186}