This repository has been archived by the owner on Mar 27, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
483aee0
commit a5c191e
Showing
3 changed files
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
|
||
# Generated by Cargo | ||
/target/ | ||
/examples/*/target/ | ||
Cargo.lock | ||
|
||
# Vim | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "dimension_expander" | ||
version = "0.1.0" | ||
authors = ["Marko Mijalkovic <marko.mijalkovic97@gmail.com>"] | ||
|
||
[dependencies] | ||
vst2 = { git = "https://github.com/overdrivenpotato/rust-vst2" } | ||
time = "0.1" | ||
|
||
[lib] | ||
name = "dimension_expander" | ||
crate-type = ["dylib"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
#[macro_use] extern crate vst2; | ||
extern crate time; | ||
|
||
use vst2::plugin::{Category, Info, Plugin}; | ||
use vst2::buffer::AudioBuffer; | ||
|
||
use std::mem; | ||
use std::f64::consts::PI; | ||
use std::collections::VecDeque; | ||
|
||
/// Calculate the length in samples for a delay. Size ranges from 0.0 to 1.0. | ||
fn delay(index: usize, mut size: f32) -> isize { | ||
const SIZE_OFFSET: f32 = 0.06; | ||
const SIZE_MULT: f32 = 1_000.0; | ||
|
||
size += SIZE_OFFSET; | ||
|
||
// Spread ratio between delays | ||
const SPREAD: f32 = 0.3; | ||
|
||
let base = size * SIZE_MULT; | ||
let mult = (index as f32 * SPREAD) + 1.0; | ||
let offset = if index > 2 { base * SPREAD / 2.0 } else { 0.0 }; | ||
|
||
(base * mult + offset) as isize | ||
} | ||
|
||
/// A left channel and right channel sample. | ||
type SamplePair = (f32, f32); | ||
|
||
/// The Dimension Expander. | ||
struct DimensionExpander { | ||
buffers: Vec<VecDeque<SamplePair>>, | ||
dry_wet: f32, | ||
size: f32, | ||
} | ||
|
||
impl Default for DimensionExpander { | ||
fn default() -> DimensionExpander { | ||
DimensionExpander::new(0.12, 0.66) | ||
} | ||
} | ||
|
||
impl DimensionExpander { | ||
fn new(size: f32, dry_wet: f32) -> DimensionExpander { | ||
const NUM_DELAYS: usize = 4; | ||
|
||
let mut buffers = Vec::new(); | ||
|
||
// Generate delay buffers | ||
for i in 0..NUM_DELAYS { | ||
let samples = delay(i, size); | ||
let mut buffer = VecDeque::with_capacity(samples as usize); | ||
|
||
// Fill in the delay buffers with empty samples | ||
for _ in 0..samples { | ||
buffer.push_back((0.0, 0.0)); | ||
} | ||
|
||
buffers.push(buffer); | ||
} | ||
|
||
DimensionExpander { | ||
buffers: buffers, | ||
dry_wet: dry_wet, | ||
size: size, | ||
} | ||
} | ||
|
||
/// Update the delay buffers with a new size value. | ||
fn resize(&mut self, n: f32) { | ||
let old_size = mem::replace(&mut self.size, n); | ||
|
||
for (i, buffer) in self.buffers.iter_mut().enumerate() { | ||
// Calculate the size difference between delays | ||
let old_delay = delay(i, old_size); | ||
let new_delay = delay(i, n); | ||
|
||
let diff = new_delay - old_delay; | ||
|
||
// Add empty samples if the delay was increased, remove if decreased | ||
if diff > 0 { | ||
for _ in 0..diff { | ||
buffer.push_back((0.0, 0.0)); | ||
} | ||
} else if diff < 0 { | ||
for _ in 0..-diff { | ||
let _ = buffer.pop_front(); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Plugin for DimensionExpander { | ||
fn get_info(&self) -> Info { | ||
Info { | ||
name: "Dimension Expander".to_string(), | ||
vendor: "overdrivenpotato".to_string(), | ||
unique_id: 243723071, | ||
version: 0001, | ||
inputs: 2, | ||
outputs: 2, | ||
parameters: 2, | ||
category: Category::Effect, | ||
|
||
..Default::default() | ||
} | ||
} | ||
|
||
fn get_parameter(&self, index: i32) -> f32 { | ||
match index { | ||
0 => self.size, | ||
1 => self.dry_wet, | ||
_ => 0.0, | ||
} | ||
} | ||
|
||
fn get_parameter_text(&self, index: i32) -> String { | ||
match index { | ||
0 => format!("{}", (self.size * 1000.0) as isize), | ||
1 => format!("{:.1}%", self.dry_wet * 100.0), | ||
_ => "".to_string() | ||
} | ||
} | ||
|
||
fn get_parameter_name(&self, index: i32) -> String { | ||
match index { | ||
0 => "Size", | ||
1 => "Dry/Wet", | ||
_ => "", | ||
}.to_string() | ||
} | ||
|
||
fn set_parameter(&mut self, index: i32, val: f32) { | ||
match index { | ||
0 => self.resize(val), | ||
1 => self.dry_wet = val, | ||
_ => (), | ||
} | ||
} | ||
|
||
fn process(&mut self, buffer: AudioBuffer<f32>) { | ||
let (inputs, mut outputs) = buffer.split(); | ||
|
||
// Assume 2 channels | ||
if inputs.len() < 2 || outputs.len() < 2 { | ||
return; | ||
} | ||
|
||
// Iterate over inputs as (&f32, &f32) | ||
let stereo_in = match inputs.split_at(1) { | ||
(l, r) => l[0].iter().zip(r[0].iter()) | ||
}; | ||
|
||
// Iterate over outputs as (&mut f32, &mut f32) | ||
let stereo_out = match outputs.split_at_mut(1) { | ||
(l, r) => l[0].iter_mut().zip(r[0].iter_mut()) | ||
}; | ||
|
||
// Zip and process | ||
for ((left_in, right_in), (left_out, right_out)) in stereo_in.zip(stereo_out) { | ||
// Push the new samples into the delay buffers. | ||
for buffer in self.buffers.iter_mut() { | ||
buffer.push_back((*left_in, *right_in)); | ||
} | ||
|
||
let mut left_processed = 0.0; | ||
let mut right_processed = 0.0; | ||
|
||
// Recalculate time per sample | ||
let time_s = time::precise_time_ns() as f64 / 1_000_000_000.0; | ||
|
||
// Use buffer index to offset volume LFO | ||
for (n, buffer) in self.buffers.iter_mut().enumerate() { | ||
if let Some((left_old, right_old)) = buffer.pop_front() { | ||
const LFO_FREQ: f64 = 0.5; | ||
const WET_MULT: f32 = 0.66; | ||
|
||
let offset = 0.25 * (n % 4) as f64; | ||
|
||
// Sine wave volume LFO | ||
let lfo = ( | ||
(time_s * LFO_FREQ + offset) * PI * 2.0 | ||
).sin() as f32; | ||
|
||
let wet = self.dry_wet * WET_MULT; | ||
let mono = (left_old + right_old) / 2.0; | ||
|
||
// Flip right channel and keep left mono so that the result is | ||
// entirely stereo | ||
left_processed += mono * wet * lfo; | ||
right_processed += -mono * wet * lfo; | ||
} | ||
} | ||
|
||
// By only adding to the input, the output value always remains the same in mono | ||
*left_out = *left_in + left_processed; | ||
*right_out = *right_in + right_processed; | ||
} | ||
} | ||
} | ||
|
||
plugin_main!(DimensionExpander); |