#![allow(incomplete_features)] #![feature(generic_associated_types)] use baseplug::{Plugin, ProcessContext}; use serde::{Deserialize, Serialize}; mod hysteresis; use hysteresis::*; baseplug::model! { #[derive(Debug, Serialize, Deserialize)] struct HysteriaModel { #[model(min = 0.0, max = 10.0)] #[parameter(name = "drive")] drive: f32, #[model(min = 0.0, max = 1.0)] #[parameter(name = "saturation")] sat: f32, #[model(min = 0.0, max = 1.0)] #[parameter(name = "width")] width: f32, } } impl Default for HysteriaModel { fn default() -> Self { Self { drive: 1., sat: 0.5, width: 0.5, } } } struct Hysteria { hysteresis_l: Hysteresis, hysteresis_r: Hysteresis, } impl Plugin for Hysteria { const NAME: &'static str = "hysteria"; const PRODUCT: &'static str = "hysteria"; const VENDOR: &'static str = "unnieversal"; const INPUT_CHANNELS: usize = 2; const OUTPUT_CHANNELS: usize = 2; type Model = HysteriaModel; #[inline] fn new(_sample_rate: f32, _model: &HysteriaModel) -> Self { Self { hysteresis_l: Hysteresis::new(), hysteresis_r: Hysteresis::new(), } } #[inline] fn process(&mut self, model: &HysteriaModelProcess, ctx: &mut ProcessContext) { let input = &ctx.inputs[0].buffers; let output = &mut ctx.outputs[0].buffers; // Update sample rate let sample_rate = ctx.sample_rate; self.hysteresis_l.set_sample_rate(sample_rate); self.hysteresis_r.set_sample_rate(sample_rate); for i in 0..ctx.nframes { // Makeup is an added extra to make it work similarly with all params let makeup = calc_makeup(model.width[i], model.sat[i]); output[0][i] = self.hysteresis_l.process( model.drive[i], model.width[i], model.sat[i], input[0][i], ) * makeup; output[1][i] = self.hysteresis_r.process( model.drive[i], model.width[i], model.sat[i], input[1][i], ) * makeup; } } } fn calc_makeup(width: f32, sat: f32) -> f32 { (1.0 + 0.6 * width) / (0.5 + 1.5 * (1.0 - sat)) } baseplug::vst2!(Hysteria, b"hyst");