diff --git a/README.md b/README.md index 4887452..243a22c 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ the following is the current list of plugins - `threebandfolding`: 3 band wave folding distortion - `double_reverse_delta_inverter`: idk, a weird distortion - `transmute_pitch`: pitch to midi converter +- `reverter`: play sound backwards there's a bit of an explanation of each of the plugins below, but it's not a thorough documentation or a manual, it's just a bunch of notes i've written and a short description of the parameters @@ -252,6 +253,17 @@ aside from that, it works well enough on my quick tests you can use it to play synths using other instruments, like a guitar, or by whistling! it's quite fun +### reverter + +records small grains of audio and plays them backwards + +params: +- `length`: length of the grains + +this plugin will introduce a delay of `length` samples, since it has to record that many samples before being able to play them backwards + +in my experience values between 5000 and 8000 tend to work well, but you might want to experiment a bit to see what works for you + ## contributing issues and prs are welcome, but please open an issue before making any big pr, i don't wanna have to reject a pr where you have put a lot of effort on. if you are fine with that, ig go ahead i'm not your mum diff --git a/crates/reverter/Cargo.toml b/crates/reverter/Cargo.toml new file mode 100644 index 0000000..c25cbf6 --- /dev/null +++ b/crates/reverter/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "reverter" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +baseplug = { git = "https://github.com/wrl/baseplug.git", rev = "9cec68f31cca9c0c7a1448379f75d92bbbc782a8" } +serde = "1.0.126" + +utils = { path = "../utils" } diff --git a/crates/reverter/src/lib.rs b/crates/reverter/src/lib.rs new file mode 100644 index 0000000..5ae944b --- /dev/null +++ b/crates/reverter/src/lib.rs @@ -0,0 +1,77 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +use baseplug::{Plugin, ProcessContext}; +use serde::{Deserialize, Serialize}; + +use utils::buffers::*; + +// If you change this remember to change the max on the model +const LEN: usize = 48000; + +baseplug::model! { + #[derive(Debug, Serialize, Deserialize)] + struct ReverterModel { + #[model(min = 10.0, max = 48000.0)] + #[parameter(name = "length")] + length: f32, + } +} + +impl Default for ReverterModel { + fn default() -> Self { + Self { length: 1.0 } + } +} + +struct Reverter { + recording: Buffers, + playing: Buffers, +} + +impl Plugin for Reverter { + const NAME: &'static str = "reverter"; + const PRODUCT: &'static str = "reverter"; + const VENDOR: &'static str = "unnieversal"; + + const INPUT_CHANNELS: usize = 2; + const OUTPUT_CHANNELS: usize = 2; + + type Model = ReverterModel; + + #[inline] + fn new(_sample_rate: f32, _model: &ReverterModel) -> Self { + Self { + recording: Buffers::::new(), + playing: Buffers::::new(), + } + } + + #[inline] + fn process(&mut self, model: &ReverterModelProcess, ctx: &mut ProcessContext) { + let input = &ctx.inputs[0].buffers; + let output = &mut ctx.outputs[0].buffers; + + for i in 0..ctx.nframes { + let len = model.length[i].floor() as usize; + + // add to recording buffer + let finished = self.recording.write_advance(input[0][i], input[1][i]); + + // we read from the playing buffer at the inverse of the recording buffer + // this ensures the two don't go out of sync + let (l, r) = self.playing.read_at(len - self.recording.idx); + + // if we're done, swap the two buffers and reset the index + if finished || self.recording.idx >= len { + self.recording.reset(); + std::mem::swap(&mut self.recording, &mut self.playing); + } + + output[0][i] = l; + output[1][i] = r; + } + } +} + +baseplug::vst2!(Reverter, b"reve");