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

Split plugin API to make it thread safe. #49 #65

Merged
merged 20 commits into from
Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ documentation = "https://rust-dsp.github.io/rust-vst/vst/"
license = "MIT"
keywords = ["vst", "vst2", "plugin"]

autoexamples = false

[dependencies]
log = "0.4"
num-traits = "0.2"
Expand Down Expand Up @@ -56,3 +58,7 @@ crate-type = ["cdylib"]
[[example]]
name = "gain_effect"
crate-type = ["cdylib"]

[[example]]
name = "transfer_and_smooth"
crate-type = ["cdylib"]
104 changes: 64 additions & 40 deletions examples/dimension_expander.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
extern crate vst;
extern crate time;

use vst::plugin::{Category, Info, Plugin};
use vst::buffer::AudioBuffer;
use vst::plugin::{Category, Info, Plugin, PluginParameters};
use vst::util::AtomicFloat;

use std::mem;
use std::f64::consts::PI;
use std::collections::VecDeque;
use std::f64::consts::PI;
use std::sync::Arc;

/// Calculate the length in samples for a delay. Size ranges from 0.0 to 1.0.
fn delay(index: usize, mut size: f32) -> isize {
Expand All @@ -34,8 +35,13 @@ type SamplePair = (f32, f32);
/// The Dimension Expander.
struct DimensionExpander {
buffers: Vec<VecDeque<SamplePair>>,
dry_wet: f32,
size: f32,
params: Arc<DimensionExpanderParameters>,
old_size: f32,
}

struct DimensionExpanderParameters {
dry_wet: AtomicFloat,
size: AtomicFloat,
}

impl Default for DimensionExpander {
Expand Down Expand Up @@ -65,14 +71,17 @@ impl DimensionExpander {

DimensionExpander {
buffers: buffers,
dry_wet: dry_wet,
size: size,
params: Arc::new(DimensionExpanderParameters {
dry_wet: AtomicFloat::new(dry_wet),
size: AtomicFloat::new(size),
}),
old_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);
let old_size = self.old_size;

for (i, buffer) in self.buffers.iter_mut().enumerate() {
// Calculate the size difference between delays
Expand All @@ -92,6 +101,8 @@ impl DimensionExpander {
}
}
}

self.old_size = n;
}
}

Expand All @@ -111,37 +122,6 @@ impl Plugin for DimensionExpander {
}
}

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: &mut AudioBuffer<f32>) {
let (inputs, mut outputs) = buffer.split();

Expand All @@ -150,6 +130,12 @@ impl Plugin for DimensionExpander {
return;
}

// Resize if size changed
let size = self.params.size.get();
if size != self.old_size {
self.resize(size);
}

// Iterate over inputs as (&f32, &f32)
let (l, r) = inputs.split_at(1);
let stereo_in = l[0].iter().zip(r[0].iter());
Expand Down Expand Up @@ -182,7 +168,7 @@ impl Plugin for DimensionExpander {
// 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 wet = self.params.dry_wet.get() * WET_MULT;
let mono = (left_old + right_old) / 2.0;

// Flip right channel and keep left mono so that the result is
Expand All @@ -197,6 +183,44 @@ impl Plugin for DimensionExpander {
*right_out = *right_in + right_processed;
}
}

fn get_parameter_object(&mut self) -> Arc<PluginParameters> {
Arc::clone(&self.params) as Arc<PluginParameters>
}
}

impl PluginParameters for DimensionExpanderParameters {
fn get_parameter(&self, index: i32) -> f32 {
match index {
0 => self.size.get(),
1 => self.dry_wet.get(),
_ => 0.0,
}
}

fn get_parameter_text(&self, index: i32) -> String {
match index {
0 => format!("{}", (self.size.get() * 1000.0) as isize),
1 => format!("{:.1}%", self.dry_wet.get() * 100.0),
_ => "".to_string(),
}
}

fn get_parameter_name(&self, index: i32) -> String {
match index {
0 => "Size",
1 => "Dry/Wet",
_ => "",
}.to_string()
}

fn set_parameter(&self, index: i32, val: f32) {
match index {
0 => self.size.set(val),
1 => self.dry_wet.set(val),
_ => (),
}
}
}

plugin_main!(DimensionExpander);
82 changes: 56 additions & 26 deletions examples/gain_effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,32 @@ extern crate vst;
extern crate time;

use vst::buffer::AudioBuffer;
use vst::plugin::{Category, Info, Plugin};
use vst::plugin::{Category, Info, Plugin, PluginParameters};
use vst::util::AtomicFloat;

use std::sync::Arc;

