bbx_dsp/blocks/effectors/
vca.rs1use crate::{block::Block, context::DspContext, parameter::ModulationOutput, sample::Sample};
6
7pub struct VcaBlock<S: Sample> {
15 _phantom: std::marker::PhantomData<S>,
16}
17
18impl<S: Sample> VcaBlock<S> {
19 pub fn new() -> Self {
21 Self {
22 _phantom: std::marker::PhantomData,
23 }
24 }
25}
26
27impl<S: Sample> Default for VcaBlock<S> {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl<S: Sample> Block<S> for VcaBlock<S> {
34 #[inline]
35 fn process(&mut self, inputs: &[&[S]], outputs: &mut [&mut [S]], _modulation_values: &[S], _context: &DspContext) {
36 let output = match outputs.first_mut() {
37 Some(out) => out,
38 None => return,
39 };
40
41 let audio_input = inputs.first().copied().unwrap_or(&[]);
42 let control_input = inputs.get(1).copied().unwrap_or(&[]);
43
44 for (i, out_sample) in output.iter_mut().enumerate() {
45 let audio = audio_input.get(i).copied().unwrap_or(S::ZERO);
46 let control = control_input.get(i).copied().unwrap_or(S::ONE);
47 *out_sample = audio * control;
48 }
49 }
50
51 #[inline]
52 fn input_count(&self) -> usize {
53 2
54 }
55
56 #[inline]
57 fn output_count(&self) -> usize {
58 1
59 }
60
61 #[inline]
62 fn modulation_outputs(&self) -> &[ModulationOutput] {
63 &[]
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::{channel::ChannelLayout, context::DspContext};
71
72 fn test_context(buffer_size: usize) -> DspContext {
73 DspContext {
74 sample_rate: 44100.0,
75 num_channels: 1,
76 buffer_size,
77 current_sample: 0,
78 channel_layout: ChannelLayout::default(),
79 }
80 }
81
82 #[test]
83 fn test_vca_multiplication() {
84 let mut vca = VcaBlock::<f32>::new();
85 let context = test_context(4);
86
87 let audio = [1.0f32, 0.5, -0.5, -1.0];
88 let control = [1.0f32, 0.5, 0.5, 0.0];
89 let mut output = [0.0f32; 4];
90
91 let inputs: [&[f32]; 2] = [&audio, &control];
92 let mut outputs: [&mut [f32]; 1] = [&mut output];
93
94 vca.process(&inputs, &mut outputs, &[], &context);
95
96 assert!((output[0] - 1.0).abs() < 1e-6);
97 assert!((output[1] - 0.25).abs() < 1e-6);
98 assert!((output[2] - -0.25).abs() < 1e-6);
99 assert!((output[3] - 0.0).abs() < 1e-6);
100 }
101
102 #[test]
103 fn test_vca_missing_control_defaults_to_unity() {
104 let mut vca = VcaBlock::<f32>::new();
105 let context = test_context(4);
106
107 let audio = [0.5f32, 0.5, 0.5, 0.5];
108 let mut output = [0.0f32; 4];
109
110 let inputs: [&[f32]; 1] = [&audio];
111 let mut outputs: [&mut [f32]; 1] = [&mut output];
112
113 vca.process(&inputs, &mut outputs, &[], &context);
114
115 for sample in output.iter() {
116 assert!((sample - 0.5).abs() < 1e-6);
117 }
118 }
119
120 #[test]
121 fn test_vca_input_output_counts_f32() {
122 let vca = VcaBlock::<f32>::new();
123 assert_eq!(vca.input_count(), 2);
124 assert_eq!(vca.output_count(), 1);
125 }
126
127 #[test]
128 fn test_vca_input_output_counts_f64() {
129 let vca = VcaBlock::<f64>::new();
130 assert_eq!(vca.input_count(), 2);
131 assert_eq!(vca.output_count(), 1);
132 }
133
134 #[test]
135 fn test_vca_multiplication_f64() {
136 let mut vca = VcaBlock::<f64>::new();
137 let context = test_context(4);
138
139 let audio = [1.0f64, 0.5, -0.5, -1.0];
140 let control = [1.0f64, 0.5, 0.5, 0.0];
141 let mut output = [0.0f64; 4];
142
143 let inputs: [&[f64]; 2] = [&audio, &control];
144 let mut outputs: [&mut [f64]; 1] = [&mut output];
145
146 vca.process(&inputs, &mut outputs, &[], &context);
147
148 assert!((output[0] - 1.0).abs() < 1e-12);
149 assert!((output[1] - 0.25).abs() < 1e-12);
150 assert!((output[2] - -0.25).abs() < 1e-12);
151 assert!((output[3] - 0.0).abs() < 1e-12);
152 }
153
154 #[test]
155 fn test_vca_default() {
156 let vca = VcaBlock::<f32>::default();
157 assert_eq!(vca.input_count(), 2);
158 assert_eq!(vca.output_count(), 1);
159 }
160
161 #[test]
162 fn test_vca_modulation_outputs_empty() {
163 let vca = VcaBlock::<f32>::new();
164 assert!(vca.modulation_outputs().is_empty());
165 }
166}