use crate::{block::Block, context::DspContext, parameter::ModulationOutput, sample::Sample};
pub struct VcaBlock<S: Sample> {
_phantom: std::marker::PhantomData<S>,
}
impl<S: Sample> VcaBlock<S> {
pub fn new() -> Self {
Self {
_phantom: std::marker::PhantomData,
}
}
}
impl<S: Sample> Default for VcaBlock<S> {
fn default() -> Self {
Self::new()
}
}
impl<S: Sample> Block<S> for VcaBlock<S> {
#[inline]
fn process(&mut self, inputs: &[&[S]], outputs: &mut [&mut [S]], _modulation_values: &[S], _context: &DspContext) {
let output = match outputs.first_mut() {
Some(out) => out,
None => return,
};
let audio_input = inputs.first().copied().unwrap_or(&[]);
let control_input = inputs.get(1).copied().unwrap_or(&[]);
for (i, out_sample) in output.iter_mut().enumerate() {
let audio = audio_input.get(i).copied().unwrap_or(S::ZERO);
let control = control_input.get(i).copied().unwrap_or(S::ONE);
*out_sample = audio * control;
}
}
#[inline]
fn input_count(&self) -> usize {
2
}
#[inline]
fn output_count(&self) -> usize {
1
}
#[inline]
fn modulation_outputs(&self) -> &[ModulationOutput] {
&[]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{channel::ChannelLayout, context::DspContext};
fn test_context(buffer_size: usize) -> DspContext {
DspContext {
sample_rate: 44100.0,
num_channels: 1,
buffer_size,
current_sample: 0,
channel_layout: ChannelLayout::default(),
}
}
#[test]
fn test_vca_multiplication() {
let mut vca = VcaBlock::<f32>::new();
let context = test_context(4);
let audio = [1.0f32, 0.5, -0.5, -1.0];
let control = [1.0f32, 0.5, 0.5, 0.0];
let mut output = [0.0f32; 4];
let inputs: [&[f32]; 2] = [&audio, &control];
let mut outputs: [&mut [f32]; 1] = [&mut output];
vca.process(&inputs, &mut outputs, &[], &context);
assert!((output[0] - 1.0).abs() < 1e-6);
assert!((output[1] - 0.25).abs() < 1e-6);
assert!((output[2] - -0.25).abs() < 1e-6);
assert!((output[3] - 0.0).abs() < 1e-6);
}
#[test]
fn test_vca_missing_control_defaults_to_unity() {
let mut vca = VcaBlock::<f32>::new();
let context = test_context(4);
let audio = [0.5f32, 0.5, 0.5, 0.5];
let mut output = [0.0f32; 4];
let inputs: [&[f32]; 1] = [&audio];
let mut outputs: [&mut [f32]; 1] = [&mut output];
vca.process(&inputs, &mut outputs, &[], &context);
for sample in output.iter() {
assert!((sample - 0.5).abs() < 1e-6);
}
}
#[test]
fn test_vca_input_output_counts_f32() {
let vca = VcaBlock::<f32>::new();
assert_eq!(vca.input_count(), 2);
assert_eq!(vca.output_count(), 1);
}
#[test]
fn test_vca_input_output_counts_f64() {
let vca = VcaBlock::<f64>::new();
assert_eq!(vca.input_count(), 2);
assert_eq!(vca.output_count(), 1);
}
#[test]
fn test_vca_multiplication_f64() {
let mut vca = VcaBlock::<f64>::new();
let context = test_context(4);
let audio = [1.0f64, 0.5, -0.5, -1.0];
let control = [1.0f64, 0.5, 0.5, 0.0];
let mut output = [0.0f64; 4];
let inputs: [&[f64]; 2] = [&audio, &control];
let mut outputs: [&mut [f64]; 1] = [&mut output];
vca.process(&inputs, &mut outputs, &[], &context);
assert!((output[0] - 1.0).abs() < 1e-12);
assert!((output[1] - 0.25).abs() < 1e-12);
assert!((output[2] - -0.25).abs() < 1e-12);
assert!((output[3] - 0.0).abs() < 1e-12);
}
#[test]
fn test_vca_default() {
let vca = VcaBlock::<f32>::default();
assert_eq!(vca.input_count(), 2);
assert_eq!(vca.output_count(), 1);
}
#[test]
fn test_vca_modulation_outputs_empty() {
let vca = VcaBlock::<f32>::new();
assert!(vca.modulation_outputs().is_empty());
}
}