/// Simple Gain Effect.
/// Note that this does not use a proper scale for sound and shouldn't be used in
/// a production amplification effect! This is purely for demonstration purposes,
/// as well as to keep things simple as this is meant to be a starting point for
/// any effect.
struct GainEffect {
// Here, we can store a variable that keeps track of the plugin's state.
amplitude: f32,
// Store a handle to the plugin's parameter object.
params: Arc<GainEffectParameters>,
}

/// The plugin's parameter object contains the values of parameters that can be
/// adjusted from the host. If we were creating an effect that didn't allow the
/// user to modify it at runtime or have any controls, we could omit this part.
///
/// The parameters object is shared between the processing and GUI threads.
/// For this reason, all mutable state in the object has to be represented
/// through thread-safe interior mutability. The easiest way to achieve this
/// is to store the parameters in atomic containers.
struct GainEffectParameters {
// The plugin's state consists of a single parameter: amplitude.
amplitude: AtomicFloat,
}

// All plugins using the `vst` crate will either need to implement the `Default`
Expand All @@ -23,7 +39,13 @@ struct GainEffect {
// 0.5 means it's halfway up.
impl Default for GainEffect {
fn default() -> GainEffect {
GainEffect { amplitude: 0.5f32 }
GainEffect { params: Arc::new(GainEffectParameters::default()) }
}
}

impl Default for GainEffectParameters {
fn default() -> GainEffectParameters {
GainEffectParameters { amplitude: AtomicFloat::new(0.5) }
}
}

Expand All @@ -46,20 +68,42 @@ impl Plugin for GainEffect {
}
}

// the `get_parameter` and `set_parameter` functions are required if we
// want to interact with the plugin. If we were creating an effect that
// didn't allow the user to modify it at runtime or have any controls,
// we could omit these next parts.
// Here is where the bulk of our audio processing code goes.
fn process(&mut self, buffer: &mut AudioBuffer<f32>) {
// Read the amplitude from the parameter object
let amplitude = self.params.amplitude.get();
// First, we destructure our audio buffer into an arbitrary number of
// input and output buffers. Usually, we'll be dealing with stereo (2 of each)
// but that might change.
for (input_buffer, output_buffer) in buffer.zip() {
// Next, we'll loop through each individual sample so we can apply the amplitude
// value to it.
for (input_sample, output_sample) in input_buffer.iter().zip(output_buffer) {
*output_sample = *input_sample * amplitude;
}
}
}

// Return the parameter object. This method can be omitted if the
// plugin has no parameters.
fn get_parameter_object(&mut self) -> Arc<PluginParameters> {
Arc::clone(&self.params) as Arc<PluginParameters>
}
}

impl PluginParameters for GainEffectParameters {
// the `get_parameter` function reads the value of a parameter.
fn get_parameter(&self, index: i32) -> f32 {
match index {
0 => self.amplitude,
0 => self.amplitude.get(),
_ => 0.0,
}
}

fn set_parameter(&mut self, index: i32, val: f32) {
// the `set_parameter` function sets the value of a parameter.
fn set_parameter(&self, index: i32, val: f32) {
match index {
0 => self.amplitude = val,
0 => self.amplitude.set(val),
_ => (),
}
}
Expand All @@ -68,7 +112,7 @@ impl Plugin for GainEffect {
// format it into a string that makes the most since.
fn get_parameter_text(&self, index: i32) -> String {
match index {
0 => format!("{:.2}", (self.amplitude - 0.5) * 2f32),
0 => format!("{:.2}", (self.amplitude.get() - 0.5) * 2f32),
_ => "".to_string(),
}
}
Expand All @@ -80,20 +124,6 @@ impl Plugin for GainEffect {
_ => "",
}.to_string()
}

// Here is where the bulk of our audio processing code goes.
fn process(&mut self, buffer: &mut AudioBuffer<f32>) {
// First, we destructure our audio buffer into an arbitrary number of
// input and output buffers. Usually, we'll be dealing with stereo (2 of each)
// but that might change.
for (input_buffer, output_buffer) in buffer.zip() {
// Next, we'll loop through each individual sample so we can apply the amplitude
// value to it.
for (input_sample, output_sample) in input_buffer.iter().zip(output_buffer) {
*output_sample = *input_sample * self.amplitude;
}
}
}
}

// This part is important! Without it, our plugin won't work.
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use vst::plugin::Plugin;
struct SampleHost;

impl Host for SampleHost {
fn automate(&mut self, index: i32, value: f32) {
fn automate(&self, index: i32, value: f32) {
println!("Parameter {} had its value changed to {}", index, value);
}
}
Expand Down
Loading