diff --git a/.appveyor.yml b/.appveyor.yml index 79755bc4..533aedfd 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,4 +1,4 @@ -# Appveyor configuration template for Rust +# Appveyor configuration template for Rust using rustup for Rust installation # https://github.com/starkat99/appveyor-rust ## Operating System (VM environment) ## @@ -23,8 +23,15 @@ os: Visual Studio 2015 # Where ARCH is the target architecture, either x86_64 or i686, and TOOLCHAIN is the linker # toolchain to use, either msvc or gnu. See https://www.rust-lang.org/downloads.html#win-foot for # a description of the toolchain differences. +# See https://github.com/rust-lang-nursery/rustup.rs/#toolchain-specification for description of +# toolchains and host triples. # # Comment out channel/target combos you do not wish to build in CI. +# +# You may use the `cargoflags` and `RUSTFLAGS` variables to set additional flags for cargo commands +# and rustc, respectively. For instance, you can uncomment the cargoflags lines in the nightly +# channels to enable unstable features when building for nightly. Or you could add additional +# matrix entries to test different combinations of features. environment: matrix: @@ -45,9 +52,11 @@ environment: # Nightly 64-bit MSVC - channel: nightly target: x86_64-pc-windows-msvc + #cargoflags: --features "unstable" # Nightly 32-bit MSVC - channel: nightly target: i686-pc-windows-msvc + #cargoflags: --features "unstable" ### GNU Toolchains ### @@ -66,9 +75,11 @@ environment: # Nightly 64-bit GNU - channel: nightly target: x86_64-pc-windows-gnu + #cargoflags: --features "unstable" # Nightly 32-bit GNU - channel: nightly target: i686-pc-windows-gnu + #cargoflags: --features "unstable" ### Allowed failures ### @@ -78,33 +89,24 @@ environment: matrix: allow_failures: - channel: nightly - - channel: beta -# 32-bit MSVC isn't stablized yet, so you may optionally allow failures there (uncomment line): - #- target: i686-pc-windows-msvc +# If you only care about stable channel build failures, uncomment the following line: + #- channel: beta ## Install Script ## # This is the most important part of the Appveyor configuration. This installs the version of Rust -# specified by the 'channel' and 'target' environment variables from the build matrix. By default, -# Rust will be installed to C:\Rust for easy usage, but this path can be overridden by setting the -# RUST_INSTALL_DIR environment variable. The URL to download rust distributions defaults to -# https://static.rust-lang.org/dist/ but can overridden by setting the RUST_DOWNLOAD_URL environment -# variable. -# -# For simple configurations, instead of using the build matrix, you can override the channel and -# target environment variables with the -channel and -target script arguments. +# specified by the 'channel' and 'target' environment variables from the build matrix. This uses +# rustup to install Rust. # -# If no channel or target arguments or environment variables are specified, will default to stable -# channel and x86_64-pc-windows-msvc target. -# -# The file appveyor_rust_install.ps1 must exist in the root directory of the repository. +# For simple configurations, instead of using the build matrix, you can simply set the +# default-toolchain and default-host manually here. install: -- ps: .\appveyor_rust_install.ps1 - -# Alternative install command for simple configurations without build matrix (uncomment line and -# comment above line): -#- ps: .\appveyor_rust_install.ps1 -channel stable -target x86_64-pc-windows-msvc + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init -yv --default-toolchain %channel% --default-host %target% + - set PATH=%PATH%;%USERPROFILE%\.cargo\bin + - rustc -vV + - cargo -vV ## Build Script ## @@ -112,8 +114,8 @@ install: # the "directory does not contain a project or solution file" error. build: false -# Uses 'cargo test' to run tests. Alternatively, the project may call compiled programs directly or -# perform other testing commands. Rust will automatically be placed in the PATH environment -# variable. +# Uses 'cargo test' to run tests and build. Alternatively, the project may call compiled programs +#directly or perform other testing commands. Rust will automatically be placed in the PATH +# environment variable. test_script: -- cmd: cargo test --verbose + - cargo test --verbose %cargoflags% \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 872dd195..902fe7fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,6 @@ num-traits = "0.1" libc = "0.2" bitflags = "0.8" libloading = "0.4" + +[features] +nightly = [] diff --git a/appveyor_rust_install.ps1 b/appveyor_rust_install.ps1 deleted file mode 100644 index 90b34177..00000000 --- a/appveyor_rust_install.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -##### Appveyor Rust Install Script ##### - -# https://github.com/starkat99/appveyor-rust - -# This is the most important part of the Appveyor configuration. This installs the version of Rust -# specified by the "channel" and "target" environment variables from the build matrix. By default, -# Rust will be installed to C:\Rust for easy usage, but this path can be overridden by setting the -# RUST_INSTALL_DIR environment variable. The URL to download rust distributions defaults to -# https://static.rust-lang.org/dist/ but can overridden by setting the RUST_DOWNLOAD_URL environment -# variable. -# -# For simple configurations, instead of using the build matrix, you can override the channel and -# target environment variables with the --channel and --target script arguments. -# -# If no channel or target arguments or environment variables are specified, will default to stable -# channel and x86_64-pc-windows-msvc target. - -param([string]$channel=${env:channel}, [string]$target=${env:target}) - -# Initialize our parameters from arguments and environment variables, falling back to defaults -if (!$channel) { - $channel = "stable" -} -if (!$target) { - $target = "x86_64-pc-windows-msvc" -} - -$downloadUrl = "https://static.rust-lang.org/dist/" -if ($env:RUST_DOWNLOAD_URL) { - $downloadUrl = $env:RUST_DOWNLOAD_URL -} - -$installDir = "C:\Rust" -if ($env:RUST_INSTALL_DIR) { - $installUrl = $env:RUST_INSTALL_DIR -} - -if ($channel -eq "stable") { - # Download manifest so we can find actual filename of installer to download. Needed for stable. - echo "Downloading $channel channel manifest" - $manifest = "${env:Temp}\channel-rust-${channel}" - Start-FileDownload "${downloadUrl}channel-rust-${channel}" -FileName "$manifest" - - # Search the manifest lines for the correct filename based on target - $match = Get-Content "$manifest" | Select-String -pattern "${target}.exe" -simplematch - - if (!$match -or !$match.line) { - throw "Could not find $target in $channel channel manifest" - } - - $installer = $match.line -} else { - # Otherwise download the file specified by channel directly. - $installer = "rust-${channel}-${target}.exe" -} - -# Download installer -echo "Downloading ${downloadUrl}$installer" -Start-FileDownload "${downloadUrl}$installer" -FileName "${env:Temp}\$installer" - -# Execute installer and wait for it to finish -echo "Installing $installer to $installDir" -&"${env:Temp}\$installer" /VERYSILENT /NORESTART /DIR="$installDir" | Write-Output - -# Add Rust to the path. -$env:Path += ";${installDir}\bin;C:\MinGW\bin" - -echo "Installation of $channel Rust $target completed" - -# Test and display installed version information for rustc and cargo -rustc -V -cargo -V diff --git a/examples/dimension_expander/Cargo.toml b/examples/dimension_expander/Cargo.toml index c1c9bb8e..837f577e 100644 --- a/examples/dimension_expander/Cargo.toml +++ b/examples/dimension_expander/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" authors = ["Marko Mijalkovic "] [dependencies] -vst2 = { git = "https://github.com/overdrivenpotato/rust-vst2" } +vst2 = { path = "../.." } time = "0.1" [lib] name = "dimension_expander" -crate-type = ["dylib"] +crate-type = ["cdylib"] diff --git a/examples/dimension_expander/src/lib.rs b/examples/dimension_expander/src/lib.rs index 7cf2dfb6..f5d80fad 100644 --- a/examples/dimension_expander/src/lib.rs +++ b/examples/dimension_expander/src/lib.rs @@ -139,8 +139,7 @@ impl Plugin for DimensionExpander { _ => (), } } - - fn process(&mut self, buffer: AudioBuffer) { + fn process(&mut self, buffer: &mut AudioBuffer) { let (inputs, mut outputs) = buffer.split(); // Assume 2 channels @@ -149,14 +148,12 @@ impl Plugin for DimensionExpander { } // Iterate over inputs as (&f32, &f32) - let stereo_in = match inputs.split_at(1) { - (l, r) => l[0].iter().zip(r[0].iter()) - }; + let (l, r) = inputs.split_at(1); + let stereo_in = 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()) - }; + let (mut l, mut r) = outputs.split_at_mut(1); + let stereo_out = 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) { diff --git a/examples/sine_synth/Cargo.toml b/examples/sine_synth/Cargo.toml index a7948e69..a734c2a0 100644 --- a/examples/sine_synth/Cargo.toml +++ b/examples/sine_synth/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Rob Saunders "] [dependencies] -vst2 = { git = "https://github.com/overdrivenpotato/rust-vst2" } +vst2 = { path = "../.." } [lib] name = "sine_synth" diff --git a/examples/sine_synth/src/lib.rs b/examples/sine_synth/src/lib.rs index e72770ee..eba8d34d 100644 --- a/examples/sine_synth/src/lib.rs +++ b/examples/sine_synth/src/lib.rs @@ -3,17 +3,18 @@ use vst2::buffer::AudioBuffer; use vst2::plugin::{Category, Plugin, Info, CanDo}; use vst2::event::Event; -use vst2::api::Supported; +use vst2::api::{Supported, Events}; use std::f64::consts::PI; -/// Convert the midi note into the equivalent frequency. +/// Convert the midi note's pitch into the equivalent frequency. /// /// This function assumes A4 is 440hz. -fn midi_note_to_hz(note: u8) -> f64 { - const A4: f64 = 440.0; +fn midi_pitch_to_freq(pitch: u8) -> f64 { + const A4_PITCH: u8 = 69; + const A4_FREQ: f64 = 440.0; - (A4 / 32.0) * ((note as f64 - 9.0) / 12.0).exp2() + (((pitch - A4_PITCH) as f64) / 12.).exp2() * A4_FREQ } struct SineSynth { @@ -87,36 +88,41 @@ impl Plugin for SineSynth { } #[allow(unused_variables)] - fn process_events(&mut self, events: Vec) { - for event in events { + fn process_events(&mut self, events: &Events) { + for &e in events.events_raw() { + let event: Event = Event::from(unsafe { *e }); + match event { + Event::Midi(ev) => self.process_midi_event(ev.data), + // More events can be handled here. + _ => () + } + } + /* on nightly you can just enable the "nightly" feature and then do: + for event in events.events() { match event { Event::Midi { data, .. } => self.process_midi_event(data), // More events can be handled here. _ => {} } } + */ } fn set_sample_rate(&mut self, rate: f32) { self.sample_rate = rate as f64; } - fn process(&mut self, buffer: AudioBuffer) { - let (inputs, outputs) = buffer.split(); - - let samples = inputs - .first() - .map(|channel| channel.len()) - .unwrap_or(0); + fn process(&mut self, buffer: &mut AudioBuffer) { + let samples = buffer.samples(); let per_sample = self.time_per_sample(); - for (input_buffer, output_buffer) in inputs.iter().zip(outputs) { + for (input_buffer, output_buffer) in buffer.zip() { let mut t = self.time; for (_, output_sample) in input_buffer.iter().zip(output_buffer) { if let Some(current_note) = self.note { - let signal = (t * midi_note_to_hz(current_note) * TAU).sin(); + let signal = (t * midi_pitch_to_freq(current_note) * TAU).sin(); // Apply a quick envelope to the attack of the signal to avoid popping. let attack = 0.5; diff --git a/src/api.rs b/src/api.rs index fcb23a49..2cd6c265 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,5 +1,4 @@ //! Structures and types for interfacing with the VST 2.4 API. -use std::mem; use std::os::raw::c_void; @@ -34,10 +33,10 @@ pub type HostCallbackProc = fn(effect: *mut AEffect, opcode: i32, index: i32, va pub type DispatcherProc = fn(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize; /// Process function used to process 32 bit floating point samples. Called by host. -pub type ProcessProc = fn(effect: *mut AEffect, inputs: *mut *mut f32, outputs: *mut *mut f32, sample_frames: i32); +pub type ProcessProc = fn(effect: *mut AEffect, inputs: *const *const f32, outputs: *mut *mut f32, sample_frames: i32); /// Process function used to process 64 bit floating point samples. Called by host. -pub type ProcessProcF64 = fn(effect: *mut AEffect, inputs: *mut *mut f64, outputs: *mut *mut f64, sample_frames: i32); +pub type ProcessProcF64 = fn(effect: *mut AEffect, inputs: *const *const f64, outputs: *mut *mut f64, sample_frames: i32); /// Callback function used to set parameter values. Called by host. pub type SetParameterProc = fn(effect: *mut AEffect, index: i32, parameter: f32); @@ -135,13 +134,17 @@ pub struct AEffect { impl AEffect { /// Return handle to Plugin object. Only works for plugins created using this library. pub unsafe fn get_plugin(&mut self) -> &mut Box { - mem::transmute::<_, &mut Box>(self.object) + &mut *(self.object as *mut Box) } /// Drop the Plugin object. Only works for plugins created using this library. pub unsafe fn drop_plugin(&mut self) { - // Possibly a simpler way of doing this..? - drop(mem::transmute::<_, Box>>(self.object)) + drop(Box::from_raw(self.object as *mut Box)); + drop(Box::from_raw(self.user as *mut super::PluginCache)); + } + + pub(crate) unsafe fn get_cache(&mut self) -> &mut super::PluginCache { + &mut *(self.user as *mut _) } } @@ -399,6 +402,77 @@ pub struct Events { pub events: [*mut Event; 2], } +impl Events { + /// Use this in your impl of process_events() to process the incoming midi events (on stable). + /// + /// # Example + /// ```no_run + /// # use vst2::plugin::{Info, Plugin, HostCallback}; + /// # use vst2::buffer::{AudioBuffer, SendEventBuffer}; + /// # use vst2::host::Host; + /// # use vst2::api; + /// # use vst2::event::Event; + /// # struct ExamplePlugin { host: HostCallback, send_buf: SendEventBuffer } + /// # impl Plugin for ExamplePlugin { + /// # fn get_info(&self) -> Info { Default::default() } + /// # + /// fn process_events(&mut self, events: &api::Events) { + /// for &e in events.events_raw() { + /// let event: Event = Event::from(unsafe { *e }); + /// match event { + /// Event::Midi(ev) => { + /// // ... + /// } + /// _ => () + /// } + /// } + /// } + /// # } + /// ``` + #[inline(always)] + pub fn events_raw(&self) -> &[*const Event] { + use std::slice; + unsafe { slice::from_raw_parts(&self.events[0] as *const *mut _ as *const *const _, self.num_events as usize) } + } + + #[inline(always)] + pub(crate) fn events_raw_mut(&mut self) -> &mut [*const SysExEvent] { + use std::slice; + unsafe { slice::from_raw_parts_mut(&mut self.events[0] as *mut *mut _ as *mut *const _, self.num_events as usize) } + } + + /// Use this in your impl of process_events() to process the incoming midi events (on nightly). + /// + /// # Example + /// ```no_run + /// # use vst2::plugin::{Info, Plugin, HostCallback}; + /// # use vst2::buffer::{AudioBuffer, SendEventBuffer}; + /// # use vst2::host::Host; + /// # use vst2::api; + /// # use vst2::event::Event; + /// # struct ExamplePlugin { host: HostCallback, send_buf: SendEventBuffer } + /// # impl Plugin for ExamplePlugin { + /// # fn get_info(&self) -> Info { Default::default() } + /// # + /// fn process_events(&mut self, events: &api::Events) { + /// for e in events.events() { + /// match e { + /// Event::Midi { data, .. } => { + /// // ... + /// } + /// _ => () + /// } + /// } + /// } + /// # } + /// ``` + #[cfg(feature = "nightly")] + #[inline(always)] + pub fn events<'a>(&'a self) -> impl Iterator + 'a { + self.events_raw().into_iter().map(|&e| ::event::Event::from(unsafe { *e })) + } +} + /// The type of event that has occured. See `api::Event.event_type`. #[repr(i32)] #[derive(Copy, Clone, Debug)] @@ -533,6 +607,7 @@ pub struct MidiEvent { /// `plugin::CanDo` has a `ReceiveSysExEvent` variant which lets the host query the plugin as to /// whether this event is supported. #[repr(C)] +#[derive(Clone)] pub struct SysExEvent { /// Should be `EventType::SysEx`. pub event_type: EventType, diff --git a/src/buffer.rs b/src/buffer.rs index 4ac73a79..ed092fc3 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,87 +1,359 @@ //! Buffers to safely work with audio samples. -use std::iter::{Zip, IntoIterator}; -use std::vec::IntoIter; -use std::slice; - use num_traits::Float; -/// A buffer containing `ChannelBuffer` buffers for each input/output. +use std::slice; +use std::iter::Zip; + +/// AudioBuffer contains references to the audio buffers for all input and output channels pub struct AudioBuffer<'a, T: 'a + Float> { - inputs: Vec<&'a mut [T]>, - outputs: Vec<&'a mut [T]>, + inputs: &'a [*const T], + outputs: &'a mut [*mut T], + samples: usize, } -/// Iterator over channel buffers for either inputs or outputs. -pub type ChannelBufferIter<'a, T> = IntoIter<&'a mut [T]>; - impl<'a, T: 'a + Float> AudioBuffer<'a, T> { - /// Create an `AudioBuffer` from vectors of slices. - /// - /// Each vector item represents either an input or output, and contains an array of samples. Eg - /// if inputs was a vector of size 2 containing slices of size 512, it would hold 2 inputs where - /// each input holds 512 samples. - pub fn new(inputs: Vec<&'a mut [T]>, outputs: Vec<&'a mut [T]>) -> AudioBuffer<'a, T> { - AudioBuffer { + + /// Create an `AudioBuffer` from slices of raw pointers. Useful in a Rust VST host. + #[inline(always)] + pub fn new(inputs: &'a [*const T], outputs: &'a mut [*mut T], samples: usize) -> Self { + Self { inputs: inputs, outputs: outputs, + samples: samples, + } + } + + /// Create an `AudioBuffer` from raw pointers. Only really useful for interacting with the VST API. + #[inline(always)] + pub fn from_raw(input_count: usize, output_count: usize, + inputs_raw: *const *const T, outputs_raw: *mut *mut T, samples: usize) -> Self { + Self { + inputs: unsafe { slice::from_raw_parts(inputs_raw, input_count) }, + outputs: unsafe { slice::from_raw_parts_mut(outputs_raw, output_count) }, + samples: samples, + } + } + + /// The number of input channels that this buffer was created for + #[inline(always)] + pub fn input_count(&self) -> usize { self.inputs.len() } + + /// The number of output channels that this buffer was created for + #[inline(always)] + pub fn output_count(&self) -> usize { self.outputs.len() } + + /// The number of samples in this buffer (same for all channels) + #[inline(always)] + pub fn samples(&self) -> usize { self.samples } + + /// The raw inputs to pass to processReplacing + #[inline(always)] + pub(crate) fn raw_inputs(&self) -> &[*const T] { &self.inputs } + + /// The raw outputs to pass to processReplacing + #[inline(always)] + pub(crate) fn raw_outputs(&mut self) -> &mut [*mut T] { &mut self.outputs } + + /// Split this buffer into separate inputs and outputs. + #[inline(always)] + pub fn split<'b>(&'b mut self) -> (Inputs<'b, T>, Outputs<'b, T>) where 'a: 'b { + ( + Inputs { bufs: &self.inputs, samples: self.samples }, + Outputs { bufs: &self.outputs, samples: self.samples } + ) + } + + /// Zip together buffers. + #[inline(always)] + pub fn zip<'b>(&'b mut self) -> Zip, OutputIterator<'b, T>> where 'a: 'b { + let (inputs, outputs) = self.split(); + inputs.into_iter().zip(outputs) + } +} + +use std::ops::{Index, IndexMut}; + +/// Wrapper type to access the buffers for the input channels of an AudioBuffer in a safe way. +/// Behaves like a slice. +#[derive(Copy, Clone)] +pub struct Inputs<'a, T: 'a> { + bufs: &'a [*const T], + samples: usize, +} + +impl<'a, T> Inputs<'a, T> { + + /// Number of channels + pub fn len(&self) -> usize { self.bufs.len() } + + /// Access channel at the given index, unchecked + pub fn get(&self, i: usize) -> &'a [T] { + unsafe { slice::from_raw_parts(self.bufs[i], self.samples) } + } + + /// Split borrowing at the given index, like for slices + pub fn split_at(&self, i: usize) -> (Inputs<'a, T>, Inputs<'a, T>) { + let (l, r) = self.bufs.split_at(i); + ( + Inputs { bufs: &l, samples: self.samples }, + Inputs { bufs: &r, samples: self.samples } + ) + } +} + +impl<'a, T> Index for Inputs<'a, T> { + type Output = [T]; + + fn index(&self, i: usize) -> &Self::Output { + self.get(i) + } +} + +/// Iterator over buffers for input channels of an AudioBuffer. +pub struct InputIterator<'a, T: 'a> { + data: Inputs<'a, T>, + i: usize, +} + +impl<'a, T> Iterator for InputIterator<'a, T> { + type Item = &'a [T]; + + fn next(&mut self) -> Option { + if self.i < self.data.len() { + let val = self.data.get(self.i); + self.i += 1; + Some(val) + } else { + None + } + } +} + +impl<'a, T: Sized> IntoIterator for Inputs<'a, T> { + type Item = &'a [T]; + type IntoIter = InputIterator<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + InputIterator { data: self, i: 0 } + } +} + +/// Wrapper type to access the buffers for the output channels of an AudioBuffer in a safe way. +/// Behaves like a slice. +#[derive(Copy, Clone)] +pub struct Outputs<'a, T: 'a> { + bufs: &'a [*mut T], + samples: usize, +} + +impl<'a, T> Outputs<'a, T> { + + /// Number of channels + pub fn len(&self) -> usize { self.bufs.len() } + + /// Access channel at the given index, unchecked + pub fn get(&self, i: usize) -> &'a [T] { + unsafe { slice::from_raw_parts(self.bufs[i], self.samples) } + } + + /// Mutably access channel at the given index, unchecked + pub fn get_mut(&self, i: usize) -> &'a mut [T] { + unsafe { slice::from_raw_parts_mut(self.bufs[i], self.samples) } + } + + /// Split borrowing at the given index, like for slices + pub fn split_at_mut(&mut self, i: usize) -> (Outputs<'a, T>, Outputs<'a, T>) { + let (l, r) = self.bufs.split_at(i); + ( + Outputs { bufs: &l, samples: self.samples }, + Outputs { bufs: &r, samples: self.samples } + ) + } +} + +impl<'a, T> Index for Outputs<'a, T> { + type Output = [T]; + + fn index(&self, i: usize) -> &Self::Output { + self.get(i) + } +} + +impl<'a, T> IndexMut for Outputs<'a, T> { + fn index_mut(&mut self, i: usize) -> &mut Self::Output { + self.get_mut(i) + } +} + +/// Iterator over buffers for output channels of an AudioBuffer. +pub struct OutputIterator<'a, T: 'a> { + data: Outputs<'a, T>, + i: usize, +} + +impl<'a, T> Iterator for OutputIterator<'a, T> { + type Item = &'a mut [T]; + + fn next(&mut self) -> Option { + if self.i < self.data.len() { + let val = self.data.get_mut(self.i); + self.i += 1; + Some(val) + } else { + None } } +} + +impl<'a, T: Sized> IntoIterator for Outputs<'a, T> { + type Item = &'a mut [T]; + type IntoIter = OutputIterator<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + OutputIterator { data: self, i: 0 } + } +} - /// Create an `AudioBuffer` from raw pointers. Only really useful for interacting with the VST - /// API. - pub unsafe fn from_raw(inputs_raw: *mut *mut T, outputs_raw: *mut *mut T, num_inputs: usize, num_outputs: usize, samples: usize) -> AudioBuffer<'a, T> { - let inputs = - // Create a slice of type &mut [*mut f32] - slice::from_raw_parts_mut(inputs_raw, num_inputs).iter() - // Convert to &mut [&mut [f32]] - .map(|input| slice::from_raw_parts_mut(*input, samples)) - // Collect into Vec<&mut [f32]> - .collect(); +use event::{Event, MidiEvent}; +use api; +use std::mem; +use std::borrow::Borrow; - let outputs = - // Create a slice of type &mut [*mut f32] - slice::from_raw_parts_mut(outputs_raw, num_outputs).iter() - // Convert to &mut [&mut [f32]] - .map(|output| slice::from_raw_parts_mut(*output, samples)) - // Collect into Vec<&mut [f32]> - .collect(); +/// This buffer is used for sending midi events through the VST interface. +/// The purpose of this is to convert outgoing midi events from event::Event to api::Events. +/// It only allocates memory in new() and reuses the memory between calls. +pub struct SendEventBuffer { + buf: Vec, + api_events: Vec, +} - // Call constructor with vectors - AudioBuffer::new(inputs, outputs) +impl Default for SendEventBuffer { + fn default() -> Self { + SendEventBuffer::new(1024) } +} + +impl SendEventBuffer { - /// Return a reference to all inputs. - pub fn inputs(&'a mut self) -> &'a mut Vec<&'a mut [T]> { - &mut self.inputs + /// Creates a buffer for sending up to the given number of midi events per frame + #[inline(always)] + pub fn new(capacity: usize) -> Self { + let header_size = mem::size_of::() - (mem::size_of::<*mut api::Event>() * 2); + let body_size = mem::size_of::<*mut api::Event>() * capacity; + let mut buf = vec![0u8; header_size + body_size]; + let api_events = vec![unsafe { mem::zeroed::() }; capacity]; + { + let ptrs = { + let e = Self::buf_as_api_events(&mut buf); + e.num_events = capacity as i32; + e.events_raw_mut() + }; + for (ptr, event) in ptrs.iter_mut().zip(&api_events) { + let (ptr, event): (&mut *const api::SysExEvent, &api::SysExEvent) = (ptr, event); + *ptr = event; + } + } + Self { + buf: buf, + api_events: api_events, + } } - /// Return a reference to all outputs. - pub fn outputs(&'a mut self) -> &'a mut Vec<&'a mut [T]> { - &mut self.outputs + #[inline(always)] + fn buf_as_api_events(buf: &mut [u8]) -> &mut api::Events { + unsafe { &mut *(buf.as_mut_ptr() as *mut api::Events) } } - /// Consume this buffer and split it into separate inputs and outputs. + /// Use this for sending events to a host or plugin. /// /// # Example - /// - /// ``` - /// # use vst2::buffer::AudioBuffer; - /// # let mut in1 = vec![0.0; 512]; - /// # let (mut in2, mut out1, mut out2) = (in1.clone(), in1.clone(), in1.clone()); + /// ```no_run + /// # use vst2::plugin::{Info, Plugin, HostCallback}; + /// # use vst2::buffer::{AudioBuffer, SendEventBuffer}; + /// # use vst2::host::Host; + /// # struct ExamplePlugin { host: HostCallback, send_buffer: SendEventBuffer } + /// # impl Plugin for ExamplePlugin { + /// # fn get_info(&self) -> Info { Default::default() } /// # - /// # let buffer = AudioBuffer::new(vec![&mut in1, &mut in2], - /// # vec![&mut out1, &mut out2]); - /// let (mut inputs, mut outputs) = buffer.split(); - /// let input: &mut [f32] = &mut inputs[0]; // First input + /// // Processor that clips samples above 0.4 or below -0.4: + /// fn process(&mut self, buffer: &mut AudioBuffer){ + /// let events = vec![ + /// // ... + /// ]; + /// self.send_buffer.store(&events); + /// self.host.process_events(self.send_buffer.events()); + /// } + /// # } /// ``` - pub fn split(self) -> (Vec<&'a mut [T]>, Vec<&'a mut [T]>) { - (self.inputs, self.outputs) + pub fn store<'a, T: IntoIterator, U: Borrow>>(&mut self, events: T) { + let count = events.into_iter().zip(self.api_events.iter_mut()).map(|(event, out)| { + let (event, out): (&Event, &mut api::SysExEvent) = (event.borrow(), out); + match *event { + Event::Midi(ev) => { + Self::store_midi_impl(out, &ev); + } + Event::SysEx(ev) => { + *out = api::SysExEvent { + event_type: api::EventType::SysEx, + byte_size: mem::size_of::() as i32, + delta_frames: ev.delta_frames, + _flags: 0, + data_size: ev.payload.len() as i32, + _reserved1: 0, + system_data: ev.payload.as_ptr() as *const u8 as *mut u8, + _reserved2: 0, + }; + } + Event::Deprecated(e) => { + let out = unsafe { &mut *(out as *mut _ as *mut _) }; + *out = e; + } + }; + }).count(); + self.set_num_events(count); } - /// Zip together buffers. - pub fn zip(self) -> Zip, ChannelBufferIter<'a, T>> { - self.inputs.into_iter().zip(self.outputs.into_iter()) + /// Use this for sending midi events to a host or plugin. + /// Like store() but for when you're not sending any SysExEvents, only MidiEvents. + pub fn store_midi, U: Borrow>(&mut self, events: T) { + let count = events.into_iter().zip(self.api_events.iter_mut()).map(|(event, out)| { + let (ev, out): (&MidiEvent, &mut api::SysExEvent) = (event.borrow(), out); + Self::store_midi_impl(out, ev); + }).count(); + self.set_num_events(count); + } + + fn store_midi_impl(out: &mut api::SysExEvent, ev: &MidiEvent) { + use api::flags::REALTIME_EVENT; + let out = unsafe { &mut *(out as *mut _ as *mut _) }; + *out = api::MidiEvent { + event_type: api::EventType::Midi, + byte_size: mem::size_of::() as i32, + delta_frames: ev.delta_frames, + flags: if ev.live { REALTIME_EVENT.bits() } else { 0 }, + note_length: ev.note_length.unwrap_or(0), + note_offset: ev.note_offset.unwrap_or(0), + midi_data: ev.data, + _midi_reserved: 0, + detune: ev.detune, + note_off_velocity: ev.note_off_velocity, + _reserved1: 0, + _reserved2: 0 + }; + } + + fn set_num_events(&mut self, events_len: usize) { + use std::cmp::min; + let e = Self::buf_as_api_events(&mut self.buf); + e.num_events = min(self.api_events.len(), events_len) as i32; + } + + /// Use this for sending midi events to a host or plugin. + /// See `store()` + #[inline(always)] + pub fn events(&self) -> &api::Events { + unsafe { &*(self.buf.as_ptr() as *const api::Events) } } } @@ -99,14 +371,15 @@ mod tests { /// This test assures that when the buffers are zipped together, the input values do not change. #[test] fn buffer_zip() { - let mut in1: Vec = (0..SIZE).map(|x| x as f32).collect(); - let mut in2 = in1.clone(); + let in1: Vec = (0..SIZE).map(|x| x as f32).collect(); + let in2 = in1.clone(); let mut out1 = vec![0.0; SIZE]; let mut out2 = out1.clone(); - let buffer = AudioBuffer::new(vec![&mut in1, &mut in2], - vec![&mut out1, &mut out2]); + let inputs = vec![in1.as_ptr(), in2.as_ptr()]; + let mut outputs = vec![out1.as_mut_ptr(), out2.as_mut_ptr()]; + let mut buffer = AudioBuffer::new(&inputs, &mut outputs, SIZE); for (input, output) in buffer.zip() { input.into_iter().zip(output.into_iter()) @@ -121,17 +394,15 @@ mod tests { /// Test that creating buffers from raw pointers works. #[test] fn from_raw() { - let mut in1: Vec = (0..SIZE).map(|x| x as f32).collect(); - let mut in2 = in1.clone(); + let in1: Vec = (0..SIZE).map(|x| x as f32).collect(); + let in2 = in1.clone(); let mut out1 = vec![0.0; SIZE]; let mut out2 = out1.clone(); - let buffer = unsafe { - AudioBuffer::from_raw(vec![in1.as_mut_ptr(), in2.as_mut_ptr()].as_mut_ptr(), - vec![out1.as_mut_ptr(), out2.as_mut_ptr()].as_mut_ptr(), - 2, 2, SIZE) - }; + let inputs = vec![in1.as_ptr(), in2.as_ptr()]; + let mut outputs = vec![out1.as_mut_ptr(), out2.as_mut_ptr()]; + let mut buffer = AudioBuffer::from_raw(2, 2, inputs.as_ptr(), outputs.as_mut_ptr(), SIZE); for (input, output) in buffer.zip() { input.into_iter().zip(output.into_iter()) @@ -142,4 +413,4 @@ mod tests { }); } } -} +} \ No newline at end of file diff --git a/src/cache.rs b/src/cache.rs new file mode 100644 index 00000000..93590faa --- /dev/null +++ b/src/cache.rs @@ -0,0 +1,13 @@ +use plugin::Info; + +pub(crate) struct PluginCache { + pub info: Info, +} + +impl PluginCache { + pub fn new(info: &Info) -> Self { + Self { + info: info.clone(), + } + } +} \ No newline at end of file diff --git a/src/event.rs b/src/event.rs index daff3350..df0a74ef 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,54 +7,19 @@ use api::flags::*; use api::{self, flags}; /// A VST event. +#[derive(Copy, Clone)] pub enum Event<'a> { /// A midi event. /// /// These are sent to the plugin before `Plugin::processing()` or `Plugin::processing_f64()` is /// called. - Midi { - /// The raw midi data associated with this event. - data: [u8; 3], - - /// Number of samples into the current processing block that this event occurs on. - /// - /// E.g. if the block size is 512 and this value is 123, the event will occur on sample - /// `samples[123]`. - // TODO: Don't repeat this value in all event types - delta_frames: i32, - - /// This midi event was created live as opposed to being played back in the sequencer. - /// - /// This can give the plugin priority over this event if it introduces a lot of latency. - live: bool, - - /// The length of the midi note associated with this event, if available. - note_length: Option, - - /// Offset in samples into note from note start, if available. - note_offset: Option, - - /// Detuning between -63 and +64 cents. - detune: i8, - - /// Note off velocity between 0 and 127. - note_off_velocity: u8, - }, + Midi(MidiEvent), /// A system exclusive event. /// /// This is just a block of data and it is up to the plugin to interpret this. Generally used /// by midi controllers. - SysEx { - /// The SysEx payload. - payload: &'a [u8], - - /// Number of samples into the current processing block that this event occurs on. - /// - /// E.g. if the block size is 512 and this value is 123, the event will occur on sample - /// `samples[123]`. - delta_frames: i32, - }, + SysEx(SysExEvent<'a>), /// A deprecated event. /// @@ -63,6 +28,57 @@ pub enum Event<'a> { Deprecated(api::Event), } + +/// A midi event. +/// +/// These are sent to the plugin before `Plugin::processing()` or `Plugin::processing_f64()` is +/// called. +#[derive(Copy, Clone)] +pub struct MidiEvent { + /// The raw midi data associated with this event. + pub data: [u8; 3], + + /// Number of samples into the current processing block that this event occurs on. + /// + /// E.g. if the block size is 512 and this value is 123, the event will occur on sample + /// `samples[123]`. + // TODO: Don't repeat this value in all event types + pub delta_frames: i32, + + /// This midi event was created live as opposed to being played back in the sequencer. + /// + /// This can give the plugin priority over this event if it introduces a lot of latency. + pub live: bool, + + /// The length of the midi note associated with this event, if available. + pub note_length: Option, + + /// Offset in samples into note from note start, if available. + pub note_offset: Option, + + /// Detuning between -63 and +64 cents. + pub detune: i8, + + /// Note off velocity between 0 and 127. + pub note_off_velocity: u8, +} + +/// A system exclusive event. +/// +/// This is just a block of data and it is up to the plugin to interpret this. Generally used +/// by midi controllers. +#[derive(Copy, Clone)] +pub struct SysExEvent<'a> { + /// The SysEx payload. + pub payload: &'a [u8], + + /// Number of samples into the current processing block that this event occurs on. + /// + /// E.g. if the block size is 512 and this value is 123, the event will occur on sample + /// `samples[123]`. + pub delta_frames: i32, +} + impl<'a> From for Event<'a> { fn from(event: api::Event) -> Event<'a> { use api::EventType::*; @@ -75,7 +91,7 @@ impl<'a> From for Event<'a> { let offset = if event.note_offset > 0 { Some(event.note_offset) } else { None }; let flags = flags::MidiEvent::from_bits(event.flags).unwrap(); - Event::Midi { + Event::Midi(MidiEvent { data: event.midi_data, delta_frames: event.delta_frames, live: flags.intersects(REALTIME_EVENT), @@ -83,10 +99,10 @@ impl<'a> From for Event<'a> { note_offset: offset, detune: event.detune, note_off_velocity: event.note_off_velocity - } + }) } - SysEx => Event::SysEx { + SysEx => Event::SysEx(SysExEvent { payload: unsafe { // We can safely transmute the event pointer to a `SysExEvent` pointer as // event_type refers to a `SysEx` type. @@ -95,7 +111,7 @@ impl<'a> From for Event<'a> { }, delta_frames: event.delta_frames - }, + }), _ => Event::Deprecated(event), } diff --git a/src/host.rs b/src/host.rs index 9522a834..7b2bfb1b 100644 --- a/src/host.rs +++ b/src/host.rs @@ -14,7 +14,6 @@ use plugin::{self, Plugin, Info, Category}; use api::{self, AEffect, PluginMain, Supported}; use api::consts::*; use buffer::AudioBuffer; -use event::Event; use channels::ChannelInfo; #[repr(usize)] @@ -209,7 +208,7 @@ pub trait Host { } /// Handle incoming events from the plugin. - fn process_events(&mut self, events: Vec) {} + fn process_events(&mut self, events: &api::Events) {} } /// All possible errors that can occur when loading a VST plugin. @@ -563,42 +562,27 @@ impl Plugin for PluginInstance { } - fn process(&mut self, buffer: AudioBuffer) { - let (inputs, outputs) = buffer.split(); - let frames = inputs[0].len(); - let mut inputs: Vec<*mut f32> = inputs.into_iter().map(|s| s.as_mut_ptr()).collect(); - let mut outputs: Vec<*mut f32> = outputs.into_iter().map(|s| s.as_mut_ptr()).collect(); - + fn process(&mut self, buffer: &mut AudioBuffer) { unsafe { ((*self.effect).processReplacing) - (self.effect, inputs.as_mut_ptr(), outputs.as_mut_ptr(), frames as i32) + (self.effect, buffer.raw_inputs().as_ptr() as *const *const _, buffer.raw_outputs().as_mut_ptr() as *mut *mut _, buffer.samples() as i32) } } - fn process_f64(&mut self, buffer: AudioBuffer) { - let (inputs, outputs) = buffer.split(); - let frames = inputs[0].len(); - let mut inputs: Vec<*mut f64> = inputs.into_iter().map(|s| s.as_mut_ptr()).collect(); - let mut outputs: Vec<*mut f64> = outputs.into_iter().map(|s| s.as_mut_ptr()).collect(); - + fn process_f64(&mut self, buffer: &mut AudioBuffer) { unsafe { ((*self.effect).processReplacingF64) - (self.effect, inputs.as_mut_ptr(), outputs.as_mut_ptr(), frames as i32) + (self.effect, buffer.raw_inputs().as_ptr() as *const *const _, buffer.raw_outputs().as_mut_ptr() as *mut *mut _, buffer.samples() as i32) } } - fn process_events(&mut self, events: Vec) { - interfaces::process_events( - events, - |ptr| { - self.dispatch( - plugin::OpCode::ProcessEvents, - 0, - 0, - ptr, - 0.0 - ); - } + fn process_events(&mut self, events: &api::Events) { + self.dispatch( + plugin::OpCode::ProcessEvents, + 0, + 0, + events as *const _ as *mut _, + 0.0 ); } diff --git a/src/interfaces.rs b/src/interfaces.rs index 32bb7418..92b77219 100644 --- a/src/interfaces.rs +++ b/src/interfaces.rs @@ -11,42 +11,29 @@ use api::consts::*; use api::{self, AEffect, ChannelProperties}; use editor::{Rect, KeyCode, Key, KnobMode}; use host::Host; -use event::Event; /// Deprecated process function. -pub fn process_deprecated(_effect: *mut AEffect, _inputs_raw: *mut *mut f32, _outputs_raw: *mut *mut f32, _samples: i32) { } +pub fn process_deprecated(_effect: *mut AEffect, _raw_inputs: *const *const f32, _raw_outputs: *mut *mut f32, _samples: i32) { } /// VST2.4 replacing function. -pub fn process_replacing(effect: *mut AEffect, inputs_raw: *mut *mut f32, outputs_raw: *mut *mut f32, samples: i32) { +pub fn process_replacing(effect: *mut AEffect, raw_inputs: *const *const f32, raw_outputs: *mut *mut f32, samples: i32) { // Handle to the vst let mut plugin = unsafe { (*effect).get_plugin() }; - - let buffer = unsafe { - AudioBuffer::from_raw(inputs_raw, - outputs_raw, - plugin.get_info().inputs as usize, - plugin.get_info().outputs as usize, - samples as usize) - }; - - plugin.process(buffer); + let cache = unsafe { (*effect).get_cache() }; + let info = &mut cache.info; + let (input_count, output_count) = (info.inputs as usize, info.outputs as usize); + let mut buffer = AudioBuffer::from_raw(input_count, output_count, raw_inputs, raw_outputs, samples as usize); + plugin.process(&mut buffer); } /// VST2.4 replacing function with `f64` values. -pub fn process_replacing_f64(effect: *mut AEffect, inputs_raw: *mut *mut f64, outputs_raw: *mut *mut f64, samples: i32) { +pub fn process_replacing_f64(effect: *mut AEffect, raw_inputs: *const *const f64, raw_outputs: *mut *mut f64, samples: i32) { let mut plugin = unsafe { (*effect).get_plugin() }; - - if plugin.get_info().f64_precision { - let buffer = unsafe { - AudioBuffer::from_raw(inputs_raw, - outputs_raw, - plugin.get_info().inputs as usize, - plugin.get_info().outputs as usize, - samples as usize) - }; - - plugin.process_f64(buffer); - } + let cache = unsafe { (*effect).get_cache() }; + let info = &mut cache.info; + let (input_count, output_count) = (info.inputs as usize, info.outputs as usize); + let mut buffer = AudioBuffer::from_raw(input_count, output_count, raw_inputs, raw_outputs, samples as usize); + plugin.process_f64(&mut buffer); } /// VST2.4 set parameter function. @@ -120,12 +107,12 @@ pub fn dispatch(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr // Given a Rect** structure // TODO: Investigate whether we are given a valid Rect** pointer already *(ptr as *mut *mut c_void) = - mem::transmute(Box::new(Rect { + Box::into_raw(Box::new(Rect { left: pos.0 as i16, // x coord of position top: pos.1 as i16, // y coord of position right: (pos.0 + size.0) as i16, // x coord of pos + x coord of size bottom: (pos.1 + size.1) as i16 // y coord of pos + y coord of size - })); + })) as *mut _; // TODO: free memory } } } @@ -174,16 +161,7 @@ pub fn dispatch(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr } OpCode::ProcessEvents => { - let events: *const api::Events = ptr as *const api::Events; - - let events: Vec = unsafe { - // Create a slice of type &mut [*mut Event] - slice::from_raw_parts(&(*events).events[0], (*events).num_events as usize) - // Deref and clone each event to get a slice - .iter().map(|item| Event::from(**item)).collect() - }; - - plugin.process_events(events); + plugin.process_events(unsafe { &*(ptr as *const api::Events) }); } OpCode::CanBeAutomated => return plugin.can_be_automated(index) as isize, OpCode::StringToParameter => return plugin.string_to_parameter(index, read_string(ptr)) as isize, @@ -285,16 +263,7 @@ pub fn host_dispatch(host: &mut Host, OpCode::GetVendorString => copy_string(ptr, &host.get_info().1, MAX_VENDOR_STR_LEN), OpCode::GetProductString => copy_string(ptr, &host.get_info().2, MAX_PRODUCT_STR_LEN), OpCode::ProcessEvents => { - let events: *const api::Events = ptr as *const api::Events; - - let events: Vec = unsafe { - // Create a slice of type &mut [*mut Event] - slice::from_raw_parts(&(*events).events[0], (*events).num_events as usize) - // Deref and clone each event to get a slice - .iter().map(|item| Event::from(**item)).collect() - }; - - host.process_events(events); + host.process_events(unsafe { &*(ptr as *const api::Events) }); } unimplemented => { @@ -313,95 +282,4 @@ fn read_string(ptr: *mut c_void) -> String { String::from_utf8_lossy( unsafe { CStr::from_ptr(ptr as *mut c_char).to_bytes() } ).into_owned() -} - -/// Translate `Vec` into `&api::Events` and use via a callback. -/// -/// Both plugins and hosts can receive VST events, this simply translates the rust structure into -/// the equivalent API structure and takes care of cleanup. -pub fn process_events(events: Vec, callback: F) - where F: FnOnce(*mut c_void) -{ - use api::flags::REALTIME_EVENT; - - let len = events.len(); - - // The `api::Events` structure uses a variable length array which is difficult to represent in - // rust. We begin by creating a vector with the appropriate byte size by calculating the header - // and the variable length body seperately. - let header_size = mem::size_of::() - (mem::size_of::<*mut api::Event>() * 2); - let body_size = mem::size_of::<*mut api::Event>() * len; - - let mut send = vec![0u8; header_size + body_size]; - - let send_events: &mut [*mut api::Event] = unsafe { - // The header is updated by casting the array to the `api::Events` type and specifying the - // required fields. We create a slice from the position of the first event and the length - // of the array. - let ptr = send.as_mut_ptr() as *mut api::Events; - (*ptr).num_events = len as i32; - - // A slice view of the body - slice::from_raw_parts_mut(&mut (*ptr).events[0], len) - }; - - // Each event is zipped with the target body array slot. Most of what's happening here is just - // copying data but the key thing to notice is that each event is boxed and cast to - // (*mut api::Event). This way we can let the callback handle the event, and then later create - // the box again from the raw pointer so that it can be properly dropped. - for (event, out) in events.iter().zip(send_events.iter_mut()) { - *out = match *event { - Event::Midi { data, delta_frames, live, - note_length, note_offset, - detune, note_off_velocity } => { - Box::into_raw(Box::new(api::MidiEvent { - event_type: api::EventType::Midi, - byte_size: mem::size_of::() as i32, - delta_frames: delta_frames, - flags: if live { REALTIME_EVENT.bits() } else { 0 }, - note_length: note_length.unwrap_or(0), - note_offset: note_offset.unwrap_or(0), - midi_data: data, - _midi_reserved: 0, - detune: detune, - note_off_velocity: note_off_velocity, - _reserved1: 0, - _reserved2: 0 - })) as *mut api::Event - } - Event::SysEx { payload, delta_frames } => { - Box::into_raw(Box::new(api::SysExEvent { - event_type: api::EventType::SysEx, - byte_size: mem::size_of::() as i32, - delta_frames: delta_frames, - _flags: 0, - data_size: payload.len() as i32, - _reserved1: 0, - system_data: payload.as_ptr() as *const u8 as *mut u8, - _reserved2: 0, - })) as *mut api::Event - } - Event::Deprecated(e) => Box::into_raw(Box::new(e)) - }; - } - - // Allow the callback to use the pointer - callback(send.as_mut_ptr() as *mut c_void); - - // Clean up the created events - unsafe { - for &mut event in send_events { - match (*event).event_type { - api::EventType::Midi => { - drop(Box::from_raw(event as *mut api::MidiEvent)); - } - api::EventType::SysEx => { - drop(Box::from_raw(event as *mut api::SysExEvent)); - } - _ => { - drop(Box::from_raw(event)); - } - } - } - } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a2c86606..984cf129 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,8 @@ //! [`PluginLoader::load`]: host/struct.PluginLoader.html#method.load //! +#![cfg_attr(feature = "nightly", feature(conservative_impl_trait))] + extern crate libc; extern crate num_traits; extern crate libloading; @@ -131,10 +133,12 @@ pub mod event; pub mod host; pub mod plugin; mod interfaces; +mod cache; use api::{HostCallbackProc, AEffect}; use api::consts::VST_MAGIC; use plugin::{HostCallback, Plugin}; +use cache::PluginCache; /// Exports the necessary symbols for the plugin to be used by a VST host. /// @@ -170,7 +174,7 @@ pub fn main(callback: HostCallbackProc) -> *mut AEffect { // Create a Box containing a zeroed AEffect. This is transmuted into a *mut pointer so that it // can be passed into the HostCallback `wrap` method. The AEffect is then updated after the vst // object is created so that the host still contains a raw pointer to the AEffect struct. - let effect = unsafe { mem::transmute(Box::new(mem::zeroed::())) }; + let effect = unsafe { Box::into_raw(Box::new(mem::zeroed::())) }; let host = HostCallback::wrap(callback, effect); if host.vst_version() == 0 { // TODO: Better criteria would probably be useful here... @@ -233,8 +237,8 @@ pub fn main(callback: HostCallbackProc) -> *mut AEffect { _offQualities: 0, _ioRatio: 0.0, - object: mem::transmute(Box::new(Box::new(plugin) as Box)), - user: ptr::null_mut(), + object: Box::into_raw(Box::new(Box::new(plugin) as Box)) as *mut _, + user: Box::into_raw(Box::new(PluginCache::new(&info))) as *mut _, uniqueId: info.unique_id, version: info.version, @@ -316,11 +320,11 @@ mod tests { #[test] fn plugin_drop() { - static mut drop_test: bool = false; + static mut DROP_TEST: bool = false; impl Drop for TestPlugin { fn drop(&mut self) { - unsafe { drop_test = true; } + unsafe { DROP_TEST = true; } } } @@ -330,7 +334,7 @@ mod tests { unsafe { (*aeffect).drop_plugin() }; // Assert that the VST is shut down and dropped. - assert!(unsafe { drop_test }); + assert!(unsafe { DROP_TEST }); } #[test] diff --git a/src/plugin.rs b/src/plugin.rs index ffef036d..e688bbe4 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -10,7 +10,7 @@ use api::{AEffect, HostCallbackProc, Supported}; use api::consts::VST_MAGIC; use buffer::AudioBuffer; use editor::Editor; -use event::Event; +use api; /// Plugin type. Generally either Effect or Synth. /// @@ -582,24 +582,24 @@ pub trait Plugin { /// # fn get_info(&self) -> Info { Default::default() } /// # /// // Processor that clips samples above 0.4 or below -0.4: - /// fn process(&mut self, buffer: AudioBuffer){ - /// let (inputs, mut outputs) = buffer.split(); - /// - /// for (channel, ibuf) in inputs.iter().enumerate() { - /// for (i, sample) in ibuf.iter().enumerate() { - /// outputs[channel][i] = if *sample > 0.4 { + /// fn process(&mut self, buffer: &mut AudioBuffer){ + /// // For each input and output + /// for (input, output) in buffer.zip() { + /// // For each input sample and output sample in buffer + /// for (in_sample, out_sample) in input.into_iter().zip(output.into_iter()) { + /// *out_sample = if *in_sample > 0.4 { /// 0.4 - /// } else if *sample < -0.4 { + /// } else if *in_sample < -0.4 { /// -0.4 /// } else { - /// *sample + /// *in_sample /// }; /// } /// } /// } /// # } /// ``` - fn process(&mut self, buffer: AudioBuffer) { + fn process(&mut self, buffer: &mut AudioBuffer) { // For each input and output for (input, output) in buffer.zip() { // For each input sample and output sample in buffer @@ -621,24 +621,24 @@ pub trait Plugin { /// # fn get_info(&self) -> Info { Default::default() } /// # /// // Processor that clips samples above 0.4 or below -0.4: - /// fn process_f64(&mut self, buffer: AudioBuffer){ - /// let (inputs, mut outputs) = buffer.split(); - /// - /// for (channel, ibuf) in inputs.iter().enumerate() { - /// for (i, sample) in ibuf.iter().enumerate() { - /// outputs[channel][i] = if *sample > 0.4 { + /// fn process_f64(&mut self, buffer: &mut AudioBuffer){ + /// // For each input and output + /// for (input, output) in buffer.zip() { + /// // For each input sample and output sample in buffer + /// for (in_sample, out_sample) in input.into_iter().zip(output.into_iter()) { + /// *out_sample = if *in_sample > 0.4 { /// 0.4 - /// } else if *sample < -0.4 { + /// } else if *in_sample < -0.4 { /// -0.4 /// } else { - /// *sample + /// *in_sample /// }; /// } /// } /// } /// # } /// ``` - fn process_f64(&mut self, buffer: AudioBuffer) { + fn process_f64(&mut self, buffer: &mut AudioBuffer) { // For each input and output for (input, output) in buffer.zip() { // For each input sample and output sample in buffer @@ -651,7 +651,7 @@ pub trait Plugin { /// Handle incoming events sent from the host. /// /// This is always called before the start of `process` or `process_f64`. - fn process_events(&mut self, events: Vec) {} + fn process_events(&mut self, events: &api::Events) {} /// Return handle to plugin editor if supported. fn get_editor(&mut self) -> Option<&mut Editor> { None } @@ -820,21 +820,14 @@ impl Host for HostCallback { /// /// [`process`]: trait.Plugin.html#method.process /// [`process_f64`]: trait.Plugin.html#method.process_f64 - fn process_events(&mut self, events: Vec) { - use interfaces; - - interfaces::process_events( - events, - |ptr| { - self.callback( - self.effect, - host::OpCode::ProcessEvents, - 0, - 0, - ptr, - 0.0 - ); - } + fn process_events(&mut self, events: &api::Events) { + self.callback( + self.effect, + host::OpCode::ProcessEvents, + 0, + 0, + events as *const _ as *mut _, + 0.0 ); } }