From f94653f3ec7dbe4e7fa9b016090dfa7efec9af49 Mon Sep 17 00:00:00 2001 From: annieversary Date: Thu, 16 Sep 2021 15:36:01 +0200 Subject: [PATCH] [opiate] create and implement crate --- crates/opiate/Cargo.toml | 15 ++++++ crates/opiate/src/lib.rs | 114 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 crates/opiate/Cargo.toml create mode 100644 crates/opiate/src/lib.rs diff --git a/crates/opiate/Cargo.toml b/crates/opiate/Cargo.toml new file mode 100644 index 0000000..b30f160 --- /dev/null +++ b/crates/opiate/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "opiate" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +# baseplug = { git = "https://github.com/wrl/baseplug.git", rev = "9cec68f31cca9c0c7a1448379f75d92bbbc782a8" } +baseplug = { path = "../baseplug" } +serde = "1.0.126" +pvoc = { path = "../pvoc-rs" } +utils = { path = "../utils" } +log = "0.4.14" diff --git a/crates/opiate/src/lib.rs b/crates/opiate/src/lib.rs new file mode 100644 index 0000000..6e01f4e --- /dev/null +++ b/crates/opiate/src/lib.rs @@ -0,0 +1,114 @@ +#![allow(incomplete_features)] +#![feature(generic_associated_types)] + +// morphing algorithm from https://ccrma.stanford.edu/~jhsu/421b/ + +use baseplug::{Plugin, ProcessContext}; +use pvoc::{Bin, PhaseVocoder}; +use serde::{Deserialize, Serialize}; +use utils::logs::*; + +baseplug::model! { + #[derive(Debug, Serialize, Deserialize)] + struct OpiateModel { + #[model(min = 0.0, max = 1.0)] + #[parameter(name = "morph")] + morph: f32, + } +} + +impl Default for OpiateModel { + fn default() -> Self { + Self { morph: 0.0 } + } +} + +struct Opiate { + pvoc: PhaseVocoder, + out_0: Vec, + out_1: Vec, + out_2: Vec, + out_3: Vec, +} + +impl Plugin for Opiate { + const NAME: &'static str = "opiate"; + const PRODUCT: &'static str = "opiate"; + const VENDOR: &'static str = "unnieversal"; + + const INPUT_CHANNELS: usize = 4; + const OUTPUT_CHANNELS: usize = 2; + + type Model = OpiateModel; + + #[inline] + fn new(sample_rate: f32, _model: &OpiateModel) -> Self { + setup_logging("opiate.log"); + + Self { + pvoc: PhaseVocoder::new(4, sample_rate as f64, 64, 4), + out_0: vec![0.0; 300], + out_1: vec![0.0; 300], + out_2: vec![0.0; 300], + out_3: vec![0.0; 300], + } + } + + #[inline] + fn process(&mut self, model: &OpiateModelProcess, ctx: &mut ProcessContext) { + let input = &ctx.inputs[0].buffers; + let output = &mut ctx.outputs[0].buffers; + + if input.len() != 4 { + for i in 0..ctx.nframes { + output[0][i] = 0.0; + output[1][i] = 0.0; + } + return; + } + + let out = &mut [ + &mut self.out_0[..], + &mut self.out_1[..], + &mut self.out_2[..], + &mut self.out_3[..], + ][..]; + + let morph = model.morph[0] as f64; + let imorph = 1.0 - morph; + self.pvoc.process( + input, + out, + |_channels: usize, bins: usize, input: &[Vec], output: &mut [Vec]| { + for j in 0..bins { + // TODO Check if working with the frequencies is the same as working with the phase + // i think we might need to try it to make sure it's the same + // to do that, we'll need to change how pvoc works + + let mags = morph * (1.0 - input[0][j].amp) + input[0][j].amp; + let mags2 = imorph * (1.0 - input[2][j].amp) + input[2][j].amp; + let phases = input[0][j].freq - (input[0][j].freq * morph); + let phases2 = input[2][j].freq - (input[2][j].freq * imorph); + + output[0][j].amp = mags * mags2; + output[0][j].freq = phases + phases2; + + let mags = morph * (1.0 - input[1][j].amp) + input[1][j].amp; + let mags2 = imorph * (1.0 - input[3][j].amp) + input[3][j].amp; + let phases = input[1][j].freq - (input[1][j].freq * morph); + let phases2 = input[3][j].freq - (input[3][j].freq * imorph); + + output[1][j].amp = mags * mags2; + output[1][j].freq = phases + phases2; + } + }, + ); + + for i in 0..ctx.nframes { + output[0][i] = self.out_0[i]; + output[1][i] = self.out_1[i]; + } + } +} + +baseplug::vst2!(Opiate, b"opiu");