Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
Add dimension expander example
Browse files Browse the repository at this point in the history
  • Loading branch information
overdrivenpotato committed Dec 7, 2016
1 parent 483aee0 commit a5c191e
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

# Generated by Cargo
/target/
/examples/*/target/
Cargo.lock

# Vim
Expand Down
12 changes: 12 additions & 0 deletions examples/dimension_expander/Cargo.toml
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"]
204 changes: 204 additions & 0 deletions examples/dimension_expander/src/lib.rs
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);

0 comments on commit a5c191e

Please sign in to comment.