#![allow(incomplete_features)] #![feature(generic_associated_types)] use baseplug::{Plugin, ProcessContext}; use serde::{Deserialize, Serialize}; mod delay; use delay::DelayLine; // If you change this remember to change the max on the model const LEN: usize = 48000; baseplug::model! { #[derive(Debug, Serialize, Deserialize)] struct SostenModel { #[model(min = 10.0, max = 48000.0)] #[parameter(name = "length")] length: f32, #[model(min = 0.0, max = 1.0)] #[parameter(name = "mix")] mix: f32, #[model(min = 0.0, max = 1.0)] #[parameter(name = "enable")] enable: f32, } } impl Default for SostenModel { fn default() -> Self { Self { length: 1000.0, mix: 1.0, enable: 0.0, } } } struct Sosten { delay_l: DelayLine, delay_r: DelayLine, buffer_l: [f32; LEN], buffer_r: [f32; LEN], playing: bool, idx: usize, } impl Plugin for Sosten { const NAME: &'static str = "sosten"; const PRODUCT: &'static str = "sosten"; const VENDOR: &'static str = "unnieversal"; const INPUT_CHANNELS: usize = 2; const OUTPUT_CHANNELS: usize = 2; type Model = SostenModel; #[inline] fn new(_sample_rate: f32, _model: &SostenModel) -> Self { Self { delay_l: DelayLine::::new(), delay_r: DelayLine::::new(), buffer_l: [0.; LEN], buffer_r: [0.; LEN], playing: false, idx: 0, } } #[inline] fn process(&mut self, model: &SostenModelProcess, ctx: &mut ProcessContext) { let input = &ctx.inputs[0].buffers; let output = &mut ctx.outputs[0].buffers; for i in 0..ctx.nframes { // Update delays self.delay_l.write_and_advance(input[0][i]); self.delay_r.write_and_advance(input[1][i]); // Toggle playing according to `enable` if model.enable[i] >= 0.5 { // If it wasn't playing before this, reload buffer if !self.playing { self.delay_l.read_slice(&mut self.buffer_l); self.delay_r.read_slice(&mut self.buffer_r); self.idx = 0; self.playing = true; } } else { self.playing = false; } // Play the repeating part if self.playing { // Length of section to play let len = model.length[i].trunc() as usize; // Pass through input let mix_inv = 1. - model.mix[i]; output[0][i] = mix_inv * input[0][i]; output[1][i] = mix_inv * input[1][i]; // If len has changed, idx may have not, so we do the min so we don't go out of bounds let idx = self.idx.min(len - 1); // Play from Buffer output[0][i] += model.mix[i] * self.buffer_l[(LEN - len) + idx]; output[1][i] += model.mix[i] * self.buffer_r[(LEN - len) + idx]; // Loop index after we finish playing a section self.idx += 1; if self.idx >= len { self.idx = 0; } } else { // If it's not on a repeat section, pass all the audio fully output[0][i] = input[0][i]; output[1][i] = input[1][i]; } } } } baseplug::vst2!(Sosten, b"sost");