├── .github └── workflows │ ├── deploy.yml │ ├── docs.yml │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── dimension_expander.rs ├── fwd_midi.rs ├── gain_effect.rs ├── ladder_filter.rs ├── simple_host.rs ├── sine_synth.rs └── transfer_and_smooth.rs ├── osx_vst_bundler.sh ├── rustfmt.toml └── src ├── api.rs ├── buffer.rs ├── cache.rs ├── channels.rs ├── editor.rs ├── event.rs ├── host.rs ├── interfaces.rs ├── lib.rs ├── plugin.rs ├── prelude.rs └── util ├── atomic_float.rs ├── mod.rs └── parameter_transfer.rs /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | # Installs the latest stable rust, and all components needed for the rest of the CI pipeline. 14 | - name: Set up CI environment 15 | uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: stable 18 | override: true 19 | 20 | # Sanity check: make sure the release builds 21 | - name: Build 22 | run: cargo build --verbose 23 | 24 | # Sanity check: make sure all tests in the release pass 25 | - name: Test 26 | run: cargo test --verbose 27 | 28 | # Deploy to crates.io 29 | # Only works on github releases (tagged commits) 30 | - name: Deploy to crates.io 31 | env: 32 | CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} 33 | run: cargo publish --token $CRATES_IO_TOKEN --manifest-path Cargo.toml -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | with: 14 | persist-credentials: false 15 | fetch-depth: 0 16 | 17 | # Installs the latest stable rust, and all components needed for the rest of the CI pipeline. 18 | - name: Set up CI environment 19 | uses: actions-rs/toolchain@v1 20 | with: 21 | toolchain: stable 22 | override: true 23 | 24 | # Sanity check: make sure the release builds 25 | - name: Build 26 | run: cargo build --verbose 27 | 28 | # Sanity check: make sure all tests in the release pass 29 | - name: Test 30 | run: cargo test --verbose 31 | 32 | # Generate docs 33 | # TODO: what does the last line here do? 34 | - name: Generate docs 35 | env: 36 | GH_ENCRYPTED_TOKEN: ${{ secrets.GH_ENCRYPTED_TOKEN }} 37 | run: | 38 | cargo doc --all --no-deps 39 | echo '' > target/doc/index.html 40 | 41 | # Push docs to github pages (branch `gh-pages`) 42 | - name: Deploy 43 | uses: peaceiris/actions-gh-pages@v3 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | publish_dir: target/doc 47 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, windows-latest, macOS-latest] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | 15 | # Installs the latest stable rust, and all components needed for the rest of the CI pipeline. 16 | - name: Set up CI environment 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | override: true 21 | components: rustfmt, clippy 22 | 23 | # Makes sure the code builds successfully. 24 | - name: Build 25 | run: cargo build --verbose 26 | 27 | # Makes sure all of the tests pass. 28 | - name: Test 29 | run: cargo test --verbose 30 | 31 | # Runs Clippy on the codebase, and makes sure there are no lint warnings. 32 | # Disabled for now. Re-enable if you find it useful enough to deal with it constantly breaking. 33 | # - name: Clippy 34 | # run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::unreadable_literal -A clippy::needless_range_loop -A clippy::float_cmp -A clippy::comparison-chain -A clippy::needless-doctest-main -A clippy::missing-safety-doc 35 | 36 | # Makes sure the codebase is up to `cargo fmt` standards 37 | - name: Format check 38 | run: cargo fmt --all -- --check -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | /examples/*/target/ 13 | Cargo.lock 14 | 15 | # Vim 16 | [._]*.s[a-w][a-z] 17 | [._]s[a-w][a-z] 18 | *.un~ 19 | Session.vim 20 | .netrwhist 21 | *~ 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## 0.4.0 9 | 10 | ### Changed 11 | 12 | - Added deprecation notice. 13 | 14 | ## 0.3.0 15 | 16 | ### Fixed 17 | 18 | - `SysExEvent` no longer contains invalid data on 64-bit systems ([#170](https://github.com/RustAudio/vst-rs/pull/171)] 19 | - Function pointers in `AEffect` marked as `extern` ([#141](https://github.com/RustAudio/vst-rs/pull/141)) 20 | - Key character fixes ([#152](https://github.com/RustAudio/vst-rs/pull/152)) 21 | - Doc and deploy actions fixes ([9eb1bef](https://github.com/RustAudio/vst-rs/commit/9eb1bef1826db1581b4162081de05c1090935afb)) 22 | - Various doc fixes ([#177](https://github.com/RustAudio/vst-rs/pull/177)) 23 | 24 | ### Added 25 | 26 | - `begin_edit` and `end_edit` now in `Host` trait ([#151](https://github.com/RustAudio/vst-rs/pull/151)) 27 | - Added a `prelude` for commonly used items when constructing a `Plugin` ([#161](https://github.com/RustAudio/vst-rs/pull/161)) 28 | - Various useful implementations for `AtomicFloat` ([#150](https://github.com/RustAudio/vst-rs/pull/150)) 29 | 30 | ### Changed 31 | 32 | - **Major breaking change:** New `Plugin` `Send` requirement ([#140](https://github.com/RustAudio/vst-rs/pull/140)) 33 | - No longer require `Plugin` to implement `Default` ([#154](https://github.com/RustAudio/vst-rs/pull/154)) 34 | - `impl_clicke` replaced with `num_enum` ([#168](https://github.com/RustAudio/vst-rs/pull/168)) 35 | - Reworked `SendEventBuffer` to make it useable in `Plugin::process_events` ([#160](https://github.com/RustAudio/vst-rs/pull/160)) 36 | - Updated dependencies and removed development dependency on `time` ([#179](https://github.com/RustAudio/vst-rs/pull/179)) 37 | 38 | ## 0.2.1 39 | 40 | ### Fixed 41 | 42 | - Introduced zero-valued `EventType` variant to enable zero-initialization of `Event`, fixing a panic on Rust 1.48 and newer ([#138](https://github.com/RustAudio/vst-rs/pull/138)) 43 | - `EditorGetRect` opcode returns `1` on success, ensuring that the provided dimensions are applied by the host ([#115](https://github.com/RustAudio/vst-rs/pull/115)) 44 | 45 | ### Added 46 | 47 | - Added `update_display()` method to `Host`, telling the host to update its display (after a parameter change) via the `UpdateDisplay` opcode ([#126](https://github.com/RustAudio/vst-rs/pull/126)) 48 | - Allow plug-in to return a custom value in `can_do()` via the `Supported::Custom` enum variant ([#130](https://github.com/RustAudio/vst-rs/pull/130)) 49 | - Added `PartialEq` and `Eq` for `Supported` ([#135](https://github.com/RustAudio/vst-rs/pull/135)) 50 | - Implemented `get_editor()` and `Editor` interface for `PluginInstance` to enable editor support on the host side ([#136](https://github.com/RustAudio/vst-rs/pull/136)) 51 | - Default value (`0.0`) for `AtomicFloat` ([#139](https://github.com/RustAudio/vst-rs/pull/139)) 52 | 53 | ## 0.2.0 54 | 55 | ### Changed 56 | 57 | - **Major breaking change:** Restructured `Plugin` API to make it thread safe ([#65](https://github.com/RustAudio/vst-rs/pull/65)) 58 | - Fixed a number of unsoundness issues in the `Outputs` API ([#67](https://github.com/RustAudio/vst-rs/pull/67), [#108](https://github.com/RustAudio/vst-rs/pull/108)) 59 | - Set parameters to be automatable by default ([#99](https://github.com/RustAudio/vst-rs/pull/99)) 60 | - Moved repository to the [RustAudio](https://github.com/RustAudio) organization and renamed it to `vst-rs` ([#90](https://github.com/RustAudio/vst-rs/pull/90), [#94](https://github.com/RustAudio/vst-rs/pull/94)) 61 | 62 | ### Fixed 63 | 64 | - Fixed a use-after-move bug in the event iterator ([#93](https://github.com/RustAudio/vst-rs/pull/93), [#111](https://github.com/RustAudio/vst-rs/pull/111)) 65 | 66 | ### Added 67 | 68 | - Handle `Opcode::GetEffectName` to resolve name display issues on some hosts ([#89](https://github.com/RustAudio/vst-rs/pull/89)) 69 | - More examples ([#65](https://github.com/RustAudio/vst-rs/pull/65), [#92](https://github.com/RustAudio/vst-rs/pull/92)) 70 | 71 | ## 0.1.0 72 | 73 | ### Added 74 | 75 | - Added initial changelog 76 | - Initial project files 77 | 78 | ### Removed 79 | 80 | - The `#[derive(Copy, Clone)]` attribute from `Outputs`. 81 | 82 | ### Changed 83 | - The signature of the `Outputs::split_at_mut` now takes an `self` parameter instead of `&mut self`. 84 | So calling `split_at_mut` will now move instead of "borrow". 85 | - Now `&mut Outputs` (instead of `Outputs`) implements the `IntoIterator` trait. 86 | - The return type of the `AudioBuffer::zip()` method (but it still implements the Iterator trait). 87 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vst" 3 | version = "0.4.0" 4 | edition = "2021" 5 | authors = [ 6 | "Marko Mijalkovic ", 7 | "Boscop", 8 | "Alex Zywicki ", 9 | "doomy ", 10 | "Ms2ger", 11 | "Rob Saunders", 12 | "David Lu", 13 | "Aske Simon Christensen", 14 | "kykc", 15 | "Jordan Earls", 16 | "xnor104", 17 | "Nathaniel Theis", 18 | "Colin Wallace", 19 | "Henrik Nordvik", 20 | "Charles Saracco", 21 | "Frederik Halkjær" ] 22 | 23 | description = "VST 2.4 API implementation in rust. Create plugins or hosts." 24 | 25 | readme = "README.md" 26 | repository = "https://github.com/rustaudio/vst-rs" 27 | 28 | license = "MIT" 29 | keywords = ["vst", "vst2", "plugin"] 30 | 31 | autoexamples = false 32 | 33 | [features] 34 | default = [] 35 | disable_deprecation_warning = [] 36 | 37 | [dependencies] 38 | log = "0.4" 39 | num-traits = "0.2" 40 | libc = "0.2" 41 | bitflags = "1" 42 | libloading = "0.7" 43 | num_enum = "0.5.2" 44 | 45 | [dev-dependencies] 46 | rand = "0.8" 47 | 48 | [[example]] 49 | name = "dimension_expander" 50 | crate-type = ["cdylib"] 51 | 52 | [[example]] 53 | name = "simple_host" 54 | crate-type = ["bin"] 55 | 56 | [[example]] 57 | name = "sine_synth" 58 | crate-type = ["cdylib"] 59 | 60 | [[example]] 61 | name = "fwd_midi" 62 | crate-type = ["cdylib"] 63 | 64 | [[example]] 65 | name = "gain_effect" 66 | crate-type = ["cdylib"] 67 | 68 | [[example]] 69 | name = "transfer_and_smooth" 70 | crate-type = ["cdylib"] 71 | 72 | [[example]] 73 | name = "ladder_filter" 74 | crate-type = ["cdylib"] 75 | 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Marko Mijalkovic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vst-rs 2 | [![crates.io][crates-img]][crates-url] 3 | [![dependency status](https://deps.rs/repo/github/rustaudio/vst-rs/status.svg)](https://deps.rs/repo/github/rustaudio/vst-rs) 4 | [![Discord Chat][discord-img]][discord-url] 5 | [![Discourse topics][dc-img]][dc-url] 6 | 7 | > **Notice**: `vst-rs` is deprecated. 8 | > 9 | > This crate is no longer actively developed or maintained. VST 2 has been [officially discontinued](http://web.archive.org/web/20210727141622/https://www.steinberg.net/en/newsandevents/news/newsdetail/article/vst-2-coming-to-an-end-4727.html) and it is [no longer possible](https://forum.juce.com/t/steinberg-closing-down-vst2-for-good/27722/25) to acquire a license to distribute VST 2 products. It is highly recommended that you make use of other libraries for developing audio plugins and plugin hosts in Rust. 10 | > 11 | > If you're looking for a high-level, multi-format framework for developing plugins in Rust, consider using [NIH-plug](https://github.com/robbert-vdh/nih-plug/) or [`baseplug`](https://github.com/wrl/baseplug/). If you're looking for bindings to specific plugin APIs, consider using [`vst3-sys`](https://github.com/RustAudio/vst3-sys/), [`clap-sys`](https://github.com/glowcoil/clap-sys), [`lv2(-sys)`](https://github.com/RustAudio/rust-lv2), or [`auv2-sys`](https://github.com/glowcoil/auv2-sys). If, despite the above warnings, you still have a need to use the VST 2 API from Rust, consider using [`vst2-sys`](https://github.com/RustAudio/vst2-sys) or generating bindings from the original VST 2 SDK using [`bindgen`](https://github.com/rust-lang/rust-bindgen). 12 | 13 | `vst-rs` is a library for creating VST2 plugins in the Rust programming language. 14 | 15 | This library is a work in progress, and as such it does not yet implement all 16 | functionality. It can create basic VST plugins without an editor interface. 17 | 18 | **Note:** If you are upgrading from a version prior to 0.2.0, you will need to update 19 | your plugin code to be compatible with the new, thread-safe plugin API. See the 20 | [`transfer_and_smooth`](examples/transfer_and_smooth.rs) example for a guide on how 21 | to port your plugin. 22 | 23 | ## Library Documentation 24 | 25 | Documentation for **released** versions can be found [here](https://docs.rs/vst/). 26 | 27 | Development documentation (current `master` branch) can be found [here](https://rustaudio.github.io/vst-rs/vst/). 28 | 29 | ## Crate 30 | This crate is available on [crates.io](https://crates.io/crates/vst). If you prefer the bleeding-edge, you can also 31 | include the crate directly from the official [Github repository](https://github.com/rustaudio/vst-rs). 32 | 33 | ```toml 34 | # get from crates.io. 35 | vst = "0.3" 36 | ``` 37 | ```toml 38 | # get directly from Github. This might be unstable! 39 | vst = { git = "https://github.com/rustaudio/vst-rs" } 40 | ``` 41 | 42 | ## Usage 43 | To create a plugin, simply create a type which implements the `Plugin` trait. Then call the `plugin_main` macro, which will export the necessary functions and handle dealing with the rest of the API. 44 | 45 | ## Example Plugin 46 | A simple plugin that bears no functionality. The provided `Cargo.toml` has a 47 | `crate-type` directive which builds a dynamic library, usable by any VST host. 48 | 49 | `src/lib.rs` 50 | 51 | ```rust 52 | #[macro_use] 53 | extern crate vst; 54 | 55 | use vst::prelude::*; 56 | 57 | struct BasicPlugin; 58 | 59 | impl Plugin for BasicPlugin { 60 | fn new(_host: HostCallback) -> Self { 61 | BasicPlugin 62 | } 63 | 64 | fn get_info(&self) -> Info { 65 | Info { 66 | name: "Basic Plugin".to_string(), 67 | unique_id: 1357, // Used by hosts to differentiate between plugins. 68 | ..Default::default() 69 | } 70 | } 71 | } 72 | 73 | plugin_main!(BasicPlugin); // Important! 74 | ``` 75 | 76 | `Cargo.toml` 77 | 78 | ```toml 79 | [package] 80 | name = "basic_vst" 81 | version = "0.0.1" 82 | authors = ["Author "] 83 | 84 | [dependencies] 85 | vst = { git = "https://github.com/rustaudio/vst-rs" } 86 | 87 | [lib] 88 | name = "basicvst" 89 | crate-type = ["cdylib"] 90 | ``` 91 | 92 | [crates-img]: https://img.shields.io/crates/v/vst.svg 93 | [crates-url]: https://crates.io/crates/vst 94 | [discord-img]: https://img.shields.io/discord/590254806208217089.svg?label=Discord&logo=discord&color=blue 95 | [discord-url]: https://discord.gg/QPdhk2u 96 | [dc-img]: https://img.shields.io/discourse/https/rust-audio.discourse.group/topics.svg?logo=discourse&color=blue 97 | [dc-url]: https://rust-audio.discourse.group 98 | 99 | #### Packaging on OS X 100 | 101 | On OS X VST plugins are packaged inside loadable bundles. 102 | To package your VST as a loadable bundle you may use the `osx_vst_bundler.sh` script this library provides.  103 | 104 | Example:  105 | 106 | ``` 107 | ./osx_vst_bundler.sh Plugin target/release/plugin.dylib 108 | Creates a Plugin.vst bundle 109 | ``` 110 | 111 | ## Special Thanks 112 | [Marko Mijalkovic](https://github.com/overdrivenpotato) for [initiating this project](https://github.com/overdrivenpotato/rust-vst2) 113 | -------------------------------------------------------------------------------- /examples/dimension_expander.rs: -------------------------------------------------------------------------------- 1 | // author: Marko Mijalkovic 2 | 3 | #[macro_use] 4 | extern crate vst; 5 | 6 | use std::collections::VecDeque; 7 | use std::f64::consts::PI; 8 | use std::sync::Arc; 9 | use std::time::{SystemTime, UNIX_EPOCH}; 10 | use vst::prelude::*; 11 | 12 | /// Calculate the length in samples for a delay. Size ranges from 0.0 to 1.0. 13 | fn delay(index: usize, mut size: f32) -> isize { 14 | const SIZE_OFFSET: f32 = 0.06; 15 | const SIZE_MULT: f32 = 1_000.0; 16 | 17 | size += SIZE_OFFSET; 18 | 19 | // Spread ratio between delays 20 | const SPREAD: f32 = 0.3; 21 | 22 | let base = size * SIZE_MULT; 23 | let mult = (index as f32 * SPREAD) + 1.0; 24 | let offset = if index > 2 { base * SPREAD / 2.0 } else { 0.0 }; 25 | 26 | (base * mult + offset) as isize 27 | } 28 | 29 | /// A left channel and right channel sample. 30 | type SamplePair = (f32, f32); 31 | 32 | /// The Dimension Expander. 33 | struct DimensionExpander { 34 | buffers: Vec>, 35 | params: Arc, 36 | old_size: f32, 37 | } 38 | 39 | struct DimensionExpanderParameters { 40 | dry_wet: AtomicFloat, 41 | size: AtomicFloat, 42 | } 43 | 44 | impl DimensionExpander { 45 | fn new(size: f32, dry_wet: f32) -> DimensionExpander { 46 | const NUM_DELAYS: usize = 4; 47 | 48 | let mut buffers = Vec::new(); 49 | 50 | // Generate delay buffers 51 | for i in 0..NUM_DELAYS { 52 | let samples = delay(i, size); 53 | let mut buffer = VecDeque::with_capacity(samples as usize); 54 | 55 | // Fill in the delay buffers with empty samples 56 | for _ in 0..samples { 57 | buffer.push_back((0.0, 0.0)); 58 | } 59 | 60 | buffers.push(buffer); 61 | } 62 | 63 | DimensionExpander { 64 | buffers, 65 | params: Arc::new(DimensionExpanderParameters { 66 | dry_wet: AtomicFloat::new(dry_wet), 67 | size: AtomicFloat::new(size), 68 | }), 69 | old_size: size, 70 | } 71 | } 72 | 73 | /// Update the delay buffers with a new size value. 74 | fn resize(&mut self, n: f32) { 75 | let old_size = self.old_size; 76 | 77 | for (i, buffer) in self.buffers.iter_mut().enumerate() { 78 | // Calculate the size difference between delays 79 | let old_delay = delay(i, old_size); 80 | let new_delay = delay(i, n); 81 | 82 | let diff = new_delay - old_delay; 83 | 84 | // Add empty samples if the delay was increased, remove if decreased 85 | if diff > 0 { 86 | for _ in 0..diff { 87 | buffer.push_back((0.0, 0.0)); 88 | } 89 | } else if diff < 0 { 90 | for _ in 0..-diff { 91 | let _ = buffer.pop_front(); 92 | } 93 | } 94 | } 95 | 96 | self.old_size = n; 97 | } 98 | } 99 | 100 | impl Plugin for DimensionExpander { 101 | fn new(_host: HostCallback) -> Self { 102 | DimensionExpander::new(0.12, 0.66) 103 | } 104 | 105 | fn get_info(&self) -> Info { 106 | Info { 107 | name: "Dimension Expander".to_string(), 108 | vendor: "overdrivenpotato".to_string(), 109 | unique_id: 243723071, 110 | version: 1, 111 | inputs: 2, 112 | outputs: 2, 113 | parameters: 2, 114 | category: Category::Effect, 115 | 116 | ..Default::default() 117 | } 118 | } 119 | 120 | fn process(&mut self, buffer: &mut AudioBuffer) { 121 | let (inputs, outputs) = buffer.split(); 122 | 123 | // Assume 2 channels 124 | if inputs.len() < 2 || outputs.len() < 2 { 125 | return; 126 | } 127 | 128 | // Resize if size changed 129 | let size = self.params.size.get(); 130 | if size != self.old_size { 131 | self.resize(size); 132 | } 133 | 134 | // Iterate over inputs as (&f32, &f32) 135 | let (l, r) = inputs.split_at(1); 136 | let stereo_in = l[0].iter().zip(r[0].iter()); 137 | 138 | // Iterate over outputs as (&mut f32, &mut f32) 139 | let (mut l, mut r) = outputs.split_at_mut(1); 140 | let stereo_out = l[0].iter_mut().zip(r[0].iter_mut()); 141 | 142 | // Zip and process 143 | for ((left_in, right_in), (left_out, right_out)) in stereo_in.zip(stereo_out) { 144 | // Push the new samples into the delay buffers. 145 | for buffer in &mut self.buffers { 146 | buffer.push_back((*left_in, *right_in)); 147 | } 148 | 149 | let mut left_processed = 0.0; 150 | let mut right_processed = 0.0; 151 | 152 | // Recalculate time per sample 153 | let time_s = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs_f64(); 154 | 155 | // Use buffer index to offset volume LFO 156 | for (n, buffer) in self.buffers.iter_mut().enumerate() { 157 | if let Some((left_old, right_old)) = buffer.pop_front() { 158 | const LFO_FREQ: f64 = 0.5; 159 | const WET_MULT: f32 = 0.66; 160 | 161 | let offset = 0.25 * (n % 4) as f64; 162 | 163 | // Sine wave volume LFO 164 | let lfo = ((time_s * LFO_FREQ + offset) * PI * 2.0).sin() as f32; 165 | 166 | let wet = self.params.dry_wet.get() * WET_MULT; 167 | let mono = (left_old + right_old) / 2.0; 168 | 169 | // Flip right channel and keep left mono so that the result is 170 | // entirely stereo 171 | left_processed += mono * wet * lfo; 172 | right_processed += -mono * wet * lfo; 173 | } 174 | } 175 | 176 | // By only adding to the input, the output value always remains the same in mono 177 | *left_out = *left_in + left_processed; 178 | *right_out = *right_in + right_processed; 179 | } 180 | } 181 | 182 | fn get_parameter_object(&mut self) -> Arc { 183 | Arc::clone(&self.params) as Arc 184 | } 185 | } 186 | 187 | impl PluginParameters for DimensionExpanderParameters { 188 | fn get_parameter(&self, index: i32) -> f32 { 189 | match index { 190 | 0 => self.size.get(), 191 | 1 => self.dry_wet.get(), 192 | _ => 0.0, 193 | } 194 | } 195 | 196 | fn get_parameter_text(&self, index: i32) -> String { 197 | match index { 198 | 0 => format!("{}", (self.size.get() * 1000.0) as isize), 199 | 1 => format!("{:.1}%", self.dry_wet.get() * 100.0), 200 | _ => "".to_string(), 201 | } 202 | } 203 | 204 | fn get_parameter_name(&self, index: i32) -> String { 205 | match index { 206 | 0 => "Size", 207 | 1 => "Dry/Wet", 208 | _ => "", 209 | } 210 | .to_string() 211 | } 212 | 213 | fn set_parameter(&self, index: i32, val: f32) { 214 | match index { 215 | 0 => self.size.set(val), 216 | 1 => self.dry_wet.set(val), 217 | _ => (), 218 | } 219 | } 220 | } 221 | 222 | plugin_main!(DimensionExpander); 223 | -------------------------------------------------------------------------------- /examples/fwd_midi.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate vst; 3 | 4 | use vst::api; 5 | use vst::prelude::*; 6 | 7 | plugin_main!(MyPlugin); // Important! 8 | 9 | #[derive(Default)] 10 | struct MyPlugin { 11 | host: HostCallback, 12 | recv_buffer: SendEventBuffer, 13 | send_buffer: SendEventBuffer, 14 | } 15 | 16 | impl MyPlugin { 17 | fn send_midi(&mut self) { 18 | self.send_buffer 19 | .send_events(self.recv_buffer.events().events(), &mut self.host); 20 | self.recv_buffer.clear(); 21 | } 22 | } 23 | 24 | impl Plugin for MyPlugin { 25 | fn new(host: HostCallback) -> Self { 26 | MyPlugin { 27 | host, 28 | ..Default::default() 29 | } 30 | } 31 | 32 | fn get_info(&self) -> Info { 33 | Info { 34 | name: "fwd_midi".to_string(), 35 | unique_id: 7357001, // Used by hosts to differentiate between plugins. 36 | ..Default::default() 37 | } 38 | } 39 | 40 | fn process_events(&mut self, events: &api::Events) { 41 | self.recv_buffer.store_events(events.events()); 42 | } 43 | 44 | fn process(&mut self, buffer: &mut AudioBuffer) { 45 | for (input, output) in buffer.zip() { 46 | for (in_sample, out_sample) in input.iter().zip(output) { 47 | *out_sample = *in_sample; 48 | } 49 | } 50 | self.send_midi(); 51 | } 52 | 53 | fn process_f64(&mut self, buffer: &mut AudioBuffer) { 54 | for (input, output) in buffer.zip() { 55 | for (in_sample, out_sample) in input.iter().zip(output) { 56 | *out_sample = *in_sample; 57 | } 58 | } 59 | self.send_midi(); 60 | } 61 | 62 | fn can_do(&self, can_do: CanDo) -> vst::api::Supported { 63 | use vst::api::Supported::*; 64 | use vst::plugin::CanDo::*; 65 | 66 | match can_do { 67 | SendEvents | SendMidiEvent | ReceiveEvents | ReceiveMidiEvent => Yes, 68 | _ => No, 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/gain_effect.rs: -------------------------------------------------------------------------------- 1 | // author: doomy 2 | 3 | #[macro_use] 4 | extern crate vst; 5 | 6 | use std::sync::Arc; 7 | 8 | use vst::prelude::*; 9 | 10 | /// Simple Gain Effect. 11 | /// Note that this does not use a proper scale for sound and shouldn't be used in 12 | /// a production amplification effect! This is purely for demonstration purposes, 13 | /// as well as to keep things simple as this is meant to be a starting point for 14 | /// any effect. 15 | struct GainEffect { 16 | // Store a handle to the plugin's parameter object. 17 | params: Arc, 18 | } 19 | 20 | /// The plugin's parameter object contains the values of parameters that can be 21 | /// adjusted from the host. If we were creating an effect that didn't allow the 22 | /// user to modify it at runtime or have any controls, we could omit this part. 23 | /// 24 | /// The parameters object is shared between the processing and GUI threads. 25 | /// For this reason, all mutable state in the object has to be represented 26 | /// through thread-safe interior mutability. The easiest way to achieve this 27 | /// is to store the parameters in atomic containers. 28 | struct GainEffectParameters { 29 | // The plugin's state consists of a single parameter: amplitude. 30 | amplitude: AtomicFloat, 31 | } 32 | 33 | impl Default for GainEffectParameters { 34 | fn default() -> GainEffectParameters { 35 | GainEffectParameters { 36 | amplitude: AtomicFloat::new(0.5), 37 | } 38 | } 39 | } 40 | 41 | // All plugins using `vst` also need to implement the `Plugin` trait. Here, we 42 | // define functions that give necessary info to our host. 43 | impl Plugin for GainEffect { 44 | fn new(_host: HostCallback) -> Self { 45 | // Note that controls will always return a value from 0 - 1. 46 | // Setting a default to 0.5 means it's halfway up. 47 | GainEffect { 48 | params: Arc::new(GainEffectParameters::default()), 49 | } 50 | } 51 | 52 | fn get_info(&self) -> Info { 53 | Info { 54 | name: "Gain Effect in Rust".to_string(), 55 | vendor: "Rust DSP".to_string(), 56 | unique_id: 243723072, 57 | version: 1, 58 | inputs: 2, 59 | outputs: 2, 60 | // This `parameters` bit is important; without it, none of our 61 | // parameters will be shown! 62 | parameters: 1, 63 | category: Category::Effect, 64 | ..Default::default() 65 | } 66 | } 67 | 68 | // Here is where the bulk of our audio processing code goes. 69 | fn process(&mut self, buffer: &mut AudioBuffer) { 70 | // Read the amplitude from the parameter object 71 | let amplitude = self.params.amplitude.get(); 72 | // First, we destructure our audio buffer into an arbitrary number of 73 | // input and output buffers. Usually, we'll be dealing with stereo (2 of each) 74 | // but that might change. 75 | for (input_buffer, output_buffer) in buffer.zip() { 76 | // Next, we'll loop through each individual sample so we can apply the amplitude 77 | // value to it. 78 | for (input_sample, output_sample) in input_buffer.iter().zip(output_buffer) { 79 | *output_sample = *input_sample * amplitude; 80 | } 81 | } 82 | } 83 | 84 | // Return the parameter object. This method can be omitted if the 85 | // plugin has no parameters. 86 | fn get_parameter_object(&mut self) -> Arc { 87 | Arc::clone(&self.params) as Arc 88 | } 89 | } 90 | 91 | impl PluginParameters for GainEffectParameters { 92 | // the `get_parameter` function reads the value of a parameter. 93 | fn get_parameter(&self, index: i32) -> f32 { 94 | match index { 95 | 0 => self.amplitude.get(), 96 | _ => 0.0, 97 | } 98 | } 99 | 100 | // the `set_parameter` function sets the value of a parameter. 101 | fn set_parameter(&self, index: i32, val: f32) { 102 | #[allow(clippy::single_match)] 103 | match index { 104 | 0 => self.amplitude.set(val), 105 | _ => (), 106 | } 107 | } 108 | 109 | // This is what will display underneath our control. We can 110 | // format it into a string that makes the most since. 111 | fn get_parameter_text(&self, index: i32) -> String { 112 | match index { 113 | 0 => format!("{:.2}", (self.amplitude.get() - 0.5) * 2f32), 114 | _ => "".to_string(), 115 | } 116 | } 117 | 118 | // This shows the control's name. 119 | fn get_parameter_name(&self, index: i32) -> String { 120 | match index { 121 | 0 => "Amplitude", 122 | _ => "", 123 | } 124 | .to_string() 125 | } 126 | } 127 | 128 | // This part is important! Without it, our plugin won't work. 129 | plugin_main!(GainEffect); 130 | -------------------------------------------------------------------------------- /examples/ladder_filter.rs: -------------------------------------------------------------------------------- 1 | //! This zero-delay feedback filter is based on a 4-stage transistor ladder filter. 2 | //! It follows the following equations: 3 | //! x = input - tanh(self.res * self.vout[3]) 4 | //! vout[0] = self.params.g.get() * (tanh(x) - tanh(self.vout[0])) + self.s[0] 5 | //! vout[1] = self.params.g.get() * (tanh(self.vout[0]) - tanh(self.vout[1])) + self.s[1] 6 | //! vout[0] = self.params.g.get() * (tanh(self.vout[1]) - tanh(self.vout[2])) + self.s[2] 7 | //! vout[0] = self.params.g.get() * (tanh(self.vout[2]) - tanh(self.vout[3])) + self.s[3] 8 | //! since we can't easily solve a nonlinear equation, 9 | //! Mystran's fixed-pivot method is used to approximate the tanh() parts. 10 | //! Quality can be improved a lot by oversampling a bit. 11 | //! Feedback is clipped independently of the input, so it doesn't disappear at high gains. 12 | 13 | #[macro_use] 14 | extern crate vst; 15 | use std::f32::consts::PI; 16 | use std::sync::atomic::{AtomicUsize, Ordering}; 17 | use std::sync::Arc; 18 | 19 | use vst::prelude::*; 20 | 21 | // this is a 4-pole filter with resonance, which is why there's 4 states and vouts 22 | #[derive(Clone)] 23 | struct LadderFilter { 24 | // Store a handle to the plugin's parameter object. 25 | params: Arc, 26 | // the output of the different filter stages 27 | vout: [f32; 4], 28 | // s is the "state" parameter. In an IIR it would be the last value from the filter 29 | // In this we find it by trapezoidal integration to avoid the unit delay 30 | s: [f32; 4], 31 | } 32 | 33 | struct LadderParameters { 34 | // the "cutoff" parameter. Determines how heavy filtering is 35 | cutoff: AtomicFloat, 36 | g: AtomicFloat, 37 | // needed to calculate cutoff. 38 | sample_rate: AtomicFloat, 39 | // makes a peak at cutoff 40 | res: AtomicFloat, 41 | // used to choose where we want our output to be 42 | poles: AtomicUsize, 43 | // pole_value is just to be able to use get_parameter on poles 44 | pole_value: AtomicFloat, 45 | // a drive parameter. Just used to increase the volume, which results in heavier distortion 46 | drive: AtomicFloat, 47 | } 48 | 49 | impl Default for LadderParameters { 50 | fn default() -> LadderParameters { 51 | LadderParameters { 52 | cutoff: AtomicFloat::new(1000.), 53 | res: AtomicFloat::new(2.), 54 | poles: AtomicUsize::new(3), 55 | pole_value: AtomicFloat::new(1.), 56 | drive: AtomicFloat::new(0.), 57 | sample_rate: AtomicFloat::new(44100.), 58 | g: AtomicFloat::new(0.07135868), 59 | } 60 | } 61 | } 62 | 63 | // member methods for the struct 64 | impl LadderFilter { 65 | // the state needs to be updated after each process. Found by trapezoidal integration 66 | fn update_state(&mut self) { 67 | self.s[0] = 2. * self.vout[0] - self.s[0]; 68 | self.s[1] = 2. * self.vout[1] - self.s[1]; 69 | self.s[2] = 2. * self.vout[2] - self.s[2]; 70 | self.s[3] = 2. * self.vout[3] - self.s[3]; 71 | } 72 | 73 | // performs a complete filter process (mystran's method) 74 | fn tick_pivotal(&mut self, input: f32) { 75 | if self.params.drive.get() > 0. { 76 | self.run_ladder_nonlinear(input * (self.params.drive.get() + 0.7)); 77 | } else { 78 | // 79 | self.run_ladder_linear(input); 80 | } 81 | self.update_state(); 82 | } 83 | 84 | // nonlinear ladder filter function with distortion. 85 | fn run_ladder_nonlinear(&mut self, input: f32) { 86 | let mut a = [1f32; 5]; 87 | let base = [input, self.s[0], self.s[1], self.s[2], self.s[3]]; 88 | // a[n] is the fixed-pivot approximation for tanh() 89 | for n in 0..base.len() { 90 | if base[n] != 0. { 91 | a[n] = base[n].tanh() / base[n]; 92 | } else { 93 | a[n] = 1.; 94 | } 95 | } 96 | // denominators of solutions of individual stages. Simplifies the math a bit 97 | let g0 = 1. / (1. + self.params.g.get() * a[1]); 98 | let g1 = 1. / (1. + self.params.g.get() * a[2]); 99 | let g2 = 1. / (1. + self.params.g.get() * a[3]); 100 | let g3 = 1. / (1. + self.params.g.get() * a[4]); 101 | // these are just factored out of the feedback solution. Makes the math way easier to read 102 | let f3 = self.params.g.get() * a[3] * g3; 103 | let f2 = self.params.g.get() * a[2] * g2 * f3; 104 | let f1 = self.params.g.get() * a[1] * g1 * f2; 105 | let f0 = self.params.g.get() * g0 * f1; 106 | // outputs a 24db filter 107 | self.vout[3] = 108 | (f0 * input * a[0] + f1 * g0 * self.s[0] + f2 * g1 * self.s[1] + f3 * g2 * self.s[2] + g3 * self.s[3]) 109 | / (f0 * self.params.res.get() * a[3] + 1.); 110 | // since we know the feedback, we can solve the remaining outputs: 111 | self.vout[0] = g0 112 | * (self.params.g.get() * a[1] * (input * a[0] - self.params.res.get() * a[3] * self.vout[3]) + self.s[0]); 113 | self.vout[1] = g1 * (self.params.g.get() * a[2] * self.vout[0] + self.s[1]); 114 | self.vout[2] = g2 * (self.params.g.get() * a[3] * self.vout[1] + self.s[2]); 115 | } 116 | 117 | // linear version without distortion 118 | pub fn run_ladder_linear(&mut self, input: f32) { 119 | // denominators of solutions of individual stages. Simplifies the math a bit 120 | let g0 = 1. / (1. + self.params.g.get()); 121 | let g1 = self.params.g.get() * g0 * g0; 122 | let g2 = self.params.g.get() * g1 * g0; 123 | let g3 = self.params.g.get() * g2 * g0; 124 | // outputs a 24db filter 125 | self.vout[3] = 126 | (g3 * self.params.g.get() * input + g0 * self.s[3] + g1 * self.s[2] + g2 * self.s[1] + g3 * self.s[0]) 127 | / (g3 * self.params.g.get() * self.params.res.get() + 1.); 128 | // since we know the feedback, we can solve the remaining outputs: 129 | self.vout[0] = g0 * (self.params.g.get() * (input - self.params.res.get() * self.vout[3]) + self.s[0]); 130 | self.vout[1] = g0 * (self.params.g.get() * self.vout[0] + self.s[1]); 131 | self.vout[2] = g0 * (self.params.g.get() * self.vout[1] + self.s[2]); 132 | } 133 | } 134 | 135 | impl LadderParameters { 136 | pub fn set_cutoff(&self, value: f32) { 137 | // cutoff formula gives us a natural feeling cutoff knob that spends more time in the low frequencies 138 | self.cutoff.set(20000. * (1.8f32.powf(10. * value - 10.))); 139 | // bilinear transformation for g gives us a very accurate cutoff 140 | self.g.set((PI * self.cutoff.get() / (self.sample_rate.get())).tan()); 141 | } 142 | 143 | // returns the value used to set cutoff. for get_parameter function 144 | pub fn get_cutoff(&self) -> f32 { 145 | 1. + 0.17012975 * (0.00005 * self.cutoff.get()).ln() 146 | } 147 | 148 | pub fn set_poles(&self, value: f32) { 149 | self.pole_value.set(value); 150 | self.poles.store(((value * 3.).round()) as usize, Ordering::Relaxed); 151 | } 152 | } 153 | 154 | impl PluginParameters for LadderParameters { 155 | // get_parameter has to return the value used in set_parameter 156 | fn get_parameter(&self, index: i32) -> f32 { 157 | match index { 158 | 0 => self.get_cutoff(), 159 | 1 => self.res.get() / 4., 160 | 2 => self.pole_value.get(), 161 | 3 => self.drive.get() / 5., 162 | _ => 0.0, 163 | } 164 | } 165 | 166 | fn set_parameter(&self, index: i32, value: f32) { 167 | match index { 168 | 0 => self.set_cutoff(value), 169 | 1 => self.res.set(value * 4.), 170 | 2 => self.set_poles(value), 171 | 3 => self.drive.set(value * 5.), 172 | _ => (), 173 | } 174 | } 175 | 176 | fn get_parameter_name(&self, index: i32) -> String { 177 | match index { 178 | 0 => "cutoff".to_string(), 179 | 1 => "resonance".to_string(), 180 | 2 => "filter order".to_string(), 181 | 3 => "drive".to_string(), 182 | _ => "".to_string(), 183 | } 184 | } 185 | 186 | fn get_parameter_label(&self, index: i32) -> String { 187 | match index { 188 | 0 => "Hz".to_string(), 189 | 1 => "%".to_string(), 190 | 2 => "poles".to_string(), 191 | 3 => "%".to_string(), 192 | _ => "".to_string(), 193 | } 194 | } 195 | // This is what will display underneath our control. We can 196 | // format it into a string that makes the most sense. 197 | fn get_parameter_text(&self, index: i32) -> String { 198 | match index { 199 | 0 => format!("{:.0}", self.cutoff.get()), 200 | 1 => format!("{:.3}", self.res.get()), 201 | 2 => format!("{}", self.poles.load(Ordering::Relaxed) + 1), 202 | 3 => format!("{:.3}", self.drive.get()), 203 | _ => format!(""), 204 | } 205 | } 206 | } 207 | 208 | impl Plugin for LadderFilter { 209 | fn new(_host: HostCallback) -> Self { 210 | LadderFilter { 211 | vout: [0f32; 4], 212 | s: [0f32; 4], 213 | params: Arc::new(LadderParameters::default()), 214 | } 215 | } 216 | 217 | fn set_sample_rate(&mut self, rate: f32) { 218 | self.params.sample_rate.set(rate); 219 | } 220 | 221 | fn get_info(&self) -> Info { 222 | Info { 223 | name: "LadderFilter".to_string(), 224 | unique_id: 9263, 225 | inputs: 1, 226 | outputs: 1, 227 | category: Category::Effect, 228 | parameters: 4, 229 | ..Default::default() 230 | } 231 | } 232 | 233 | fn process(&mut self, buffer: &mut AudioBuffer) { 234 | for (input_buffer, output_buffer) in buffer.zip() { 235 | for (input_sample, output_sample) in input_buffer.iter().zip(output_buffer) { 236 | self.tick_pivotal(*input_sample); 237 | // the poles parameter chooses which filter stage we take our output from. 238 | *output_sample = self.vout[self.params.poles.load(Ordering::Relaxed)]; 239 | } 240 | } 241 | } 242 | 243 | fn get_parameter_object(&mut self) -> Arc { 244 | Arc::clone(&self.params) as Arc 245 | } 246 | } 247 | 248 | plugin_main!(LadderFilter); 249 | -------------------------------------------------------------------------------- /examples/simple_host.rs: -------------------------------------------------------------------------------- 1 | extern crate vst; 2 | 3 | use std::env; 4 | use std::path::Path; 5 | use std::process; 6 | use std::sync::{Arc, Mutex}; 7 | 8 | use vst::host::{Host, PluginLoader}; 9 | use vst::plugin::Plugin; 10 | 11 | #[allow(dead_code)] 12 | struct SampleHost; 13 | 14 | impl Host for SampleHost { 15 | fn automate(&self, index: i32, value: f32) { 16 | println!("Parameter {} had its value changed to {}", index, value); 17 | } 18 | } 19 | 20 | fn main() { 21 | let args: Vec = env::args().collect(); 22 | if args.len() < 2 { 23 | println!("usage: simple_host path/to/vst"); 24 | process::exit(1); 25 | } 26 | 27 | let path = Path::new(&args[1]); 28 | 29 | // Create the host 30 | let host = Arc::new(Mutex::new(SampleHost)); 31 | 32 | println!("Loading {}...", path.to_str().unwrap()); 33 | 34 | // Load the plugin 35 | let mut loader = 36 | PluginLoader::load(path, Arc::clone(&host)).unwrap_or_else(|e| panic!("Failed to load plugin: {}", e)); 37 | 38 | // Create an instance of the plugin 39 | let mut instance = loader.instance().unwrap(); 40 | 41 | // Get the plugin information 42 | let info = instance.get_info(); 43 | 44 | println!( 45 | "Loaded '{}':\n\t\ 46 | Vendor: {}\n\t\ 47 | Presets: {}\n\t\ 48 | Parameters: {}\n\t\ 49 | VST ID: {}\n\t\ 50 | Version: {}\n\t\ 51 | Initial Delay: {} samples", 52 | info.name, info.vendor, info.presets, info.parameters, info.unique_id, info.version, info.initial_delay 53 | ); 54 | 55 | // Initialize the instance 56 | instance.init(); 57 | println!("Initialized instance!"); 58 | 59 | println!("Closing instance..."); 60 | // Close the instance. This is not necessary as the instance is shut down when 61 | // it is dropped as it goes out of scope. 62 | // drop(instance); 63 | } 64 | -------------------------------------------------------------------------------- /examples/sine_synth.rs: -------------------------------------------------------------------------------- 1 | // author: Rob Saunders 2 | 3 | #[macro_use] 4 | extern crate vst; 5 | 6 | use vst::prelude::*; 7 | 8 | use std::f64::consts::PI; 9 | 10 | /// Convert the midi note's pitch into the equivalent frequency. 11 | /// 12 | /// This function assumes A4 is 440hz. 13 | fn midi_pitch_to_freq(pitch: u8) -> f64 { 14 | const A4_PITCH: i8 = 69; 15 | const A4_FREQ: f64 = 440.0; 16 | 17 | // Midi notes can be 0-127 18 | ((f64::from(pitch as i8 - A4_PITCH)) / 12.).exp2() * A4_FREQ 19 | } 20 | 21 | struct SineSynth { 22 | sample_rate: f64, 23 | time: f64, 24 | note_duration: f64, 25 | note: Option, 26 | } 27 | 28 | impl SineSynth { 29 | fn time_per_sample(&self) -> f64 { 30 | 1.0 / self.sample_rate 31 | } 32 | 33 | /// Process an incoming midi event. 34 | /// 35 | /// The midi data is split up like so: 36 | /// 37 | /// `data[0]`: Contains the status and the channel. Source: [source] 38 | /// `data[1]`: Contains the supplemental data for the message - so, if this was a NoteOn then 39 | /// this would contain the note. 40 | /// `data[2]`: Further supplemental data. Would be velocity in the case of a NoteOn message. 41 | /// 42 | /// [source]: http://www.midimountain.com/midi/midi_status.htm 43 | fn process_midi_event(&mut self, data: [u8; 3]) { 44 | match data[0] { 45 | 128 => self.note_off(data[1]), 46 | 144 => self.note_on(data[1]), 47 | _ => (), 48 | } 49 | } 50 | 51 | fn note_on(&mut self, note: u8) { 52 | self.note_duration = 0.0; 53 | self.note = Some(note) 54 | } 55 | 56 | fn note_off(&mut self, note: u8) { 57 | if self.note == Some(note) { 58 | self.note = None 59 | } 60 | } 61 | } 62 | 63 | pub const TAU: f64 = PI * 2.0; 64 | 65 | impl Plugin for SineSynth { 66 | fn new(_host: HostCallback) -> Self { 67 | SineSynth { 68 | sample_rate: 44100.0, 69 | note_duration: 0.0, 70 | time: 0.0, 71 | note: None, 72 | } 73 | } 74 | 75 | fn get_info(&self) -> Info { 76 | Info { 77 | name: "SineSynth".to_string(), 78 | vendor: "DeathDisco".to_string(), 79 | unique_id: 6667, 80 | category: Category::Synth, 81 | inputs: 2, 82 | outputs: 2, 83 | parameters: 0, 84 | initial_delay: 0, 85 | ..Info::default() 86 | } 87 | } 88 | 89 | #[allow(unused_variables)] 90 | #[allow(clippy::single_match)] 91 | fn process_events(&mut self, events: &Events) { 92 | for event in events.events() { 93 | match event { 94 | Event::Midi(ev) => self.process_midi_event(ev.data), 95 | // More events can be handled here. 96 | _ => (), 97 | } 98 | } 99 | } 100 | 101 | fn set_sample_rate(&mut self, rate: f32) { 102 | self.sample_rate = f64::from(rate); 103 | } 104 | 105 | fn process(&mut self, buffer: &mut AudioBuffer) { 106 | let samples = buffer.samples(); 107 | let (_, mut outputs) = buffer.split(); 108 | let output_count = outputs.len(); 109 | let per_sample = self.time_per_sample(); 110 | let mut output_sample; 111 | for sample_idx in 0..samples { 112 | let time = self.time; 113 | let note_duration = self.note_duration; 114 | if let Some(current_note) = self.note { 115 | let signal = (time * midi_pitch_to_freq(current_note) * TAU).sin(); 116 | 117 | // Apply a quick envelope to the attack of the signal to avoid popping. 118 | let attack = 0.5; 119 | let alpha = if note_duration < attack { 120 | note_duration / attack 121 | } else { 122 | 1.0 123 | }; 124 | 125 | output_sample = (signal * alpha) as f32; 126 | 127 | self.time += per_sample; 128 | self.note_duration += per_sample; 129 | } else { 130 | output_sample = 0.0; 131 | } 132 | for buf_idx in 0..output_count { 133 | let buff = outputs.get_mut(buf_idx); 134 | buff[sample_idx] = output_sample; 135 | } 136 | } 137 | } 138 | 139 | fn can_do(&self, can_do: CanDo) -> Supported { 140 | match can_do { 141 | CanDo::ReceiveMidiEvent => Supported::Yes, 142 | _ => Supported::Maybe, 143 | } 144 | } 145 | } 146 | 147 | plugin_main!(SineSynth); 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use midi_pitch_to_freq; 152 | 153 | #[test] 154 | fn test_midi_pitch_to_freq() { 155 | for i in 0..127 { 156 | // expect no panics 157 | midi_pitch_to_freq(i); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /examples/transfer_and_smooth.rs: -------------------------------------------------------------------------------- 1 | // This example illustrates how an existing plugin can be ported to the new, 2 | // thread-safe API with the help of the ParameterTransfer struct. 3 | // It shows how the parameter iteration feature of ParameterTransfer can be 4 | // used to react explicitly to parameter changes in an efficient way (here, 5 | // to implement smoothing of parameters). 6 | 7 | #[macro_use] 8 | extern crate vst; 9 | 10 | use std::f32; 11 | use std::sync::Arc; 12 | 13 | use vst::prelude::*; 14 | 15 | const PARAMETER_COUNT: usize = 100; 16 | const BASE_FREQUENCY: f32 = 5.0; 17 | const FILTER_FACTOR: f32 = 0.01; // Set this to 1.0 to disable smoothing. 18 | const TWO_PI: f32 = 2.0 * f32::consts::PI; 19 | 20 | // 1. Define a struct to hold parameters. Put a ParameterTransfer inside it, 21 | // plus optionally a HostCallback. 22 | struct MyPluginParameters { 23 | #[allow(dead_code)] 24 | host: HostCallback, 25 | transfer: ParameterTransfer, 26 | } 27 | 28 | // 2. Put an Arc reference to your parameter struct in your main Plugin struct. 29 | struct MyPlugin { 30 | params: Arc, 31 | states: Vec, 32 | sample_rate: f32, 33 | phase: f32, 34 | } 35 | 36 | // 3. Implement PluginParameters for your parameter struct. 37 | // The set_parameter and get_parameter just access the ParameterTransfer. 38 | // The other methods can be implemented on top of this as well. 39 | impl PluginParameters for MyPluginParameters { 40 | fn set_parameter(&self, index: i32, value: f32) { 41 | self.transfer.set_parameter(index as usize, value); 42 | } 43 | 44 | fn get_parameter(&self, index: i32) -> f32 { 45 | self.transfer.get_parameter(index as usize) 46 | } 47 | } 48 | 49 | impl Plugin for MyPlugin { 50 | fn new(host: HostCallback) -> Self { 51 | MyPlugin { 52 | // 4. Initialize your main Plugin struct with a parameter struct 53 | // wrapped in an Arc, and put the HostCallback inside it. 54 | params: Arc::new(MyPluginParameters { 55 | host, 56 | transfer: ParameterTransfer::new(PARAMETER_COUNT), 57 | }), 58 | states: vec![Smoothed::default(); PARAMETER_COUNT], 59 | sample_rate: 44100.0, 60 | phase: 0.0, 61 | } 62 | } 63 | 64 | fn get_info(&self) -> Info { 65 | Info { 66 | parameters: PARAMETER_COUNT as i32, 67 | inputs: 0, 68 | outputs: 2, 69 | category: Category::Synth, 70 | f64_precision: false, 71 | 72 | name: "transfer_and_smooth".to_string(), 73 | vendor: "Loonies".to_string(), 74 | unique_id: 0x500007, 75 | version: 100, 76 | 77 | ..Info::default() 78 | } 79 | } 80 | 81 | // 5. Return a reference to the parameter struct from get_parameter_object. 82 | fn get_parameter_object(&mut self) -> Arc { 83 | Arc::clone(&self.params) as Arc 84 | } 85 | 86 | fn set_sample_rate(&mut self, sample_rate: f32) { 87 | self.sample_rate = sample_rate; 88 | } 89 | 90 | fn process(&mut self, buffer: &mut AudioBuffer) { 91 | // 6. In the process method, iterate over changed parameters and do 92 | // for each what you would previously do in set_parameter. Since this 93 | // runs in the processing thread, it has mutable access to the Plugin. 94 | for (p, value) in self.params.transfer.iterate(true) { 95 | // Example: Update filter state of changed parameter. 96 | self.states[p].set(value); 97 | } 98 | 99 | // Example: Dummy synth adding together a bunch of sines. 100 | let samples = buffer.samples(); 101 | let mut outputs = buffer.split().1; 102 | for i in 0..samples { 103 | let mut sum = 0.0; 104 | for p in 0..PARAMETER_COUNT { 105 | let amp = self.states[p].get(); 106 | if amp != 0.0 { 107 | sum += (self.phase * p as f32 * TWO_PI).sin() * amp; 108 | } 109 | } 110 | outputs[0][i] = sum; 111 | outputs[1][i] = sum; 112 | self.phase = (self.phase + BASE_FREQUENCY / self.sample_rate).fract(); 113 | } 114 | } 115 | } 116 | 117 | // Example: Parameter smoothing as an example of non-trivial parameter handling 118 | // that has to happen when a parameter changes. 119 | #[derive(Clone, Default)] 120 | struct Smoothed { 121 | state: f32, 122 | target: f32, 123 | } 124 | 125 | impl Smoothed { 126 | fn set(&mut self, value: f32) { 127 | self.target = value; 128 | } 129 | 130 | fn get(&mut self) -> f32 { 131 | self.state += (self.target - self.state) * FILTER_FACTOR; 132 | self.state 133 | } 134 | } 135 | 136 | plugin_main!(MyPlugin); 137 | -------------------------------------------------------------------------------- /osx_vst_bundler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make sure we have the arguments we need 4 | if [[ -z $1 || -z $2 ]]; then 5 | echo "Generates a macOS bundle from a compiled dylib file" 6 | echo "Example:" 7 | echo -e "\t$0 Plugin target/release/plugin.dylib" 8 | echo -e "\tCreates a Plugin.vst bundle" 9 | else 10 | # Make the bundle folder 11 | mkdir -p "$1.vst/Contents/MacOS" 12 | 13 | # Create the PkgInfo 14 | echo "BNDL????" > "$1.vst/Contents/PkgInfo" 15 | 16 | #build the Info.Plist 17 | echo " 18 | 19 | 20 | 21 | CFBundleDevelopmentRegion 22 | English 23 | 24 | CFBundleExecutable 25 | $1 26 | 27 | CFBundleGetInfoString 28 | vst 29 | 30 | CFBundleIconFile 31 | 32 | 33 | CFBundleIdentifier 34 | com.rust-vst.$1 35 | 36 | CFBundleInfoDictionaryVersion 37 | 6.0 38 | 39 | CFBundleName 40 | $1 41 | 42 | CFBundlePackageType 43 | BNDL 44 | 45 | CFBundleVersion 46 | 1.0 47 | 48 | CFBundleSignature 49 | $((RANDOM % 9999)) 50 | 51 | CSResourcesFileMapped 52 | 53 | 54 | 55 | " > "$1.vst/Contents/Info.plist" 56 | 57 | # move the provided library to the correct location 58 | cp "$2" "$1.vst/Contents/MacOS/$1" 59 | 60 | echo "Created bundle $1.vst" 61 | fi 62 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 -------------------------------------------------------------------------------- /src/api.rs: -------------------------------------------------------------------------------- 1 | //! Structures and types for interfacing with the VST 2.4 API. 2 | 3 | use std::os::raw::c_void; 4 | use std::sync::Arc; 5 | 6 | use self::consts::*; 7 | use crate::{ 8 | editor::Editor, 9 | plugin::{Info, Plugin, PluginParameters}, 10 | }; 11 | 12 | /// Constant values 13 | #[allow(missing_docs)] // For obvious constants 14 | pub mod consts { 15 | 16 | pub const MAX_PRESET_NAME_LEN: usize = 24; 17 | pub const MAX_PARAM_STR_LEN: usize = 32; 18 | pub const MAX_LABEL: usize = 64; 19 | pub const MAX_SHORT_LABEL: usize = 8; 20 | pub const MAX_PRODUCT_STR_LEN: usize = 64; 21 | pub const MAX_VENDOR_STR_LEN: usize = 64; 22 | 23 | /// VST plugins are identified by a magic number. This corresponds to 0x56737450. 24 | pub const VST_MAGIC: i32 = ('V' as i32) << 24 | ('s' as i32) << 16 | ('t' as i32) << 8 | ('P' as i32); 25 | } 26 | 27 | /// `VSTPluginMain` function signature. 28 | pub type PluginMain = fn(callback: HostCallbackProc) -> *mut AEffect; 29 | 30 | /// Host callback function passed to plugin. 31 | /// Can be used to query host information from plugin side. 32 | pub type HostCallbackProc = 33 | extern "C" fn(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize; 34 | 35 | /// Dispatcher function used to process opcodes. Called by host. 36 | pub type DispatcherProc = 37 | extern "C" fn(effect: *mut AEffect, opcode: i32, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize; 38 | 39 | /// Process function used to process 32 bit floating point samples. Called by host. 40 | pub type ProcessProc = 41 | extern "C" fn(effect: *mut AEffect, inputs: *const *const f32, outputs: *mut *mut f32, sample_frames: i32); 42 | 43 | /// Process function used to process 64 bit floating point samples. Called by host. 44 | pub type ProcessProcF64 = 45 | extern "C" fn(effect: *mut AEffect, inputs: *const *const f64, outputs: *mut *mut f64, sample_frames: i32); 46 | 47 | /// Callback function used to set parameter values. Called by host. 48 | pub type SetParameterProc = extern "C" fn(effect: *mut AEffect, index: i32, parameter: f32); 49 | 50 | /// Callback function used to get parameter values. Called by host. 51 | pub type GetParameterProc = extern "C" fn(effect: *mut AEffect, index: i32) -> f32; 52 | 53 | /// Used with the VST API to pass around plugin information. 54 | #[allow(non_snake_case)] 55 | #[repr(C)] 56 | pub struct AEffect { 57 | /// Magic number. Must be `['V', 'S', 'T', 'P']`. 58 | pub magic: i32, 59 | 60 | /// Host to plug-in dispatcher. 61 | pub dispatcher: DispatcherProc, 62 | 63 | /// Accumulating process mode is deprecated in VST 2.4! Use `processReplacing` instead! 64 | pub _process: ProcessProc, 65 | 66 | /// Set value of automatable parameter. 67 | pub setParameter: SetParameterProc, 68 | 69 | /// Get value of automatable parameter. 70 | pub getParameter: GetParameterProc, 71 | 72 | /// Number of programs (Presets). 73 | pub numPrograms: i32, 74 | 75 | /// Number of parameters. All programs are assumed to have this many parameters. 76 | pub numParams: i32, 77 | 78 | /// Number of audio inputs. 79 | pub numInputs: i32, 80 | 81 | /// Number of audio outputs. 82 | pub numOutputs: i32, 83 | 84 | /// Bitmask made of values from `api::PluginFlags`. 85 | /// 86 | /// ```no_run 87 | /// use vst::api::PluginFlags; 88 | /// let flags = PluginFlags::CAN_REPLACING | PluginFlags::CAN_DOUBLE_REPLACING; 89 | /// // ... 90 | /// ``` 91 | pub flags: i32, 92 | 93 | /// Reserved for host, must be 0. 94 | pub reserved1: isize, 95 | 96 | /// Reserved for host, must be 0. 97 | pub reserved2: isize, 98 | 99 | /// For algorithms which need input in the first place (Group delay or latency in samples). 100 | /// 101 | /// This value should be initially in a resume state. 102 | pub initialDelay: i32, 103 | 104 | /// Deprecated unused member. 105 | pub _realQualities: i32, 106 | 107 | /// Deprecated unused member. 108 | pub _offQualities: i32, 109 | 110 | /// Deprecated unused member. 111 | pub _ioRatio: f32, 112 | 113 | /// Void pointer usable by api to store object data. 114 | pub object: *mut c_void, 115 | 116 | /// User defined pointer. 117 | pub user: *mut c_void, 118 | 119 | /// Registered unique identifier (register it at Steinberg 3rd party support Web). 120 | /// This is used to identify a plug-in during save+load of preset and project. 121 | pub uniqueId: i32, 122 | 123 | /// Plug-in version (e.g. 1100 for v1.1.0.0). 124 | pub version: i32, 125 | 126 | /// Process audio samples in replacing mode. 127 | pub processReplacing: ProcessProc, 128 | 129 | /// Process double-precision audio samples in replacing mode. 130 | pub processReplacingF64: ProcessProcF64, 131 | 132 | /// Reserved for future use (please zero). 133 | pub future: [u8; 56], 134 | } 135 | 136 | impl AEffect { 137 | /// Return handle to Plugin object. Only works for plugins created using this library. 138 | /// Caller is responsible for not calling this function concurrently. 139 | // Suppresses warning about returning a reference to a box 140 | #[allow(clippy::borrowed_box)] 141 | pub unsafe fn get_plugin(&self) -> &mut Box { 142 | //FIXME: find a way to do this without resorting to transmuting via a box 143 | &mut *(self.object as *mut Box) 144 | } 145 | 146 | /// Return handle to Info object. Only works for plugins created using this library. 147 | pub unsafe fn get_info(&self) -> &Info { 148 | &(*(self.user as *mut super::PluginCache)).info 149 | } 150 | 151 | /// Return handle to PluginParameters object. Only works for plugins created using this library. 152 | pub unsafe fn get_params(&self) -> &Arc { 153 | &(*(self.user as *mut super::PluginCache)).params 154 | } 155 | 156 | /// Return handle to Editor object. Only works for plugins created using this library. 157 | /// Caller is responsible for not calling this function concurrently. 158 | pub unsafe fn get_editor(&self) -> &mut Option> { 159 | &mut (*(self.user as *mut super::PluginCache)).editor 160 | } 161 | 162 | /// Drop the Plugin object. Only works for plugins created using this library. 163 | pub unsafe fn drop_plugin(&mut self) { 164 | drop(Box::from_raw(self.object as *mut Box)); 165 | drop(Box::from_raw(self.user as *mut super::PluginCache)); 166 | } 167 | } 168 | 169 | /// Information about a channel. Only some hosts use this information. 170 | #[repr(C)] 171 | pub struct ChannelProperties { 172 | /// Channel name. 173 | pub name: [u8; MAX_LABEL as usize], 174 | 175 | /// Flags found in `ChannelFlags`. 176 | pub flags: i32, 177 | 178 | /// Type of speaker arrangement this channel is a part of. 179 | pub arrangement_type: SpeakerArrangementType, 180 | 181 | /// Name of channel (recommended: 6 characters + delimiter). 182 | pub short_name: [u8; MAX_SHORT_LABEL as usize], 183 | 184 | /// Reserved for future use. 185 | pub future: [u8; 48], 186 | } 187 | 188 | /// Tells the host how the channels are intended to be used in the plugin. Only useful for some 189 | /// hosts. 190 | #[repr(i32)] 191 | #[derive(Clone, Copy)] 192 | pub enum SpeakerArrangementType { 193 | /// User defined arrangement. 194 | Custom = -2, 195 | /// Empty arrangement. 196 | Empty = -1, 197 | 198 | /// Mono. 199 | Mono = 0, 200 | 201 | /// L R 202 | Stereo, 203 | /// Ls Rs 204 | StereoSurround, 205 | /// Lc Rc 206 | StereoCenter, 207 | /// Sl Sr 208 | StereoSide, 209 | /// C Lfe 210 | StereoCLfe, 211 | 212 | /// L R C 213 | Cinema30, 214 | /// L R S 215 | Music30, 216 | 217 | /// L R C Lfe 218 | Cinema31, 219 | /// L R Lfe S 220 | Music31, 221 | 222 | /// L R C S (LCRS) 223 | Cinema40, 224 | /// L R Ls Rs (Quadro) 225 | Music40, 226 | 227 | /// L R C Lfe S (LCRS + Lfe) 228 | Cinema41, 229 | /// L R Lfe Ls Rs (Quadro + Lfe) 230 | Music41, 231 | 232 | /// L R C Ls Rs 233 | Surround50, 234 | /// L R C Lfe Ls Rs 235 | Surround51, 236 | 237 | /// L R C Ls Rs Cs 238 | Cinema60, 239 | /// L R Ls Rs Sl Sr 240 | Music60, 241 | 242 | /// L R C Lfe Ls Rs Cs 243 | Cinema61, 244 | /// L R Lfe Ls Rs Sl Sr 245 | Music61, 246 | 247 | /// L R C Ls Rs Lc Rc 248 | Cinema70, 249 | /// L R C Ls Rs Sl Sr 250 | Music70, 251 | 252 | /// L R C Lfe Ls Rs Lc Rc 253 | Cinema71, 254 | /// L R C Lfe Ls Rs Sl Sr 255 | Music71, 256 | 257 | /// L R C Ls Rs Lc Rc Cs 258 | Cinema80, 259 | /// L R C Ls Rs Cs Sl Sr 260 | Music80, 261 | 262 | /// L R C Lfe Ls Rs Lc Rc Cs 263 | Cinema81, 264 | /// L R C Lfe Ls Rs Cs Sl Sr 265 | Music81, 266 | 267 | /// L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2 268 | Surround102, 269 | } 270 | 271 | /// Used to specify whether functionality is supported. 272 | #[allow(missing_docs)] 273 | #[derive(PartialEq, Eq)] 274 | pub enum Supported { 275 | Yes, 276 | Maybe, 277 | No, 278 | Custom(isize), 279 | } 280 | 281 | impl Supported { 282 | /// Create a `Supported` value from an integer if possible. 283 | pub fn from(val: isize) -> Option { 284 | use self::Supported::*; 285 | 286 | match val { 287 | 1 => Some(Yes), 288 | 0 => Some(Maybe), 289 | -1 => Some(No), 290 | _ => None, 291 | } 292 | } 293 | } 294 | 295 | impl Into for Supported { 296 | /// Convert to integer ordinal for interop with VST api. 297 | fn into(self) -> isize { 298 | use self::Supported::*; 299 | 300 | match self { 301 | Yes => 1, 302 | Maybe => 0, 303 | No => -1, 304 | Custom(i) => i, 305 | } 306 | } 307 | } 308 | 309 | /// Denotes in which thread the host is in. 310 | #[repr(i32)] 311 | pub enum ProcessLevel { 312 | /// Unsupported by host. 313 | Unknown = 0, 314 | 315 | /// GUI thread. 316 | User, 317 | /// Audio process thread. 318 | Realtime, 319 | /// Sequence thread (MIDI, etc). 320 | Prefetch, 321 | /// Offline processing thread (therefore GUI/user thread). 322 | Offline, 323 | } 324 | 325 | /// Language that the host is using. 326 | #[repr(i32)] 327 | #[allow(missing_docs)] 328 | pub enum HostLanguage { 329 | English = 1, 330 | German, 331 | French, 332 | Italian, 333 | Spanish, 334 | Japanese, 335 | } 336 | 337 | /// The file operation to perform. 338 | #[repr(i32)] 339 | pub enum FileSelectCommand { 340 | /// Load a file. 341 | Load = 0, 342 | /// Save a file. 343 | Save, 344 | /// Load multiple files simultaneously. 345 | LoadMultipleFiles, 346 | /// Choose a directory. 347 | SelectDirectory, 348 | } 349 | 350 | // TODO: investigate removing this. 351 | /// Format to select files. 352 | pub enum FileSelectType { 353 | /// Regular file selector. 354 | Regular, 355 | } 356 | 357 | /// File type descriptor. 358 | #[repr(C)] 359 | pub struct FileType { 360 | /// Display name of file type. 361 | pub name: [u8; 128], 362 | 363 | /// OS X file type. 364 | pub osx_type: [u8; 8], 365 | /// Windows file type. 366 | pub win_type: [u8; 8], 367 | /// Unix file type. 368 | pub nix_type: [u8; 8], 369 | 370 | /// MIME type. 371 | pub mime_type_1: [u8; 128], 372 | /// Additional MIME type. 373 | pub mime_type_2: [u8; 128], 374 | } 375 | 376 | /// File selector descriptor used in `host::OpCode::OpenFileSelector`. 377 | #[repr(C)] 378 | pub struct FileSelect { 379 | /// The type of file selection to perform. 380 | pub command: FileSelectCommand, 381 | /// The file selector to open. 382 | pub select_type: FileSelectType, 383 | /// Unknown. 0 = no creator. 384 | pub mac_creator: i32, 385 | /// Number of file types. 386 | pub num_types: i32, 387 | /// List of file types to show. 388 | pub file_types: *mut FileType, 389 | 390 | /// File selector's title. 391 | pub title: [u8; 1024], 392 | /// Initial path. 393 | pub initial_path: *mut u8, 394 | /// Used when operation returns a single path. 395 | pub return_path: *mut u8, 396 | /// Size of the path buffer in bytes. 397 | pub size_return_path: i32, 398 | 399 | /// Used when operation returns multiple paths. 400 | pub return_multiple_paths: *mut *mut u8, 401 | /// Number of paths returned. 402 | pub num_paths: i32, 403 | 404 | /// Reserved by host. 405 | pub reserved: isize, 406 | /// Reserved for future use. 407 | pub future: [u8; 116], 408 | } 409 | 410 | /// A struct which contains events. 411 | #[repr(C)] 412 | pub struct Events { 413 | /// Number of events. 414 | pub num_events: i32, 415 | 416 | /// Reserved for future use. Should be 0. 417 | pub _reserved: isize, 418 | 419 | /// Variable-length array of pointers to `api::Event` objects. 420 | /// 421 | /// The VST standard specifies a variable length array of initial size 2. If there are more 422 | /// than 2 elements a larger array must be stored in this structure. 423 | pub events: [*mut Event; 2], 424 | } 425 | 426 | impl Events { 427 | #[inline] 428 | pub(crate) fn events_raw(&self) -> &[*const Event] { 429 | use std::slice; 430 | unsafe { 431 | slice::from_raw_parts( 432 | &self.events[0] as *const *mut _ as *const *const _, 433 | self.num_events as usize, 434 | ) 435 | } 436 | } 437 | 438 | #[inline] 439 | pub(crate) fn events_raw_mut(&mut self) -> &mut [*const SysExEvent] { 440 | use std::slice; 441 | unsafe { 442 | slice::from_raw_parts_mut( 443 | &mut self.events[0] as *mut *mut _ as *mut *const _, 444 | self.num_events as usize, 445 | ) 446 | } 447 | } 448 | 449 | /// Use this in your impl of process_events() to process the incoming midi events. 450 | /// 451 | /// # Example 452 | /// ```no_run 453 | /// # use vst::plugin::{Info, Plugin, HostCallback}; 454 | /// # use vst::buffer::{AudioBuffer, SendEventBuffer}; 455 | /// # use vst::host::Host; 456 | /// # use vst::api; 457 | /// # use vst::event::{Event, MidiEvent}; 458 | /// # struct ExamplePlugin { host: HostCallback, send_buf: SendEventBuffer } 459 | /// # impl Plugin for ExamplePlugin { 460 | /// # fn new(host: HostCallback) -> Self { Self { host, send_buf: Default::default() } } 461 | /// # 462 | /// # fn get_info(&self) -> Info { Default::default() } 463 | /// # 464 | /// fn process_events(&mut self, events: &api::Events) { 465 | /// for e in events.events() { 466 | /// match e { 467 | /// Event::Midi(MidiEvent { data, .. }) => { 468 | /// // ... 469 | /// } 470 | /// _ => () 471 | /// } 472 | /// } 473 | /// } 474 | /// # } 475 | /// ``` 476 | #[inline] 477 | #[allow(clippy::needless_lifetimes)] 478 | pub fn events<'a>(&'a self) -> impl Iterator> { 479 | self.events_raw() 480 | .iter() 481 | .map(|ptr| unsafe { crate::event::Event::from_raw_event(*ptr) }) 482 | } 483 | } 484 | 485 | /// The type of event that has occurred. See `api::Event.event_type`. 486 | #[repr(i32)] 487 | #[derive(Copy, Clone, Debug)] 488 | pub enum EventType { 489 | /// Value used for uninitialized placeholder events. 490 | _Placeholder = 0, 491 | 492 | /// Midi event. See `api::MidiEvent`. 493 | Midi = 1, 494 | 495 | /// Deprecated. 496 | _Audio, 497 | /// Deprecated. 498 | _Video, 499 | /// Deprecated. 500 | _Parameter, 501 | /// Deprecated. 502 | _Trigger, 503 | 504 | /// System exclusive event. See `api::SysExEvent`. 505 | SysEx, 506 | } 507 | 508 | /// A VST event intended to be casted to a corresponding type. 509 | /// 510 | /// The event types are not all guaranteed to be the same size, 511 | /// so casting between them can be done 512 | /// via `mem::transmute()` while leveraging pointers, e.g. 513 | /// 514 | /// ``` 515 | /// # use vst::api::{Event, EventType, MidiEvent, SysExEvent}; 516 | /// # let mut event: *mut Event = &mut unsafe { std::mem::zeroed() }; 517 | /// // let event: *const Event = ...; 518 | /// let midi_event: &MidiEvent = unsafe { std::mem::transmute(event) }; 519 | /// ``` 520 | #[repr(C)] 521 | #[derive(Copy, Clone)] 522 | pub struct Event { 523 | /// The type of event. This lets you know which event this object should be casted to. 524 | /// 525 | /// # Example 526 | /// 527 | /// ``` 528 | /// # use vst::api::{Event, EventType, MidiEvent, SysExEvent}; 529 | /// # 530 | /// # // Valid for test 531 | /// # let mut event: *mut Event = &mut unsafe { std::mem::zeroed() }; 532 | /// # 533 | /// // let mut event: *mut Event = ... 534 | /// match unsafe { (*event).event_type } { 535 | /// EventType::Midi => { 536 | /// let midi_event: &MidiEvent = unsafe { 537 | /// std::mem::transmute(event) 538 | /// }; 539 | /// 540 | /// // ... 541 | /// } 542 | /// EventType::SysEx => { 543 | /// let sys_event: &SysExEvent = unsafe { 544 | /// std::mem::transmute(event) 545 | /// }; 546 | /// 547 | /// // ... 548 | /// } 549 | /// // ... 550 | /// # _ => {} 551 | /// } 552 | /// ``` 553 | pub event_type: EventType, 554 | 555 | /// Size of this structure; `mem::sizeof::()`. 556 | pub byte_size: i32, 557 | 558 | /// Number of samples into the current processing block that this event occurs on. 559 | /// 560 | /// E.g. if the block size is 512 and this value is 123, the event will occur on sample 561 | /// `samples[123]`. 562 | pub delta_frames: i32, 563 | 564 | /// Generic flags, none defined in VST api yet. 565 | pub _flags: i32, 566 | 567 | /// The `Event` type is cast appropriately, so this acts as reserved space. 568 | /// 569 | /// The actual size of the data may vary 570 | ///as this type is not guaranteed to be the same size as the other event types. 571 | pub _reserved: [u8; 16], 572 | } 573 | 574 | /// A midi event. 575 | #[repr(C)] 576 | pub struct MidiEvent { 577 | /// Should be `EventType::Midi`. 578 | pub event_type: EventType, 579 | 580 | /// Size of this structure; `mem::sizeof::()`. 581 | pub byte_size: i32, 582 | 583 | /// Number of samples into the current processing block that this event occurs on. 584 | /// 585 | /// E.g. if the block size is 512 and this value is 123, the event will occur on sample 586 | /// `samples[123]`. 587 | pub delta_frames: i32, 588 | 589 | /// See `MidiEventFlags`. 590 | pub flags: i32, 591 | 592 | /// Length in sample frames of entire note if available, otherwise 0. 593 | pub note_length: i32, 594 | 595 | /// Offset in samples into note from start if available, otherwise 0. 596 | pub note_offset: i32, 597 | 598 | /// 1 to 3 midi bytes. TODO: Doc 599 | pub midi_data: [u8; 3], 600 | 601 | /// Reserved midi byte (0). 602 | pub _midi_reserved: u8, 603 | 604 | /// Detuning between -63 and +64 cents, 605 | /// for scales other than 'well-tempered'. e.g. 'microtuning' 606 | pub detune: i8, 607 | 608 | /// Note off velocity between 0 and 127. 609 | pub note_off_velocity: u8, 610 | 611 | /// Reserved for future use. Should be 0. 612 | pub _reserved1: u8, 613 | /// Reserved for future use. Should be 0. 614 | pub _reserved2: u8, 615 | } 616 | 617 | /// A midi system exclusive event. 618 | /// 619 | /// This event only contains raw byte data, and is up to the plugin to interpret it correctly. 620 | /// `plugin::CanDo` has a `ReceiveSysExEvent` variant which lets the host query the plugin as to 621 | /// whether this event is supported. 622 | #[repr(C)] 623 | #[derive(Clone)] 624 | pub struct SysExEvent { 625 | /// Should be `EventType::SysEx`. 626 | pub event_type: EventType, 627 | 628 | /// Size of this structure; `mem::sizeof::()`. 629 | pub byte_size: i32, 630 | 631 | /// Number of samples into the current processing block that this event occurs on. 632 | /// 633 | /// E.g. if the block size is 512 and this value is 123, the event will occur on sample 634 | /// `samples[123]`. 635 | pub delta_frames: i32, 636 | 637 | /// Generic flags, none defined in VST api yet. 638 | pub _flags: i32, 639 | 640 | /// Size of payload in bytes. 641 | pub data_size: i32, 642 | 643 | /// Reserved for future use. Should be 0. 644 | pub _reserved1: isize, 645 | 646 | /// Pointer to payload. 647 | pub system_data: *mut u8, 648 | 649 | /// Reserved for future use. Should be 0. 650 | pub _reserved2: isize, 651 | } 652 | 653 | unsafe impl Send for SysExEvent {} 654 | 655 | #[repr(C)] 656 | #[derive(Clone, Default, Copy)] 657 | /// Describes the time at the start of the block currently being processed 658 | pub struct TimeInfo { 659 | /// current Position in audio samples (always valid) 660 | pub sample_pos: f64, 661 | 662 | /// current Sample Rate in Hertz (always valid) 663 | pub sample_rate: f64, 664 | 665 | /// System Time in nanoseconds (10^-9 second) 666 | pub nanoseconds: f64, 667 | 668 | /// Musical Position, in Quarter Note (1.0 equals 1 Quarter Note) 669 | pub ppq_pos: f64, 670 | 671 | /// current Tempo in BPM (Beats Per Minute) 672 | pub tempo: f64, 673 | 674 | /// last Bar Start Position, in Quarter Note 675 | pub bar_start_pos: f64, 676 | 677 | /// Cycle Start (left locator), in Quarter Note 678 | pub cycle_start_pos: f64, 679 | 680 | /// Cycle End (right locator), in Quarter Note 681 | pub cycle_end_pos: f64, 682 | 683 | /// Time Signature Numerator (e.g. 3 for 3/4) 684 | pub time_sig_numerator: i32, 685 | 686 | /// Time Signature Denominator (e.g. 4 for 3/4) 687 | pub time_sig_denominator: i32, 688 | 689 | /// SMPTE offset in SMPTE subframes (bits; 1/80 of a frame). 690 | /// The current SMPTE position can be calculated using `sample_pos`, `sample_rate`, and `smpte_frame_rate`. 691 | pub smpte_offset: i32, 692 | 693 | /// See `SmpteFrameRate` 694 | pub smpte_frame_rate: SmpteFrameRate, 695 | 696 | /// MIDI Clock Resolution (24 Per Quarter Note), can be negative (nearest clock) 697 | pub samples_to_next_clock: i32, 698 | 699 | /// See `TimeInfoFlags` 700 | pub flags: i32, 701 | } 702 | 703 | #[repr(i32)] 704 | #[derive(Copy, Clone, Debug)] 705 | /// SMPTE Frame Rates. 706 | pub enum SmpteFrameRate { 707 | /// 24 fps 708 | Smpte24fps = 0, 709 | /// 25 fps 710 | Smpte25fps = 1, 711 | /// 29.97 fps 712 | Smpte2997fps = 2, 713 | /// 30 fps 714 | Smpte30fps = 3, 715 | 716 | /// 29.97 drop 717 | Smpte2997dfps = 4, 718 | /// 30 drop 719 | Smpte30dfps = 5, 720 | 721 | /// Film 16mm 722 | SmpteFilm16mm = 6, 723 | /// Film 35mm 724 | SmpteFilm35mm = 7, 725 | 726 | /// HDTV: 23.976 fps 727 | Smpte239fps = 10, 728 | /// HDTV: 24.976 fps 729 | Smpte249fps = 11, 730 | /// HDTV: 59.94 fps 731 | Smpte599fps = 12, 732 | /// HDTV: 60 fps 733 | Smpte60fps = 13, 734 | } 735 | impl Default for SmpteFrameRate { 736 | fn default() -> Self { 737 | SmpteFrameRate::Smpte24fps 738 | } 739 | } 740 | 741 | bitflags! { 742 | /// Flags for VST channels. 743 | pub struct ChannelFlags: i32 { 744 | /// Indicates channel is active. Ignored by host. 745 | const ACTIVE = 1; 746 | /// Indicates channel is first of stereo pair. 747 | const STEREO = 1 << 1; 748 | /// Use channel's specified speaker_arrangement instead of stereo flag. 749 | const SPEAKER = 1 << 2; 750 | } 751 | } 752 | 753 | bitflags! { 754 | /// Flags for VST plugins. 755 | pub struct PluginFlags: i32 { 756 | /// Plugin has an editor. 757 | const HAS_EDITOR = 1; 758 | /// Plugin can process 32 bit audio. (Mandatory in VST 2.4). 759 | const CAN_REPLACING = 1 << 4; 760 | /// Plugin preset data is handled in formatless chunks. 761 | const PROGRAM_CHUNKS = 1 << 5; 762 | /// Plugin is a synth. 763 | const IS_SYNTH = 1 << 8; 764 | /// Plugin does not produce sound when all input is silence. 765 | const NO_SOUND_IN_STOP = 1 << 9; 766 | /// Supports 64 bit audio processing. 767 | const CAN_DOUBLE_REPLACING = 1 << 12; 768 | } 769 | } 770 | 771 | bitflags! { 772 | /// Cross platform modifier key flags. 773 | pub struct ModifierKey: u8 { 774 | /// Shift key. 775 | const SHIFT = 1; 776 | /// Alt key. 777 | const ALT = 1 << 1; 778 | /// Control on mac. 779 | const COMMAND = 1 << 2; 780 | /// Command on mac, ctrl on other. 781 | const CONTROL = 1 << 3; // Ctrl on PC, Apple on Mac 782 | } 783 | } 784 | 785 | bitflags! { 786 | /// MIDI event flags. 787 | pub struct MidiEventFlags: i32 { 788 | /// This event is played live (not in playback from a sequencer track). This allows the 789 | /// plugin to handle these flagged events with higher priority, especially when the 790 | /// plugin has a big latency as per `plugin::Info::initial_delay`. 791 | const REALTIME_EVENT = 1; 792 | } 793 | } 794 | 795 | bitflags! { 796 | /// Used in the `flags` field of `TimeInfo`, and for querying the host for specific values 797 | pub struct TimeInfoFlags : i32 { 798 | /// Indicates that play, cycle or record state has changed. 799 | const TRANSPORT_CHANGED = 1; 800 | /// Set if Host sequencer is currently playing. 801 | const TRANSPORT_PLAYING = 1 << 1; 802 | /// Set if Host sequencer is in cycle mode. 803 | const TRANSPORT_CYCLE_ACTIVE = 1 << 2; 804 | /// Set if Host sequencer is in record mode. 805 | const TRANSPORT_RECORDING = 1 << 3; 806 | 807 | /// Set if automation write mode active (record parameter changes). 808 | const AUTOMATION_WRITING = 1 << 6; 809 | /// Set if automation read mode active (play parameter changes). 810 | const AUTOMATION_READING = 1 << 7; 811 | 812 | /// Set if TimeInfo::nanoseconds is valid. 813 | const NANOSECONDS_VALID = 1 << 8; 814 | /// Set if TimeInfo::ppq_pos is valid. 815 | const PPQ_POS_VALID = 1 << 9; 816 | /// Set if TimeInfo::tempo is valid. 817 | const TEMPO_VALID = 1 << 10; 818 | /// Set if TimeInfo::bar_start_pos is valid. 819 | const BARS_VALID = 1 << 11; 820 | /// Set if both TimeInfo::cycle_start_pos and VstTimeInfo::cycle_end_pos are valid. 821 | const CYCLE_POS_VALID = 1 << 12; 822 | /// Set if both TimeInfo::time_sig_numerator and TimeInfo::time_sig_denominator are valid. 823 | const TIME_SIG_VALID = 1 << 13; 824 | /// Set if both TimeInfo::smpte_offset and VstTimeInfo::smpte_frame_rate are valid. 825 | const SMPTE_VALID = 1 << 14; 826 | /// Set if TimeInfo::samples_to_next_clock is valid. 827 | const VST_CLOCK_VALID = 1 << 15; 828 | } 829 | } 830 | 831 | #[cfg(test)] 832 | mod tests { 833 | use super::super::event; 834 | use super::*; 835 | use std::mem; 836 | 837 | // This container is used because we have to store somewhere the events 838 | // that are pointed to by raw pointers in the events object. We heap allocate 839 | // the event so the pointer in events stays consistent when the container is moved. 840 | pub struct EventContainer { 841 | stored_event: Box, 842 | pub events: Events, 843 | } 844 | 845 | // A convenience method which creates an api::Events object representing a midi event. 846 | // This represents code that might be found in a VST host using this API. 847 | fn encode_midi_message_as_events(message: [u8; 3]) -> EventContainer { 848 | let midi_event: MidiEvent = MidiEvent { 849 | event_type: EventType::Midi, 850 | byte_size: mem::size_of::() as i32, 851 | delta_frames: 0, 852 | flags: 0, 853 | note_length: 0, 854 | note_offset: 0, 855 | midi_data: [message[0], message[1], message[2]], 856 | _midi_reserved: 0, 857 | detune: 0, 858 | note_off_velocity: 0, 859 | _reserved1: 0, 860 | _reserved2: 0, 861 | }; 862 | let mut event: Event = unsafe { std::mem::transmute(midi_event) }; 863 | event.event_type = EventType::Midi; 864 | 865 | let events = Events { 866 | num_events: 1, 867 | _reserved: 0, 868 | events: [&mut event, &mut event], // Second one is a dummy 869 | }; 870 | let mut ec = EventContainer { 871 | stored_event: Box::new(event), 872 | events, 873 | }; 874 | ec.events.events[0] = &mut *(ec.stored_event); // Overwrite ptrs, since we moved the event into ec 875 | ec 876 | } 877 | 878 | #[test] 879 | fn encode_and_decode_gives_back_original_message() { 880 | let message: [u8; 3] = [35, 16, 22]; 881 | let encoded = encode_midi_message_as_events(message); 882 | assert_eq!(encoded.events.num_events, 1); 883 | assert_eq!(encoded.events.events.len(), 2); 884 | let e_vec: Vec = encoded.events.events().collect(); 885 | assert_eq!(e_vec.len(), 1); 886 | 887 | match e_vec[0] { 888 | event::Event::Midi(event::MidiEvent { data, .. }) => { 889 | assert_eq!(data, message); 890 | } 891 | _ => { 892 | panic!("Not a midi event!"); 893 | } 894 | }; 895 | } 896 | 897 | // This is a regression test for a bug fixed in PR #93 898 | // We check here that calling events() on an api::Events object 899 | // does not mutate the underlying events. 900 | #[test] 901 | fn message_survives_calling_events() { 902 | let message: [u8; 3] = [35, 16, 22]; 903 | let encoded = encode_midi_message_as_events(message); 904 | 905 | for e in encoded.events.events() { 906 | match e { 907 | event::Event::Midi(event::MidiEvent { data, .. }) => { 908 | assert_eq!(data, message); 909 | } 910 | _ => { 911 | panic!("Not a midi event!"); 912 | } 913 | } 914 | } 915 | 916 | for e in encoded.events.events() { 917 | match e { 918 | event::Event::Midi(event::MidiEvent { data, .. }) => { 919 | assert_eq!(data, message); 920 | } 921 | _ => { 922 | panic!("Not a midi event!"); // FAILS here! 923 | } 924 | } 925 | } 926 | } 927 | } 928 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | //! Buffers to safely work with audio samples. 2 | 3 | use num_traits::Float; 4 | 5 | use std::slice; 6 | 7 | /// `AudioBuffer` contains references to the audio buffers for all input and output channels. 8 | /// 9 | /// To create an `AudioBuffer` in a host, use a [`HostBuffer`](../host/struct.HostBuffer.html). 10 | pub struct AudioBuffer<'a, T: 'a + Float> { 11 | inputs: &'a [*const T], 12 | outputs: &'a mut [*mut T], 13 | samples: usize, 14 | } 15 | 16 | impl<'a, T: 'a + Float> AudioBuffer<'a, T> { 17 | /// Create an `AudioBuffer` from raw pointers. 18 | /// Only really useful for interacting with the VST API. 19 | #[inline] 20 | pub unsafe fn from_raw( 21 | input_count: usize, 22 | output_count: usize, 23 | inputs_raw: *const *const T, 24 | outputs_raw: *mut *mut T, 25 | samples: usize, 26 | ) -> Self { 27 | Self { 28 | inputs: slice::from_raw_parts(inputs_raw, input_count), 29 | outputs: slice::from_raw_parts_mut(outputs_raw, output_count), 30 | samples, 31 | } 32 | } 33 | 34 | /// The number of input channels that this buffer was created for 35 | #[inline] 36 | pub fn input_count(&self) -> usize { 37 | self.inputs.len() 38 | } 39 | 40 | /// The number of output channels that this buffer was created for 41 | #[inline] 42 | pub fn output_count(&self) -> usize { 43 | self.outputs.len() 44 | } 45 | 46 | /// The number of samples in this buffer (same for all channels) 47 | #[inline] 48 | pub fn samples(&self) -> usize { 49 | self.samples 50 | } 51 | 52 | /// The raw inputs to pass to processReplacing 53 | #[inline] 54 | pub(crate) fn raw_inputs(&self) -> &[*const T] { 55 | self.inputs 56 | } 57 | 58 | /// The raw outputs to pass to processReplacing 59 | #[inline] 60 | pub(crate) fn raw_outputs(&mut self) -> &mut [*mut T] { 61 | &mut self.outputs 62 | } 63 | 64 | /// Split this buffer into separate inputs and outputs. 65 | #[inline] 66 | pub fn split<'b>(&'b mut self) -> (Inputs<'b, T>, Outputs<'b, T>) 67 | where 68 | 'a: 'b, 69 | { 70 | ( 71 | Inputs { 72 | bufs: self.inputs, 73 | samples: self.samples, 74 | }, 75 | Outputs { 76 | bufs: self.outputs, 77 | samples: self.samples, 78 | }, 79 | ) 80 | } 81 | 82 | /// Create an iterator over pairs of input buffers and output buffers. 83 | #[inline] 84 | pub fn zip<'b>(&'b mut self) -> AudioBufferIterator<'a, 'b, T> { 85 | AudioBufferIterator { 86 | audio_buffer: self, 87 | index: 0, 88 | } 89 | } 90 | } 91 | 92 | /// Iterator over pairs of buffers of input channels and output channels. 93 | pub struct AudioBufferIterator<'a, 'b, T> 94 | where 95 | T: 'a + Float, 96 | 'a: 'b, 97 | { 98 | audio_buffer: &'b mut AudioBuffer<'a, T>, 99 | index: usize, 100 | } 101 | 102 | impl<'a, 'b, T> Iterator for AudioBufferIterator<'a, 'b, T> 103 | where 104 | T: 'b + Float, 105 | { 106 | type Item = (&'b [T], &'b mut [T]); 107 | 108 | fn next(&mut self) -> Option { 109 | if self.index < self.audio_buffer.inputs.len() && self.index < self.audio_buffer.outputs.len() { 110 | let input = 111 | unsafe { slice::from_raw_parts(self.audio_buffer.inputs[self.index], self.audio_buffer.samples) }; 112 | let output = 113 | unsafe { slice::from_raw_parts_mut(self.audio_buffer.outputs[self.index], self.audio_buffer.samples) }; 114 | let val = (input, output); 115 | self.index += 1; 116 | Some(val) 117 | } else { 118 | None 119 | } 120 | } 121 | } 122 | 123 | use std::ops::{Index, IndexMut}; 124 | 125 | /// Wrapper type to access the buffers for the input channels of an `AudioBuffer` in a safe way. 126 | /// Behaves like a slice. 127 | #[derive(Copy, Clone)] 128 | pub struct Inputs<'a, T: 'a> { 129 | bufs: &'a [*const T], 130 | samples: usize, 131 | } 132 | 133 | impl<'a, T> Inputs<'a, T> { 134 | /// Number of channels 135 | pub fn len(&self) -> usize { 136 | self.bufs.len() 137 | } 138 | 139 | /// Returns true if the buffer is empty 140 | pub fn is_empty(&self) -> bool { 141 | self.len() == 0 142 | } 143 | 144 | /// Access channel at the given index 145 | pub fn get(&self, i: usize) -> &'a [T] { 146 | unsafe { slice::from_raw_parts(self.bufs[i], self.samples) } 147 | } 148 | 149 | /// Split borrowing at the given index, like for slices 150 | pub fn split_at(&self, i: usize) -> (Inputs<'a, T>, Inputs<'a, T>) { 151 | let (l, r) = self.bufs.split_at(i); 152 | ( 153 | Inputs { 154 | bufs: l, 155 | samples: self.samples, 156 | }, 157 | Inputs { 158 | bufs: r, 159 | samples: self.samples, 160 | }, 161 | ) 162 | } 163 | } 164 | 165 | impl<'a, T> Index for Inputs<'a, T> { 166 | type Output = [T]; 167 | 168 | fn index(&self, i: usize) -> &Self::Output { 169 | self.get(i) 170 | } 171 | } 172 | 173 | /// Iterator over buffers for input channels of an `AudioBuffer`. 174 | pub struct InputIterator<'a, T: 'a> { 175 | data: Inputs<'a, T>, 176 | i: usize, 177 | } 178 | 179 | impl<'a, T> Iterator for InputIterator<'a, T> { 180 | type Item = &'a [T]; 181 | 182 | fn next(&mut self) -> Option { 183 | if self.i < self.data.len() { 184 | let val = self.data.get(self.i); 185 | self.i += 1; 186 | Some(val) 187 | } else { 188 | None 189 | } 190 | } 191 | } 192 | 193 | impl<'a, T: Sized> IntoIterator for Inputs<'a, T> { 194 | type Item = &'a [T]; 195 | type IntoIter = InputIterator<'a, T>; 196 | 197 | fn into_iter(self) -> Self::IntoIter { 198 | InputIterator { data: self, i: 0 } 199 | } 200 | } 201 | 202 | /// Wrapper type to access the buffers for the output channels of an `AudioBuffer` in a safe way. 203 | /// Behaves like a slice. 204 | pub struct Outputs<'a, T: 'a> { 205 | bufs: &'a [*mut T], 206 | samples: usize, 207 | } 208 | 209 | impl<'a, T> Outputs<'a, T> { 210 | /// Number of channels 211 | pub fn len(&self) -> usize { 212 | self.bufs.len() 213 | } 214 | 215 | /// Returns true if the buffer is empty 216 | pub fn is_empty(&self) -> bool { 217 | self.len() == 0 218 | } 219 | 220 | /// Access channel at the given index 221 | pub fn get(&self, i: usize) -> &'a [T] { 222 | unsafe { slice::from_raw_parts(self.bufs[i], self.samples) } 223 | } 224 | 225 | /// Mutably access channel at the given index 226 | pub fn get_mut(&mut self, i: usize) -> &'a mut [T] { 227 | unsafe { slice::from_raw_parts_mut(self.bufs[i], self.samples) } 228 | } 229 | 230 | /// Split borrowing at the given index, like for slices 231 | pub fn split_at_mut(self, i: usize) -> (Outputs<'a, T>, Outputs<'a, T>) { 232 | let (l, r) = self.bufs.split_at(i); 233 | ( 234 | Outputs { 235 | bufs: l, 236 | samples: self.samples, 237 | }, 238 | Outputs { 239 | bufs: r, 240 | samples: self.samples, 241 | }, 242 | ) 243 | } 244 | } 245 | 246 | impl<'a, T> Index for Outputs<'a, T> { 247 | type Output = [T]; 248 | 249 | fn index(&self, i: usize) -> &Self::Output { 250 | self.get(i) 251 | } 252 | } 253 | 254 | impl<'a, T> IndexMut for Outputs<'a, T> { 255 | fn index_mut(&mut self, i: usize) -> &mut Self::Output { 256 | self.get_mut(i) 257 | } 258 | } 259 | 260 | /// Iterator over buffers for output channels of an `AudioBuffer`. 261 | pub struct OutputIterator<'a, 'b, T> 262 | where 263 | T: 'a, 264 | 'a: 'b, 265 | { 266 | data: &'b mut Outputs<'a, T>, 267 | i: usize, 268 | } 269 | 270 | impl<'a, 'b, T> Iterator for OutputIterator<'a, 'b, T> 271 | where 272 | T: 'b, 273 | { 274 | type Item = &'b mut [T]; 275 | 276 | fn next(&mut self) -> Option { 277 | if self.i < self.data.len() { 278 | let val = self.data.get_mut(self.i); 279 | self.i += 1; 280 | Some(val) 281 | } else { 282 | None 283 | } 284 | } 285 | } 286 | 287 | impl<'a, 'b, T: Sized> IntoIterator for &'b mut Outputs<'a, T> { 288 | type Item = &'b mut [T]; 289 | type IntoIter = OutputIterator<'a, 'b, T>; 290 | 291 | fn into_iter(self) -> Self::IntoIter { 292 | OutputIterator { data: self, i: 0 } 293 | } 294 | } 295 | 296 | use crate::event::{Event, MidiEvent, SysExEvent}; 297 | 298 | /// This is used as a placeholder to pre-allocate space for a fixed number of 299 | /// midi events in the re-useable `SendEventBuffer`, because `SysExEvent` is 300 | /// larger than `MidiEvent`, so either one can be stored in a `SysExEvent`. 301 | pub type PlaceholderEvent = api::SysExEvent; 302 | 303 | /// This trait is used by `SendEventBuffer::send_events` to accept iterators over midi events 304 | pub trait WriteIntoPlaceholder { 305 | /// writes an event into the given placeholder memory location 306 | fn write_into(&self, out: &mut PlaceholderEvent); 307 | } 308 | 309 | impl<'a, T: WriteIntoPlaceholder> WriteIntoPlaceholder for &'a T { 310 | fn write_into(&self, out: &mut PlaceholderEvent) { 311 | (*self).write_into(out); 312 | } 313 | } 314 | 315 | impl WriteIntoPlaceholder for MidiEvent { 316 | fn write_into(&self, out: &mut PlaceholderEvent) { 317 | let out = unsafe { &mut *(out as *mut _ as *mut _) }; 318 | *out = api::MidiEvent { 319 | event_type: api::EventType::Midi, 320 | byte_size: mem::size_of::() as i32, 321 | delta_frames: self.delta_frames, 322 | flags: if self.live { 323 | api::MidiEventFlags::REALTIME_EVENT.bits() 324 | } else { 325 | 0 326 | }, 327 | note_length: self.note_length.unwrap_or(0), 328 | note_offset: self.note_offset.unwrap_or(0), 329 | midi_data: self.data, 330 | _midi_reserved: 0, 331 | detune: self.detune, 332 | note_off_velocity: self.note_off_velocity, 333 | _reserved1: 0, 334 | _reserved2: 0, 335 | }; 336 | } 337 | } 338 | 339 | impl<'a> WriteIntoPlaceholder for SysExEvent<'a> { 340 | fn write_into(&self, out: &mut PlaceholderEvent) { 341 | *out = PlaceholderEvent { 342 | event_type: api::EventType::SysEx, 343 | byte_size: mem::size_of::() as i32, 344 | delta_frames: self.delta_frames, 345 | _flags: 0, 346 | data_size: self.payload.len() as i32, 347 | _reserved1: 0, 348 | system_data: self.payload.as_ptr() as *const u8 as *mut u8, 349 | _reserved2: 0, 350 | }; 351 | } 352 | } 353 | 354 | impl<'a> WriteIntoPlaceholder for Event<'a> { 355 | fn write_into(&self, out: &mut PlaceholderEvent) { 356 | match *self { 357 | Event::Midi(ref ev) => { 358 | ev.write_into(out); 359 | } 360 | Event::SysEx(ref ev) => { 361 | ev.write_into(out); 362 | } 363 | Event::Deprecated(e) => { 364 | let out = unsafe { &mut *(out as *mut _ as *mut _) }; 365 | *out = e; 366 | } 367 | }; 368 | } 369 | } 370 | 371 | use crate::{api, host::Host}; 372 | use std::mem; 373 | 374 | /// This buffer is used for sending midi events through the VST interface. 375 | /// The purpose of this is to convert outgoing midi events from `event::Event` to `api::Events`. 376 | /// It only allocates memory in new() and reuses the memory between calls. 377 | pub struct SendEventBuffer { 378 | buf: Vec, 379 | api_events: Vec, // using SysExEvent to store both because it's larger than MidiEvent 380 | } 381 | 382 | impl Default for SendEventBuffer { 383 | fn default() -> Self { 384 | SendEventBuffer::new(1024) 385 | } 386 | } 387 | 388 | impl SendEventBuffer { 389 | /// Creates a buffer for sending up to the given number of midi events per frame 390 | #[inline(always)] 391 | pub fn new(capacity: usize) -> Self { 392 | let header_size = mem::size_of::() - (mem::size_of::<*mut api::Event>() * 2); 393 | let body_size = mem::size_of::<*mut api::Event>() * capacity; 394 | let mut buf = vec![0u8; header_size + body_size]; 395 | let api_events = vec![unsafe { mem::zeroed::() }; capacity]; 396 | { 397 | let ptrs = { 398 | let e = Self::buf_as_api_events(&mut buf); 399 | e.num_events = capacity as i32; 400 | e.events_raw_mut() 401 | }; 402 | for (ptr, event) in ptrs.iter_mut().zip(&api_events) { 403 | let (ptr, event): (&mut *const PlaceholderEvent, &PlaceholderEvent) = (ptr, event); 404 | *ptr = event; 405 | } 406 | } 407 | Self { buf, api_events } 408 | } 409 | 410 | /// Sends events to the host. See the `fwd_midi` example. 411 | /// 412 | /// # Example 413 | /// ```no_run 414 | /// # use vst::plugin::{Info, Plugin, HostCallback}; 415 | /// # use vst::buffer::{AudioBuffer, SendEventBuffer}; 416 | /// # use vst::host::Host; 417 | /// # use vst::event::*; 418 | /// # struct ExamplePlugin { host: HostCallback, send_buffer: SendEventBuffer } 419 | /// # impl Plugin for ExamplePlugin { 420 | /// # fn new(host: HostCallback) -> Self { Self { host, send_buffer: Default::default() } } 421 | /// # 422 | /// # fn get_info(&self) -> Info { Default::default() } 423 | /// # 424 | /// fn process(&mut self, buffer: &mut AudioBuffer){ 425 | /// let events: Vec = vec![ 426 | /// // ... 427 | /// ]; 428 | /// self.send_buffer.send_events(&events, &mut self.host); 429 | /// } 430 | /// # } 431 | /// ``` 432 | #[inline(always)] 433 | pub fn send_events, U: WriteIntoPlaceholder>(&mut self, events: T, host: &mut dyn Host) { 434 | self.store_events(events); 435 | host.process_events(self.events()); 436 | } 437 | 438 | /// Stores events in the buffer, replacing the buffer's current content. 439 | /// Use this in [`process_events`](crate::Plugin::process_events) to store received input events, then read them in [`process`](crate::Plugin::process) using [`events`](SendEventBuffer::events). 440 | #[inline(always)] 441 | pub fn store_events, U: WriteIntoPlaceholder>(&mut self, events: T) { 442 | #[allow(clippy::suspicious_map)] 443 | let count = events 444 | .into_iter() 445 | .zip(self.api_events.iter_mut()) 446 | .map(|(ev, out)| ev.write_into(out)) 447 | .count(); 448 | self.set_num_events(count); 449 | } 450 | 451 | /// Returns a reference to the stored events 452 | #[inline(always)] 453 | pub fn events(&self) -> &api::Events { 454 | #[allow(clippy::cast_ptr_alignment)] 455 | unsafe { 456 | &*(self.buf.as_ptr() as *const api::Events) 457 | } 458 | } 459 | 460 | /// Clears the buffer 461 | #[inline(always)] 462 | pub fn clear(&mut self) { 463 | self.set_num_events(0); 464 | } 465 | 466 | #[inline(always)] 467 | fn buf_as_api_events(buf: &mut [u8]) -> &mut api::Events { 468 | #[allow(clippy::cast_ptr_alignment)] 469 | unsafe { 470 | &mut *(buf.as_mut_ptr() as *mut api::Events) 471 | } 472 | } 473 | 474 | #[inline(always)] 475 | fn set_num_events(&mut self, events_len: usize) { 476 | use std::cmp::min; 477 | let e = Self::buf_as_api_events(&mut self.buf); 478 | e.num_events = min(self.api_events.len(), events_len) as i32; 479 | } 480 | } 481 | 482 | #[cfg(test)] 483 | mod tests { 484 | use crate::buffer::AudioBuffer; 485 | 486 | /// Size of buffers used in tests. 487 | const SIZE: usize = 1024; 488 | 489 | /// Test that creating and zipping buffers works. 490 | /// 491 | /// This test creates a channel for 2 inputs and 2 outputs. 492 | /// The input channels are simply values 493 | /// from 0 to `SIZE-1` (e.g. [0, 1, 2, 3, 4, .. , SIZE - 1]) 494 | /// and the output channels are just 0. 495 | /// This test assures that when the buffers are zipped together, 496 | /// the input values do not change. 497 | #[test] 498 | fn buffer_zip() { 499 | let in1: Vec = (0..SIZE).map(|x| x as f32).collect(); 500 | let in2 = in1.clone(); 501 | 502 | let mut out1 = vec![0.0; SIZE]; 503 | let mut out2 = out1.clone(); 504 | 505 | let inputs = vec![in1.as_ptr(), in2.as_ptr()]; 506 | let mut outputs = vec![out1.as_mut_ptr(), out2.as_mut_ptr()]; 507 | let mut buffer = unsafe { AudioBuffer::from_raw(2, 2, inputs.as_ptr(), outputs.as_mut_ptr(), SIZE) }; 508 | 509 | for (input, output) in buffer.zip() { 510 | input.iter().zip(output.iter_mut()).fold(0, |acc, (input, output)| { 511 | assert_eq!(*input, acc as f32); 512 | assert_eq!(*output, 0.0); 513 | acc + 1 514 | }); 515 | } 516 | } 517 | 518 | // Test that the `zip()` method returns an iterator that gives `n` elements 519 | // where n is the number of inputs when this is lower than the number of outputs. 520 | #[test] 521 | fn buffer_zip_fewer_inputs_than_outputs() { 522 | let in1 = vec![1.0; SIZE]; 523 | let in2 = vec![2.0; SIZE]; 524 | 525 | let mut out1 = vec![3.0; SIZE]; 526 | let mut out2 = vec![4.0; SIZE]; 527 | let mut out3 = vec![5.0; SIZE]; 528 | 529 | let inputs = vec![in1.as_ptr(), in2.as_ptr()]; 530 | let mut outputs = vec![out1.as_mut_ptr(), out2.as_mut_ptr(), out3.as_mut_ptr()]; 531 | let mut buffer = unsafe { AudioBuffer::from_raw(2, 3, inputs.as_ptr(), outputs.as_mut_ptr(), SIZE) }; 532 | 533 | let mut iter = buffer.zip(); 534 | if let Some((observed_in1, observed_out1)) = iter.next() { 535 | assert_eq!(1.0, observed_in1[0]); 536 | assert_eq!(3.0, observed_out1[0]); 537 | } else { 538 | unreachable!(); 539 | } 540 | 541 | if let Some((observed_in2, observed_out2)) = iter.next() { 542 | assert_eq!(2.0, observed_in2[0]); 543 | assert_eq!(4.0, observed_out2[0]); 544 | } else { 545 | unreachable!(); 546 | } 547 | 548 | assert_eq!(None, iter.next()); 549 | } 550 | 551 | // Test that the `zip()` method returns an iterator that gives `n` elements 552 | // where n is the number of outputs when this is lower than the number of inputs. 553 | #[test] 554 | fn buffer_zip_more_inputs_than_outputs() { 555 | let in1 = vec![1.0; SIZE]; 556 | let in2 = vec![2.0; SIZE]; 557 | let in3 = vec![3.0; SIZE]; 558 | 559 | let mut out1 = vec![4.0; SIZE]; 560 | let mut out2 = vec![5.0; SIZE]; 561 | 562 | let inputs = vec![in1.as_ptr(), in2.as_ptr(), in3.as_ptr()]; 563 | let mut outputs = vec![out1.as_mut_ptr(), out2.as_mut_ptr()]; 564 | let mut buffer = unsafe { AudioBuffer::from_raw(3, 2, inputs.as_ptr(), outputs.as_mut_ptr(), SIZE) }; 565 | 566 | let mut iter = buffer.zip(); 567 | 568 | if let Some((observed_in1, observed_out1)) = iter.next() { 569 | assert_eq!(1.0, observed_in1[0]); 570 | assert_eq!(4.0, observed_out1[0]); 571 | } else { 572 | unreachable!(); 573 | } 574 | 575 | if let Some((observed_in2, observed_out2)) = iter.next() { 576 | assert_eq!(2.0, observed_in2[0]); 577 | assert_eq!(5.0, observed_out2[0]); 578 | } else { 579 | unreachable!(); 580 | } 581 | 582 | assert_eq!(None, iter.next()); 583 | } 584 | 585 | /// Test that creating buffers from raw pointers works. 586 | #[test] 587 | fn from_raw() { 588 | let in1: Vec = (0..SIZE).map(|x| x as f32).collect(); 589 | let in2 = in1.clone(); 590 | 591 | let mut out1 = vec![0.0; SIZE]; 592 | let mut out2 = out1.clone(); 593 | 594 | let inputs = vec![in1.as_ptr(), in2.as_ptr()]; 595 | let mut outputs = vec![out1.as_mut_ptr(), out2.as_mut_ptr()]; 596 | let mut buffer = unsafe { AudioBuffer::from_raw(2, 2, inputs.as_ptr(), outputs.as_mut_ptr(), SIZE) }; 597 | 598 | for (input, output) in buffer.zip() { 599 | input.iter().zip(output.iter_mut()).fold(0, |acc, (input, output)| { 600 | assert_eq!(*input, acc as f32); 601 | assert_eq!(*output, 0.0); 602 | acc + 1 603 | }); 604 | } 605 | } 606 | } 607 | -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::{editor::Editor, prelude::*}; 4 | 5 | pub(crate) struct PluginCache { 6 | pub info: Info, 7 | pub params: Arc, 8 | pub editor: Option>, 9 | } 10 | 11 | impl PluginCache { 12 | pub fn new(info: &Info, params: Arc, editor: Option>) -> Self { 13 | Self { 14 | info: info.clone(), 15 | params, 16 | editor, 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/channels.rs: -------------------------------------------------------------------------------- 1 | //! Meta data for dealing with input / output channels. Not all hosts use this so it is not 2 | //! necessary for plugin functionality. 3 | 4 | use crate::api; 5 | use crate::api::consts::{MAX_LABEL, MAX_SHORT_LABEL}; 6 | 7 | /// Information about an input / output channel. This isn't necessary for a channel to function but 8 | /// informs the host how the channel is meant to be used. 9 | pub struct ChannelInfo { 10 | name: String, 11 | short_name: String, 12 | active: bool, 13 | arrangement_type: SpeakerArrangementType, 14 | } 15 | 16 | impl ChannelInfo { 17 | /// Construct a new `ChannelInfo` object. 18 | /// 19 | /// `name` is a user friendly name for this channel limited to `MAX_LABEL` characters. 20 | /// `short_name` is an optional field which provides a short name limited to `MAX_SHORT_LABEL`. 21 | /// `active` determines whether this channel is active. 22 | /// `arrangement_type` describes the arrangement type for this channel. 23 | pub fn new( 24 | name: String, 25 | short_name: Option, 26 | active: bool, 27 | arrangement_type: Option, 28 | ) -> ChannelInfo { 29 | ChannelInfo { 30 | name: name.clone(), 31 | 32 | short_name: if let Some(short_name) = short_name { 33 | short_name 34 | } else { 35 | name 36 | }, 37 | 38 | active, 39 | 40 | arrangement_type: arrangement_type.unwrap_or(SpeakerArrangementType::Custom), 41 | } 42 | } 43 | } 44 | 45 | impl Into for ChannelInfo { 46 | /// Convert to the VST api equivalent of this structure. 47 | fn into(self) -> api::ChannelProperties { 48 | api::ChannelProperties { 49 | name: { 50 | let mut label = [0; MAX_LABEL as usize]; 51 | for (b, c) in self.name.bytes().zip(label.iter_mut()) { 52 | *c = b; 53 | } 54 | label 55 | }, 56 | flags: { 57 | let mut flag = api::ChannelFlags::empty(); 58 | if self.active { 59 | flag |= api::ChannelFlags::ACTIVE 60 | } 61 | if self.arrangement_type.is_left_stereo() { 62 | flag |= api::ChannelFlags::STEREO 63 | } 64 | if self.arrangement_type.is_speaker_type() { 65 | flag |= api::ChannelFlags::SPEAKER 66 | } 67 | flag.bits() 68 | }, 69 | arrangement_type: self.arrangement_type.into(), 70 | short_name: { 71 | let mut label = [0; MAX_SHORT_LABEL as usize]; 72 | for (b, c) in self.short_name.bytes().zip(label.iter_mut()) { 73 | *c = b; 74 | } 75 | label 76 | }, 77 | future: [0; 48], 78 | } 79 | } 80 | } 81 | 82 | impl From for ChannelInfo { 83 | fn from(api: api::ChannelProperties) -> ChannelInfo { 84 | ChannelInfo { 85 | name: String::from_utf8_lossy(&api.name).to_string(), 86 | short_name: String::from_utf8_lossy(&api.short_name).to_string(), 87 | active: api::ChannelFlags::from_bits(api.flags) 88 | .expect("Invalid bits in channel info") 89 | .intersects(api::ChannelFlags::ACTIVE), 90 | arrangement_type: SpeakerArrangementType::from(api), 91 | } 92 | } 93 | } 94 | 95 | /// Target for Speaker arrangement type. Can be a cinema configuration or music configuration. Both 96 | /// are technically identical but this provides extra information to the host. 97 | pub enum ArrangementTarget { 98 | /// Music arrangement. Technically identical to Cinema. 99 | Music, 100 | /// Cinematic arrangement. Technically identical to Music. 101 | Cinema, 102 | } 103 | 104 | /// An enum for all channels in a stereo configuration. 105 | pub enum StereoChannel { 106 | /// Left channel. 107 | Left, 108 | /// Right channel. 109 | Right, 110 | } 111 | 112 | /// Possible stereo speaker configurations. 113 | #[allow(non_camel_case_types)] 114 | pub enum StereoConfig { 115 | /// Regular. 116 | L_R, 117 | /// Left surround, right surround. 118 | Ls_Rs, 119 | /// Left center, right center. 120 | Lc_Rc, 121 | /// Side left, side right. 122 | Sl_Sr, 123 | /// Center, low frequency effects. 124 | C_Lfe, 125 | } 126 | 127 | /// Possible surround speaker configurations. 128 | #[allow(non_camel_case_types)] 129 | pub enum SurroundConfig { 130 | /// 3.0 surround sound. 131 | /// Cinema: L R C 132 | /// Music: L R S 133 | S3_0(ArrangementTarget), 134 | /// 3.1 surround sound. 135 | /// Cinema: L R C Lfe 136 | /// Music: L R Lfe S 137 | S3_1(ArrangementTarget), 138 | /// 4.0 surround sound. 139 | /// Cinema: L R C S (LCRS) 140 | /// Music: L R Ls Rs (Quadro) 141 | S4_0(ArrangementTarget), 142 | /// 4.1 surround sound. 143 | /// Cinema: L R C Lfe S (LCRS + Lfe) 144 | /// Music: L R Ls Rs (Quadro + Lfe) 145 | S4_1(ArrangementTarget), 146 | /// 5.0 surround sound. 147 | /// Cinema and music: L R C Ls Rs 148 | S5_0, 149 | /// 5.1 surround sound. 150 | /// Cinema and music: L R C Lfe Ls Rs 151 | S5_1, 152 | /// 6.0 surround sound. 153 | /// Cinema: L R C Ls Rs Cs 154 | /// Music: L R Ls Rs Sl Sr 155 | S6_0(ArrangementTarget), 156 | /// 6.1 surround sound. 157 | /// Cinema: L R C Lfe Ls Rs Cs 158 | /// Music: L R Ls Rs Sl Sr 159 | S6_1(ArrangementTarget), 160 | /// 7.0 surround sound. 161 | /// Cinema: L R C Ls Rs Lc Rc 162 | /// Music: L R C Ls Rs Sl Sr 163 | S7_0(ArrangementTarget), 164 | /// 7.1 surround sound. 165 | /// Cinema: L R C Lfe Ls Rs Lc Rc 166 | /// Music: L R C Lfe Ls Rs Sl Sr 167 | S7_1(ArrangementTarget), 168 | /// 8.0 surround sound. 169 | /// Cinema: L R C Ls Rs Lc Rc Cs 170 | /// Music: L R C Ls Rs Cs Sl Sr 171 | S8_0(ArrangementTarget), 172 | /// 8.1 surround sound. 173 | /// Cinema: L R C Lfe Ls Rs Lc Rc Cs 174 | /// Music: L R C Lfe Ls Rs Cs Sl Sr 175 | S8_1(ArrangementTarget), 176 | /// 10.2 surround sound. 177 | /// Cinema + Music: L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2 178 | S10_2, 179 | } 180 | 181 | /// Type representing how a channel is used. Only useful for some hosts. 182 | pub enum SpeakerArrangementType { 183 | /// Custom arrangement not specified to host. 184 | Custom, 185 | /// Empty arrangement. 186 | Empty, 187 | /// Mono channel. 188 | Mono, 189 | /// Stereo channel. Contains type of stereo arrangement and speaker represented. 190 | Stereo(StereoConfig, StereoChannel), 191 | /// Surround channel. Contains surround arrangement and target (cinema or music). 192 | Surround(SurroundConfig), 193 | } 194 | 195 | impl Default for SpeakerArrangementType { 196 | fn default() -> SpeakerArrangementType { 197 | SpeakerArrangementType::Mono 198 | } 199 | } 200 | 201 | impl SpeakerArrangementType { 202 | /// Determine whether this channel is part of a surround speaker arrangement. 203 | pub fn is_speaker_type(&self) -> bool { 204 | if let SpeakerArrangementType::Surround(..) = *self { 205 | true 206 | } else { 207 | false 208 | } 209 | } 210 | 211 | /// Determine whether this channel is the left speaker in a stereo pair. 212 | pub fn is_left_stereo(&self) -> bool { 213 | if let SpeakerArrangementType::Stereo(_, StereoChannel::Left) = *self { 214 | true 215 | } else { 216 | false 217 | } 218 | } 219 | } 220 | 221 | impl Into for SpeakerArrangementType { 222 | /// Convert to VST API arrangement type. 223 | fn into(self) -> api::SpeakerArrangementType { 224 | use self::ArrangementTarget::{Cinema, Music}; 225 | use self::SpeakerArrangementType::*; 226 | use api::SpeakerArrangementType as Raw; 227 | 228 | match self { 229 | Custom => Raw::Custom, 230 | Empty => Raw::Empty, 231 | Mono => Raw::Mono, 232 | Stereo(conf, _) => { 233 | match conf { 234 | // Stereo channels. 235 | StereoConfig::L_R => Raw::Stereo, 236 | StereoConfig::Ls_Rs => Raw::StereoSurround, 237 | StereoConfig::Lc_Rc => Raw::StereoCenter, 238 | StereoConfig::Sl_Sr => Raw::StereoSide, 239 | StereoConfig::C_Lfe => Raw::StereoCLfe, 240 | } 241 | } 242 | Surround(conf) => { 243 | match conf { 244 | // Surround channels. 245 | SurroundConfig::S3_0(Music) => Raw::Music30, 246 | SurroundConfig::S3_0(Cinema) => Raw::Cinema30, 247 | 248 | SurroundConfig::S3_1(Music) => Raw::Music31, 249 | SurroundConfig::S3_1(Cinema) => Raw::Cinema31, 250 | 251 | SurroundConfig::S4_0(Music) => Raw::Music40, 252 | SurroundConfig::S4_0(Cinema) => Raw::Cinema40, 253 | 254 | SurroundConfig::S4_1(Music) => Raw::Music41, 255 | SurroundConfig::S4_1(Cinema) => Raw::Cinema41, 256 | 257 | SurroundConfig::S5_0 => Raw::Surround50, 258 | SurroundConfig::S5_1 => Raw::Surround51, 259 | 260 | SurroundConfig::S6_0(Music) => Raw::Music60, 261 | SurroundConfig::S6_0(Cinema) => Raw::Cinema60, 262 | 263 | SurroundConfig::S6_1(Music) => Raw::Music61, 264 | SurroundConfig::S6_1(Cinema) => Raw::Cinema61, 265 | 266 | SurroundConfig::S7_0(Music) => Raw::Music70, 267 | SurroundConfig::S7_0(Cinema) => Raw::Cinema70, 268 | 269 | SurroundConfig::S7_1(Music) => Raw::Music71, 270 | SurroundConfig::S7_1(Cinema) => Raw::Cinema71, 271 | 272 | SurroundConfig::S8_0(Music) => Raw::Music80, 273 | SurroundConfig::S8_0(Cinema) => Raw::Cinema80, 274 | 275 | SurroundConfig::S8_1(Music) => Raw::Music81, 276 | SurroundConfig::S8_1(Cinema) => Raw::Cinema81, 277 | 278 | SurroundConfig::S10_2 => Raw::Surround102, 279 | } 280 | } 281 | } 282 | } 283 | } 284 | 285 | /// Convert the VST API equivalent struct into something more usable. 286 | /// 287 | /// We implement `From` as `SpeakerArrangementType` contains extra info about 288 | /// stereo speakers found in the channel flags. 289 | impl From for SpeakerArrangementType { 290 | fn from(api: api::ChannelProperties) -> SpeakerArrangementType { 291 | use self::ArrangementTarget::{Cinema, Music}; 292 | use self::SpeakerArrangementType::*; 293 | use self::SurroundConfig::*; 294 | use api::SpeakerArrangementType as Raw; 295 | 296 | let stereo = if api::ChannelFlags::from_bits(api.flags) 297 | .expect("Invalid Channel Flags") 298 | .intersects(api::ChannelFlags::STEREO) 299 | { 300 | StereoChannel::Left 301 | } else { 302 | StereoChannel::Right 303 | }; 304 | 305 | match api.arrangement_type { 306 | Raw::Custom => Custom, 307 | Raw::Empty => Empty, 308 | Raw::Mono => Mono, 309 | 310 | Raw::Stereo => Stereo(StereoConfig::L_R, stereo), 311 | Raw::StereoSurround => Stereo(StereoConfig::Ls_Rs, stereo), 312 | Raw::StereoCenter => Stereo(StereoConfig::Lc_Rc, stereo), 313 | Raw::StereoSide => Stereo(StereoConfig::Sl_Sr, stereo), 314 | Raw::StereoCLfe => Stereo(StereoConfig::C_Lfe, stereo), 315 | 316 | Raw::Music30 => Surround(S3_0(Music)), 317 | Raw::Cinema30 => Surround(S3_0(Cinema)), 318 | 319 | Raw::Music31 => Surround(S3_1(Music)), 320 | Raw::Cinema31 => Surround(S3_1(Cinema)), 321 | 322 | Raw::Music40 => Surround(S4_0(Music)), 323 | Raw::Cinema40 => Surround(S4_0(Cinema)), 324 | 325 | Raw::Music41 => Surround(S4_1(Music)), 326 | Raw::Cinema41 => Surround(S4_1(Cinema)), 327 | 328 | Raw::Surround50 => Surround(S5_0), 329 | Raw::Surround51 => Surround(S5_1), 330 | 331 | Raw::Music60 => Surround(S6_0(Music)), 332 | Raw::Cinema60 => Surround(S6_0(Cinema)), 333 | 334 | Raw::Music61 => Surround(S6_1(Music)), 335 | Raw::Cinema61 => Surround(S6_1(Cinema)), 336 | 337 | Raw::Music70 => Surround(S7_0(Music)), 338 | Raw::Cinema70 => Surround(S7_0(Cinema)), 339 | 340 | Raw::Music71 => Surround(S7_1(Music)), 341 | Raw::Cinema71 => Surround(S7_1(Cinema)), 342 | 343 | Raw::Music80 => Surround(S8_0(Music)), 344 | Raw::Cinema80 => Surround(S8_0(Cinema)), 345 | 346 | Raw::Music81 => Surround(S8_1(Music)), 347 | Raw::Cinema81 => Surround(S8_1(Cinema)), 348 | 349 | Raw::Surround102 => Surround(S10_2), 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /src/editor.rs: -------------------------------------------------------------------------------- 1 | //! All VST plugin editor related functionality. 2 | 3 | use num_enum::{IntoPrimitive, TryFromPrimitive}; 4 | 5 | use std::os::raw::c_void; 6 | 7 | /// Implemented by plugin editors. 8 | #[allow(unused_variables)] 9 | pub trait Editor { 10 | /// Get the size of the editor window. 11 | fn size(&self) -> (i32, i32); 12 | 13 | /// Get the coordinates of the editor window. 14 | fn position(&self) -> (i32, i32); 15 | 16 | /// Editor idle call. Called by host. 17 | fn idle(&mut self) {} 18 | 19 | /// Called when the editor window is closed. 20 | fn close(&mut self) {} 21 | 22 | /// Called when the editor window is opened. 23 | /// 24 | /// `parent` is a window pointer that the new window should attach itself to. 25 | /// **It is dependent upon the platform you are targeting.** 26 | /// 27 | /// A few examples: 28 | /// 29 | /// - On Windows, it should be interpreted as a `HWND` 30 | /// - On Mac OS X (64 bit), it should be interpreted as a `NSView*` 31 | /// - On X11 platforms, it should be interpreted as a `u32` (the ID number of the parent window) 32 | /// 33 | /// Return `true` if the window opened successfully, `false` otherwise. 34 | fn open(&mut self, parent: *mut c_void) -> bool; 35 | 36 | /// Return whether the window is currently open. 37 | fn is_open(&mut self) -> bool; 38 | 39 | /// Set the knob mode for this editor (if supported by host). 40 | /// 41 | /// Return `true` if the knob mode was set. 42 | fn set_knob_mode(&mut self, mode: KnobMode) -> bool { 43 | false 44 | } 45 | 46 | /// Receive key up event. Return `true` if the key was used. 47 | fn key_up(&mut self, keycode: KeyCode) -> bool { 48 | false 49 | } 50 | 51 | /// Receive key down event. Return `true` if the key was used. 52 | fn key_down(&mut self, keycode: KeyCode) -> bool { 53 | false 54 | } 55 | } 56 | 57 | /// Rectangle used to specify dimensions of editor window. 58 | #[doc(hidden)] 59 | #[derive(Copy, Clone, Debug)] 60 | pub struct Rect { 61 | /// Y value in pixels of top side. 62 | pub top: i16, 63 | /// X value in pixels of left side. 64 | pub left: i16, 65 | /// Y value in pixels of bottom side. 66 | pub bottom: i16, 67 | /// X value in pixels of right side. 68 | pub right: i16, 69 | } 70 | 71 | /// A platform independent key code. Includes modifier keys. 72 | #[derive(Copy, Clone, Debug)] 73 | pub struct KeyCode { 74 | /// ASCII character for key pressed (if applicable). 75 | pub character: char, 76 | /// Key pressed. See `enums::Key`. 77 | pub key: Key, 78 | /// Modifier key bitflags. See `enums::flags::modifier_key`. 79 | pub modifier: u8, 80 | } 81 | 82 | /// Allows host to set how a parameter knob works. 83 | #[repr(isize)] 84 | #[derive(Copy, Clone, Debug, TryFromPrimitive, IntoPrimitive)] 85 | #[allow(missing_docs)] 86 | pub enum KnobMode { 87 | Circular, 88 | CircularRelative, 89 | Linear, 90 | } 91 | 92 | /// Platform independent key codes. 93 | #[allow(missing_docs)] 94 | #[repr(isize)] 95 | #[derive(Debug, Copy, Clone, TryFromPrimitive, IntoPrimitive)] 96 | pub enum Key { 97 | None = 0, 98 | Back, 99 | Tab, 100 | Clear, 101 | Return, 102 | Pause, 103 | Escape, 104 | Space, 105 | Next, 106 | End, 107 | Home, 108 | Left, 109 | Up, 110 | Right, 111 | Down, 112 | PageUp, 113 | PageDown, 114 | Select, 115 | Print, 116 | Enter, 117 | Snapshot, 118 | Insert, 119 | Delete, 120 | Help, 121 | Numpad0, 122 | Numpad1, 123 | Numpad2, 124 | Numpad3, 125 | Numpad4, 126 | Numpad5, 127 | Numpad6, 128 | Numpad7, 129 | Numpad8, 130 | Numpad9, 131 | Multiply, 132 | Add, 133 | Separator, 134 | Subtract, 135 | Decimal, 136 | Divide, 137 | F1, 138 | F2, 139 | F3, 140 | F4, 141 | F5, 142 | F6, 143 | F7, 144 | F8, 145 | F9, 146 | F10, 147 | F11, 148 | F12, 149 | Numlock, 150 | Scroll, 151 | Shift, 152 | Control, 153 | Alt, 154 | Equals, 155 | } 156 | -------------------------------------------------------------------------------- /src/event.rs: -------------------------------------------------------------------------------- 1 | //! Interfaces to VST events. 2 | // TODO: Update and explain both host and plugin events 3 | 4 | use std::{mem, slice}; 5 | 6 | use crate::api; 7 | 8 | /// A VST event. 9 | #[derive(Copy, Clone)] 10 | pub enum Event<'a> { 11 | /// A midi event. 12 | /// 13 | /// These are sent to the plugin before `Plugin::processing()` or `Plugin::processing_f64()` is 14 | /// called. 15 | Midi(MidiEvent), 16 | 17 | /// A system exclusive event. 18 | /// 19 | /// This is just a block of data and it is up to the plugin to interpret this. Generally used 20 | /// by midi controllers. 21 | SysEx(SysExEvent<'a>), 22 | 23 | /// A deprecated event. 24 | /// 25 | /// Passes the raw midi event structure along with this so that implementors can handle 26 | /// optionally handle this event. 27 | Deprecated(api::Event), 28 | } 29 | 30 | /// A midi event. 31 | /// 32 | /// These are sent to the plugin before `Plugin::processing()` or `Plugin::processing_f64()` is 33 | /// called. 34 | #[derive(Copy, Clone)] 35 | pub struct MidiEvent { 36 | /// The raw midi data associated with this event. 37 | pub data: [u8; 3], 38 | 39 | /// Number of samples into the current processing block that this event occurs on. 40 | /// 41 | /// E.g. if the block size is 512 and this value is 123, the event will occur on sample 42 | /// `samples[123]`. 43 | // TODO: Don't repeat this value in all event types 44 | pub delta_frames: i32, 45 | 46 | /// This midi event was created live as opposed to being played back in the sequencer. 47 | /// 48 | /// This can give the plugin priority over this event if it introduces a lot of latency. 49 | pub live: bool, 50 | 51 | /// The length of the midi note associated with this event, if available. 52 | pub note_length: Option, 53 | 54 | /// Offset in samples into note from note start, if available. 55 | pub note_offset: Option, 56 | 57 | /// Detuning between -63 and +64 cents. 58 | pub detune: i8, 59 | 60 | /// Note off velocity between 0 and 127. 61 | pub note_off_velocity: u8, 62 | } 63 | 64 | /// A system exclusive event. 65 | /// 66 | /// This is just a block of data and it is up to the plugin to interpret this. Generally used 67 | /// by midi controllers. 68 | #[derive(Copy, Clone)] 69 | pub struct SysExEvent<'a> { 70 | /// The SysEx payload. 71 | pub payload: &'a [u8], 72 | 73 | /// Number of samples into the current processing block that this event occurs on. 74 | /// 75 | /// E.g. if the block size is 512 and this value is 123, the event will occur on sample 76 | /// `samples[123]`. 77 | pub delta_frames: i32, 78 | } 79 | 80 | impl<'a> Event<'a> { 81 | /// Creates a high-level event from the given low-level API event. 82 | /// 83 | /// # Safety 84 | /// 85 | /// You must ensure that the given pointer refers to a valid event of the correct event type. 86 | /// For example, if the event type is [`api::EventType::SysEx`], it should point to a 87 | /// [`SysExEvent`]. In case of a [`SysExEvent`], `system_data` and `data_size` must be correct. 88 | pub unsafe fn from_raw_event(event: *const api::Event) -> Event<'a> { 89 | use api::EventType::*; 90 | let event = &*event; 91 | match event.event_type { 92 | Midi => { 93 | let event: api::MidiEvent = mem::transmute(*event); 94 | 95 | let length = if event.note_length > 0 { 96 | Some(event.note_length) 97 | } else { 98 | None 99 | }; 100 | let offset = if event.note_offset > 0 { 101 | Some(event.note_offset) 102 | } else { 103 | None 104 | }; 105 | let flags = api::MidiEventFlags::from_bits(event.flags).unwrap(); 106 | 107 | Event::Midi(MidiEvent { 108 | data: event.midi_data, 109 | delta_frames: event.delta_frames, 110 | live: flags.intersects(api::MidiEventFlags::REALTIME_EVENT), 111 | note_length: length, 112 | note_offset: offset, 113 | detune: event.detune, 114 | note_off_velocity: event.note_off_velocity, 115 | }) 116 | } 117 | 118 | SysEx => Event::SysEx(SysExEvent { 119 | payload: { 120 | // We can safely cast the event pointer to a `SysExEvent` pointer as 121 | // event_type refers to a `SysEx` type. 122 | #[allow(clippy::cast_ptr_alignment)] 123 | let event: &api::SysExEvent = &*(event as *const api::Event as *const api::SysExEvent); 124 | slice::from_raw_parts(event.system_data, event.data_size as usize) 125 | }, 126 | 127 | delta_frames: event.delta_frames, 128 | }), 129 | 130 | _ => Event::Deprecated(*event), 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/host.rs: -------------------------------------------------------------------------------- 1 | //! Host specific structures. 2 | 3 | use num_enum::{IntoPrimitive, TryFromPrimitive}; 4 | use num_traits::Float; 5 | 6 | use libloading::Library; 7 | use std::cell::UnsafeCell; 8 | use std::convert::TryFrom; 9 | use std::error::Error; 10 | use std::ffi::CString; 11 | use std::mem::MaybeUninit; 12 | use std::os::raw::c_void; 13 | use std::path::Path; 14 | use std::sync::{Arc, Mutex}; 15 | use std::{fmt, ptr, slice}; 16 | 17 | use crate::{ 18 | api::{self, consts::*, AEffect, PluginFlags, PluginMain, Supported, TimeInfo}, 19 | buffer::AudioBuffer, 20 | channels::ChannelInfo, 21 | editor::{Editor, Rect}, 22 | interfaces, 23 | plugin::{self, Category, HostCallback, Info, Plugin, PluginParameters}, 24 | }; 25 | 26 | #[repr(i32)] 27 | #[derive(Clone, Copy, Debug, TryFromPrimitive, IntoPrimitive)] 28 | #[doc(hidden)] 29 | pub enum OpCode { 30 | /// [index]: parameter index 31 | /// [opt]: parameter value 32 | Automate = 0, 33 | /// [return]: host vst version (e.g. 2400 for VST 2.4) 34 | Version, 35 | /// [return]: current plugin ID (useful for shell plugins to figure out which plugin to load in 36 | /// `VSTPluginMain()`). 37 | CurrentId, 38 | /// No arguments. Give idle time to Host application, e.g. if plug-in editor is doing mouse 39 | /// tracking in a modal loop. 40 | Idle, 41 | /// Deprecated. 42 | _PinConnected = 4, 43 | 44 | /// Deprecated. 45 | _WantMidi = 6, // Not a typo 46 | /// [value]: request mask. see `VstTimeInfoFlags` 47 | /// [return]: `VstTimeInfo` pointer or null if not supported. 48 | GetTime, 49 | /// Inform host that the plugin has MIDI events ready to be processed. Should be called at the 50 | /// end of `Plugin::process`. 51 | /// [ptr]: `VstEvents*` the events to be processed. 52 | /// [return]: 1 if supported and processed OK. 53 | ProcessEvents, 54 | /// Deprecated. 55 | _SetTime, 56 | /// Deprecated. 57 | _TempoAt, 58 | /// Deprecated. 59 | _GetNumAutomatableParameters, 60 | /// Deprecated. 61 | _GetParameterQuantization, 62 | 63 | /// Notifies the host that the input/output setup has changed. This can allow the host to check 64 | /// numInputs/numOutputs or call `getSpeakerArrangement()`. 65 | /// [return]: 1 if supported. 66 | IOChanged, 67 | 68 | /// Deprecated. 69 | _NeedIdle, 70 | 71 | /// Request the host to resize the plugin window. 72 | /// [index]: new width. 73 | /// [value]: new height. 74 | SizeWindow, 75 | /// [return]: the current sample rate. 76 | GetSampleRate, 77 | /// [return]: the current block size. 78 | GetBlockSize, 79 | /// [return]: the input latency in samples. 80 | GetInputLatency, 81 | /// [return]: the output latency in samples. 82 | GetOutputLatency, 83 | 84 | /// Deprecated. 85 | _GetPreviousPlug, 86 | /// Deprecated. 87 | _GetNextPlug, 88 | /// Deprecated. 89 | _WillReplaceOrAccumulate, 90 | 91 | /// [return]: the current process level, see `VstProcessLevels` 92 | GetCurrentProcessLevel, 93 | /// [return]: the current automation state, see `VstAutomationStates` 94 | GetAutomationState, 95 | 96 | /// The plugin is ready to begin offline processing. 97 | /// [index]: number of new audio files. 98 | /// [value]: number of audio files. 99 | /// [ptr]: `AudioFile*` the host audio files. Flags can be updated from plugin. 100 | OfflineStart, 101 | /// Called by the plugin to read data. 102 | /// [index]: (bool) 103 | /// VST offline processing allows a plugin to overwrite existing files. If this value is 104 | /// true then the host will read the original file's samples, but if it is false it will 105 | /// read the samples which the plugin has written via `OfflineWrite` 106 | /// [value]: see `OfflineOption` 107 | /// [ptr]: `OfflineTask*` describing the task. 108 | /// [return]: 1 on success 109 | OfflineRead, 110 | /// Called by the plugin to write data. 111 | /// [value]: see `OfflineOption` 112 | /// [ptr]: `OfflineTask*` describing the task. 113 | OfflineWrite, 114 | /// Unknown. Used in offline processing. 115 | OfflineGetCurrentPass, 116 | /// Unknown. Used in offline processing. 117 | OfflineGetCurrentMetaPass, 118 | 119 | /// Deprecated. 120 | _SetOutputSampleRate, 121 | /// Deprecated. 122 | _GetOutputSpeakerArrangement, 123 | 124 | /// Get the vendor string. 125 | /// [ptr]: `char*` for vendor string, limited to `MAX_VENDOR_STR_LEN`. 126 | GetVendorString, 127 | /// Get the product string. 128 | /// [ptr]: `char*` for vendor string, limited to `MAX_PRODUCT_STR_LEN`. 129 | GetProductString, 130 | /// [return]: vendor-specific version 131 | GetVendorVersion, 132 | /// Vendor specific handling. 133 | VendorSpecific, 134 | 135 | /// Deprecated. 136 | _SetIcon, 137 | 138 | /// Check if the host supports a feature. 139 | /// [ptr]: `char*` can do string 140 | /// [return]: 1 if supported 141 | CanDo, 142 | /// Get the language of the host. 143 | /// [return]: `VstHostLanguage` 144 | GetLanguage, 145 | 146 | /// Deprecated. 147 | _OpenWindow, 148 | /// Deprecated. 149 | _CloseWindow, 150 | 151 | /// Get the current directory. 152 | /// [return]: `FSSpec` on OS X, `char*` otherwise 153 | GetDirectory, 154 | /// Tell the host that the plugin's parameters have changed, refresh the UI. 155 | /// 156 | /// No arguments. 157 | UpdateDisplay, 158 | /// Tell the host that if needed, it should record automation data for a control. 159 | /// 160 | /// Typically called when the plugin editor begins changing a control. 161 | /// 162 | /// [index]: index of the control. 163 | /// [return]: true on success. 164 | BeginEdit, 165 | /// A control is no longer being changed. 166 | /// 167 | /// Typically called after the plugin editor is done. 168 | /// 169 | /// [index]: index of the control. 170 | /// [return]: true on success. 171 | EndEdit, 172 | /// Open the host file selector. 173 | /// [ptr]: `VstFileSelect*` 174 | /// [return]: true on success. 175 | OpenFileSelector, 176 | /// Close the host file selector. 177 | /// [ptr]: `VstFileSelect*` 178 | /// [return]: true on success. 179 | CloseFileSelector, 180 | 181 | /// Deprecated. 182 | _EditFile, 183 | /// Deprecated. 184 | /// [ptr]: char[2048] or sizeof (FSSpec). 185 | /// [return]: 1 if supported. 186 | _GetChunkFile, 187 | /// Deprecated. 188 | _GetInputSpeakerArrangement, 189 | } 190 | 191 | /// Implemented by all VST hosts. 192 | #[allow(unused_variables)] 193 | pub trait Host { 194 | /// Automate a parameter; the value has been changed. 195 | fn automate(&self, index: i32, value: f32) {} 196 | 197 | /// Signal that automation of a parameter started (the knob has been touched / mouse button down). 198 | fn begin_edit(&self, index: i32) {} 199 | 200 | /// Signal that automation of a parameter ended (the knob is no longer been touched / mouse button up). 201 | fn end_edit(&self, index: i32) {} 202 | 203 | /// Get the plugin ID of the currently loading plugin. 204 | /// 205 | /// This is only useful for shell plugins where this value will change the plugin returned. 206 | /// `TODO: implement shell plugins` 207 | fn get_plugin_id(&self) -> i32 { 208 | // TODO: Handle this properly 209 | 0 210 | } 211 | 212 | /// An idle call. 213 | /// 214 | /// This is useful when the plugin is doing something such as mouse tracking in the UI. 215 | fn idle(&self) {} 216 | 217 | /// Get vendor and product information. 218 | /// 219 | /// Returns a tuple in the form of `(version, vendor_name, product_name)`. 220 | fn get_info(&self) -> (isize, String, String) { 221 | (1, "vendor string".to_owned(), "product string".to_owned()) 222 | } 223 | 224 | /// Handle incoming events from the plugin. 225 | fn process_events(&self, events: &api::Events) {} 226 | 227 | /// Get time information. 228 | fn get_time_info(&self, mask: i32) -> Option { 229 | None 230 | } 231 | 232 | /// Get block size. 233 | fn get_block_size(&self) -> isize { 234 | 0 235 | } 236 | 237 | /// Refresh UI after the plugin's parameters changed. 238 | /// 239 | /// Note: some hosts will call some `PluginParameters` methods from within the `update_display` 240 | /// call, including `get_parameter`, `get_parameter_label`, `get_parameter_name` 241 | /// and `get_parameter_text`. 242 | fn update_display(&self) {} 243 | } 244 | 245 | /// All possible errors that can occur when loading a VST plugin. 246 | #[derive(Debug)] 247 | pub enum PluginLoadError { 248 | /// Could not load given path. 249 | InvalidPath, 250 | 251 | /// Given path is not a VST plugin. 252 | NotAPlugin, 253 | 254 | /// Failed to create an instance of this plugin. 255 | /// 256 | /// This can happen for many reasons, such as if the plugin requires a different version of 257 | /// the VST API to be used, or due to improper licensing. 258 | InstanceFailed, 259 | 260 | /// The API version which the plugin used is not supported by this library. 261 | InvalidApiVersion, 262 | } 263 | 264 | impl fmt::Display for PluginLoadError { 265 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 266 | use self::PluginLoadError::*; 267 | let description = match self { 268 | InvalidPath => "Could not open the requested path", 269 | NotAPlugin => "The given path does not contain a VST2.4 compatible library", 270 | InstanceFailed => "Failed to create a plugin instance", 271 | InvalidApiVersion => "The plugin API version is not compatible with this library", 272 | }; 273 | write!(f, "{}", description) 274 | } 275 | } 276 | 277 | impl Error for PluginLoadError {} 278 | 279 | /// Wrapper for an externally loaded VST plugin. 280 | /// 281 | /// The only functionality this struct provides is loading plugins, which can be done via the 282 | /// [`load`](#method.load) method. 283 | pub struct PluginLoader { 284 | main: PluginMain, 285 | lib: Arc, 286 | host: Arc>, 287 | } 288 | 289 | /// An instance of an externally loaded VST plugin. 290 | #[allow(dead_code)] // To keep `lib` around. 291 | pub struct PluginInstance { 292 | params: Arc, 293 | lib: Arc, 294 | info: Info, 295 | is_editor_active: bool, 296 | } 297 | 298 | struct PluginParametersInstance { 299 | effect: UnsafeCell<*mut AEffect>, 300 | } 301 | 302 | unsafe impl Send for PluginParametersInstance {} 303 | unsafe impl Sync for PluginParametersInstance {} 304 | 305 | impl Drop for PluginInstance { 306 | fn drop(&mut self) { 307 | self.dispatch(plugin::OpCode::Shutdown, 0, 0, ptr::null_mut(), 0.0); 308 | } 309 | } 310 | 311 | /// The editor of an externally loaded VST plugin. 312 | struct EditorInstance { 313 | params: Arc, 314 | is_open: bool, 315 | } 316 | 317 | impl EditorInstance { 318 | fn get_rect(&self) -> Option { 319 | let mut rect: *mut Rect = std::ptr::null_mut(); 320 | let rect_ptr: *mut *mut Rect = &mut rect; 321 | 322 | let result = self 323 | .params 324 | .dispatch(plugin::OpCode::EditorGetRect, 0, 0, rect_ptr as *mut c_void, 0.0); 325 | 326 | if result == 0 || rect.is_null() { 327 | return None; 328 | } 329 | Some(unsafe { *rect }) // TODO: Who owns rect? Who should free the memory? 330 | } 331 | } 332 | 333 | impl Editor for EditorInstance { 334 | fn size(&self) -> (i32, i32) { 335 | // Assuming coordinate origins from top-left 336 | match self.get_rect() { 337 | None => (0, 0), 338 | Some(rect) => ((rect.right - rect.left) as i32, (rect.bottom - rect.top) as i32), 339 | } 340 | } 341 | 342 | fn position(&self) -> (i32, i32) { 343 | // Assuming coordinate origins from top-left 344 | match self.get_rect() { 345 | None => (0, 0), 346 | Some(rect) => (rect.left as i32, rect.top as i32), 347 | } 348 | } 349 | 350 | fn close(&mut self) { 351 | self.params 352 | .dispatch(plugin::OpCode::EditorClose, 0, 0, ptr::null_mut(), 0.0); 353 | self.is_open = false; 354 | } 355 | 356 | fn open(&mut self, parent: *mut c_void) -> bool { 357 | let result = self.params.dispatch(plugin::OpCode::EditorOpen, 0, 0, parent, 0.0); 358 | 359 | let opened = result == 1; 360 | if opened { 361 | self.is_open = true; 362 | } 363 | 364 | opened 365 | } 366 | 367 | fn is_open(&mut self) -> bool { 368 | self.is_open 369 | } 370 | } 371 | 372 | impl PluginLoader { 373 | /// Load a plugin at the given path with the given host. 374 | /// 375 | /// Because of the possibility of multi-threading problems that can occur when using plugins, 376 | /// the host must be passed in via an `Arc>` object. This makes sure that even if the 377 | /// plugins are multi-threaded no data race issues can occur. 378 | /// 379 | /// Upon success, this method returns a [`PluginLoader`](.) object which you can use to call 380 | /// [`instance`](#method.instance) to create a new instance of the plugin. 381 | /// 382 | /// # Example 383 | /// 384 | /// ```no_run 385 | /// # use std::path::Path; 386 | /// # use std::sync::{Arc, Mutex}; 387 | /// # use vst::host::{Host, PluginLoader}; 388 | /// # let path = Path::new("."); 389 | /// # struct MyHost; 390 | /// # impl MyHost { fn new() -> MyHost { MyHost } } 391 | /// # impl Host for MyHost { 392 | /// # fn automate(&self, _: i32, _: f32) {} 393 | /// # fn get_plugin_id(&self) -> i32 { 0 } 394 | /// # } 395 | /// // ... 396 | /// let host = Arc::new(Mutex::new(MyHost::new())); 397 | /// 398 | /// let mut plugin = PluginLoader::load(path, host.clone()).unwrap(); 399 | /// 400 | /// let instance = plugin.instance().unwrap(); 401 | /// // ... 402 | /// ``` 403 | /// 404 | /// # Linux/Windows 405 | /// * This should be a path to the library, typically ending in `.so`/`.dll`. 406 | /// * Possible full path: `/home/overdrivenpotato/.vst/u-he/Zebra2.64.so` 407 | /// * Possible full path: `C:\Program Files (x86)\VSTPlugins\iZotope Ozone 5.dll` 408 | /// 409 | /// # OS X 410 | /// * This should point to the mach-o file within the `.vst` bundle. 411 | /// * Plugin: `/Library/Audio/Plug-Ins/VST/iZotope Ozone 5.vst` 412 | /// * Possible full path: 413 | /// `/Library/Audio/Plug-Ins/VST/iZotope Ozone 5.vst/Contents/MacOS/PluginHooksVST` 414 | pub fn load(path: &Path, host: Arc>) -> Result, PluginLoadError> { 415 | // Try loading the library at the given path 416 | unsafe { 417 | let lib = match Library::new(path) { 418 | Ok(l) => l, 419 | Err(_) => return Err(PluginLoadError::InvalidPath), 420 | }; 421 | 422 | Ok(PluginLoader { 423 | main: 424 | // Search the library for the VSTAPI entry point 425 | match lib.get(b"VSTPluginMain") { 426 | Ok(s) => *s, 427 | _ => return Err(PluginLoadError::NotAPlugin), 428 | } 429 | , 430 | lib: Arc::new(lib), 431 | host, 432 | }) 433 | } 434 | } 435 | 436 | /// Call the VST entry point and retrieve a (possibly null) pointer. 437 | unsafe fn call_main(&mut self) -> *mut AEffect { 438 | LOAD_POINTER = Box::into_raw(Box::new(Arc::clone(&self.host))) as *mut c_void; 439 | (self.main)(callback_wrapper::) 440 | } 441 | 442 | /// Try to create an instance of this VST plugin. 443 | /// 444 | /// If the instance is successfully created, a [`PluginInstance`](struct.PluginInstance.html) 445 | /// is returned. This struct implements the [`Plugin` trait](../plugin/trait.Plugin.html). 446 | pub fn instance(&mut self) -> Result { 447 | // Call the plugin main function. This also passes the plugin main function as the closure 448 | // could not return an error if the symbol wasn't found 449 | let effect = unsafe { self.call_main() }; 450 | 451 | if effect.is_null() { 452 | return Err(PluginLoadError::InstanceFailed); 453 | } 454 | 455 | unsafe { 456 | // Move the host to the heap and add it to the `AEffect` struct for future reference 457 | (*effect).reserved1 = Box::into_raw(Box::new(Arc::clone(&self.host))) as isize; 458 | } 459 | 460 | let instance = PluginInstance::new(effect, Arc::clone(&self.lib)); 461 | 462 | let api_ver = instance.dispatch(plugin::OpCode::GetApiVersion, 0, 0, ptr::null_mut(), 0.0); 463 | if api_ver >= 2400 { 464 | Ok(instance) 465 | } else { 466 | trace!("Could not load plugin with api version {}", api_ver); 467 | Err(PluginLoadError::InvalidApiVersion) 468 | } 469 | } 470 | } 471 | 472 | impl PluginInstance { 473 | fn new(effect: *mut AEffect, lib: Arc) -> PluginInstance { 474 | use plugin::OpCode as op; 475 | 476 | let params = Arc::new(PluginParametersInstance { 477 | effect: UnsafeCell::new(effect), 478 | }); 479 | let mut plug = PluginInstance { 480 | params, 481 | lib, 482 | info: Default::default(), 483 | is_editor_active: false, 484 | }; 485 | 486 | unsafe { 487 | let effect: &AEffect = &*effect; 488 | let flags = PluginFlags::from_bits_truncate(effect.flags); 489 | 490 | plug.info = Info { 491 | name: plug.read_string(op::GetProductName, MAX_PRODUCT_STR_LEN), 492 | vendor: plug.read_string(op::GetVendorName, MAX_VENDOR_STR_LEN), 493 | 494 | presets: effect.numPrograms, 495 | parameters: effect.numParams, 496 | inputs: effect.numInputs, 497 | outputs: effect.numOutputs, 498 | 499 | midi_inputs: 0, 500 | midi_outputs: 0, 501 | 502 | unique_id: effect.uniqueId, 503 | version: effect.version, 504 | 505 | category: Category::try_from(plug.opcode(op::GetCategory)).unwrap_or(Category::Unknown), 506 | 507 | initial_delay: effect.initialDelay, 508 | 509 | preset_chunks: flags.intersects(PluginFlags::PROGRAM_CHUNKS), 510 | f64_precision: flags.intersects(PluginFlags::CAN_DOUBLE_REPLACING), 511 | silent_when_stopped: flags.intersects(PluginFlags::NO_SOUND_IN_STOP), 512 | }; 513 | } 514 | 515 | plug 516 | } 517 | } 518 | 519 | trait Dispatch { 520 | fn get_effect(&self) -> *mut AEffect; 521 | 522 | /// Send a dispatch message to the plugin. 523 | fn dispatch(&self, opcode: plugin::OpCode, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize { 524 | let dispatcher = unsafe { (*self.get_effect()).dispatcher }; 525 | if (dispatcher as *mut u8).is_null() { 526 | panic!("Plugin was not loaded correctly."); 527 | } 528 | dispatcher(self.get_effect(), opcode.into(), index, value, ptr, opt) 529 | } 530 | 531 | /// Send a lone opcode with no parameters. 532 | fn opcode(&self, opcode: plugin::OpCode) -> isize { 533 | self.dispatch(opcode, 0, 0, ptr::null_mut(), 0.0) 534 | } 535 | 536 | /// Like `dispatch`, except takes a `&str` to send via `ptr`. 537 | fn write_string(&self, opcode: plugin::OpCode, index: i32, value: isize, string: &str, opt: f32) -> isize { 538 | let string = CString::new(string).expect("Invalid string data"); 539 | self.dispatch(opcode, index, value, string.as_bytes().as_ptr() as *mut c_void, opt) 540 | } 541 | 542 | fn read_string(&self, opcode: plugin::OpCode, max: usize) -> String { 543 | self.read_string_param(opcode, 0, 0, 0.0, max) 544 | } 545 | 546 | fn read_string_param(&self, opcode: plugin::OpCode, index: i32, value: isize, opt: f32, max: usize) -> String { 547 | let mut buf = vec![0; max]; 548 | self.dispatch(opcode, index, value, buf.as_mut_ptr() as *mut c_void, opt); 549 | String::from_utf8_lossy(&buf) 550 | .chars() 551 | .take_while(|c| *c != '\0') 552 | .collect() 553 | } 554 | } 555 | 556 | impl Dispatch for PluginInstance { 557 | fn get_effect(&self) -> *mut AEffect { 558 | self.params.get_effect() 559 | } 560 | } 561 | 562 | impl Dispatch for PluginParametersInstance { 563 | fn get_effect(&self) -> *mut AEffect { 564 | unsafe { *self.effect.get() } 565 | } 566 | } 567 | 568 | impl Plugin for PluginInstance { 569 | fn get_info(&self) -> plugin::Info { 570 | self.info.clone() 571 | } 572 | 573 | fn new(_host: HostCallback) -> Self { 574 | // Plugin::new is only called on client side and PluginInstance is only used on host side 575 | unreachable!() 576 | } 577 | 578 | fn init(&mut self) { 579 | self.opcode(plugin::OpCode::Initialize); 580 | } 581 | 582 | fn set_sample_rate(&mut self, rate: f32) { 583 | self.dispatch(plugin::OpCode::SetSampleRate, 0, 0, ptr::null_mut(), rate); 584 | } 585 | 586 | fn set_block_size(&mut self, size: i64) { 587 | self.dispatch(plugin::OpCode::SetBlockSize, 0, size as isize, ptr::null_mut(), 0.0); 588 | } 589 | 590 | fn resume(&mut self) { 591 | self.dispatch(plugin::OpCode::StateChanged, 0, 1, ptr::null_mut(), 0.0); 592 | } 593 | 594 | fn suspend(&mut self) { 595 | self.dispatch(plugin::OpCode::StateChanged, 0, 0, ptr::null_mut(), 0.0); 596 | } 597 | 598 | fn vendor_specific(&mut self, index: i32, value: isize, ptr: *mut c_void, opt: f32) -> isize { 599 | self.dispatch(plugin::OpCode::VendorSpecific, index, value, ptr, opt) 600 | } 601 | 602 | fn can_do(&self, can_do: plugin::CanDo) -> Supported { 603 | let s: String = can_do.into(); 604 | Supported::from(self.write_string(plugin::OpCode::CanDo, 0, 0, &s, 0.0)) 605 | .expect("Invalid response received when querying plugin CanDo") 606 | } 607 | 608 | fn get_tail_size(&self) -> isize { 609 | self.opcode(plugin::OpCode::GetTailSize) 610 | } 611 | 612 | fn process(&mut self, buffer: &mut AudioBuffer) { 613 | if buffer.input_count() < self.info.inputs as usize { 614 | panic!("Too few inputs in AudioBuffer"); 615 | } 616 | if buffer.output_count() < self.info.outputs as usize { 617 | panic!("Too few outputs in AudioBuffer"); 618 | } 619 | unsafe { 620 | ((*self.get_effect()).processReplacing)( 621 | self.get_effect(), 622 | buffer.raw_inputs().as_ptr() as *const *const _, 623 | buffer.raw_outputs().as_mut_ptr() as *mut *mut _, 624 | buffer.samples() as i32, 625 | ) 626 | } 627 | } 628 | 629 | fn process_f64(&mut self, buffer: &mut AudioBuffer) { 630 | if buffer.input_count() < self.info.inputs as usize { 631 | panic!("Too few inputs in AudioBuffer"); 632 | } 633 | if buffer.output_count() < self.info.outputs as usize { 634 | panic!("Too few outputs in AudioBuffer"); 635 | } 636 | unsafe { 637 | ((*self.get_effect()).processReplacingF64)( 638 | self.get_effect(), 639 | buffer.raw_inputs().as_ptr() as *const *const _, 640 | buffer.raw_outputs().as_mut_ptr() as *mut *mut _, 641 | buffer.samples() as i32, 642 | ) 643 | } 644 | } 645 | 646 | fn process_events(&mut self, events: &api::Events) { 647 | self.dispatch(plugin::OpCode::ProcessEvents, 0, 0, events as *const _ as *mut _, 0.0); 648 | } 649 | 650 | fn get_input_info(&self, input: i32) -> ChannelInfo { 651 | let mut props: MaybeUninit = MaybeUninit::uninit(); 652 | let ptr = props.as_mut_ptr() as *mut c_void; 653 | 654 | self.dispatch(plugin::OpCode::GetInputInfo, input, 0, ptr, 0.0); 655 | 656 | ChannelInfo::from(unsafe { props.assume_init() }) 657 | } 658 | 659 | fn get_output_info(&self, output: i32) -> ChannelInfo { 660 | let mut props: MaybeUninit = MaybeUninit::uninit(); 661 | let ptr = props.as_mut_ptr() as *mut c_void; 662 | 663 | self.dispatch(plugin::OpCode::GetOutputInfo, output, 0, ptr, 0.0); 664 | 665 | ChannelInfo::from(unsafe { props.assume_init() }) 666 | } 667 | 668 | fn get_parameter_object(&mut self) -> Arc { 669 | Arc::clone(&self.params) as Arc 670 | } 671 | 672 | fn get_editor(&mut self) -> Option> { 673 | if self.is_editor_active { 674 | // An editor is already active, the caller should be using the active editor instead of 675 | // requesting for a new one. 676 | return None; 677 | } 678 | 679 | self.is_editor_active = true; 680 | Some(Box::new(EditorInstance { 681 | params: self.params.clone(), 682 | is_open: false, 683 | })) 684 | } 685 | } 686 | 687 | impl PluginParameters for PluginParametersInstance { 688 | fn change_preset(&self, preset: i32) { 689 | self.dispatch(plugin::OpCode::ChangePreset, 0, preset as isize, ptr::null_mut(), 0.0); 690 | } 691 | 692 | fn get_preset_num(&self) -> i32 { 693 | self.opcode(plugin::OpCode::GetCurrentPresetNum) as i32 694 | } 695 | 696 | fn set_preset_name(&self, name: String) { 697 | self.write_string(plugin::OpCode::SetCurrentPresetName, 0, 0, &name, 0.0); 698 | } 699 | 700 | fn get_preset_name(&self, preset: i32) -> String { 701 | self.read_string_param(plugin::OpCode::GetPresetName, preset, 0, 0.0, MAX_PRESET_NAME_LEN) 702 | } 703 | 704 | fn get_parameter_label(&self, index: i32) -> String { 705 | self.read_string_param(plugin::OpCode::GetParameterLabel, index, 0, 0.0, MAX_PARAM_STR_LEN) 706 | } 707 | 708 | fn get_parameter_text(&self, index: i32) -> String { 709 | self.read_string_param(plugin::OpCode::GetParameterDisplay, index, 0, 0.0, MAX_PARAM_STR_LEN) 710 | } 711 | 712 | fn get_parameter_name(&self, index: i32) -> String { 713 | self.read_string_param(plugin::OpCode::GetParameterName, index, 0, 0.0, MAX_PARAM_STR_LEN) 714 | } 715 | 716 | fn get_parameter(&self, index: i32) -> f32 { 717 | unsafe { ((*self.get_effect()).getParameter)(self.get_effect(), index) } 718 | } 719 | 720 | fn set_parameter(&self, index: i32, value: f32) { 721 | unsafe { ((*self.get_effect()).setParameter)(self.get_effect(), index, value) } 722 | } 723 | 724 | fn can_be_automated(&self, index: i32) -> bool { 725 | self.dispatch(plugin::OpCode::CanBeAutomated, index, 0, ptr::null_mut(), 0.0) > 0 726 | } 727 | 728 | fn string_to_parameter(&self, index: i32, text: String) -> bool { 729 | self.write_string(plugin::OpCode::StringToParameter, index, 0, &text, 0.0) > 0 730 | } 731 | 732 | // TODO: Editor 733 | 734 | fn get_preset_data(&self) -> Vec { 735 | // Create a pointer that can be updated from the plugin. 736 | let mut ptr: *mut u8 = ptr::null_mut(); 737 | let len = self.dispatch( 738 | plugin::OpCode::GetData, 739 | 1, /*preset*/ 740 | 0, 741 | &mut ptr as *mut *mut u8 as *mut c_void, 742 | 0.0, 743 | ); 744 | let slice = unsafe { slice::from_raw_parts(ptr, len as usize) }; 745 | slice.to_vec() 746 | } 747 | 748 | fn get_bank_data(&self) -> Vec { 749 | // Create a pointer that can be updated from the plugin. 750 | let mut ptr: *mut u8 = ptr::null_mut(); 751 | let len = self.dispatch( 752 | plugin::OpCode::GetData, 753 | 0, /*bank*/ 754 | 0, 755 | &mut ptr as *mut *mut u8 as *mut c_void, 756 | 0.0, 757 | ); 758 | let slice = unsafe { slice::from_raw_parts(ptr, len as usize) }; 759 | slice.to_vec() 760 | } 761 | 762 | fn load_preset_data(&self, data: &[u8]) { 763 | self.dispatch( 764 | plugin::OpCode::SetData, 765 | 1, 766 | data.len() as isize, 767 | data.as_ptr() as *mut c_void, 768 | 0.0, 769 | ); 770 | } 771 | 772 | fn load_bank_data(&self, data: &[u8]) { 773 | self.dispatch( 774 | plugin::OpCode::SetData, 775 | 0, 776 | data.len() as isize, 777 | data.as_ptr() as *mut c_void, 778 | 0.0, 779 | ); 780 | } 781 | } 782 | 783 | /// Used for constructing `AudioBuffer` instances on the host. 784 | /// 785 | /// This struct contains all necessary allocations for an `AudioBuffer` apart 786 | /// from the actual sample arrays. This way, the inner processing loop can 787 | /// be allocation free even if `AudioBuffer` instances are repeatedly created. 788 | /// 789 | /// ```rust 790 | /// # use vst::host::HostBuffer; 791 | /// # use vst::plugin::Plugin; 792 | /// # fn test(plugin: &mut P) { 793 | /// let mut host_buffer: HostBuffer = HostBuffer::new(2, 2); 794 | /// let inputs = vec![vec![0.0; 1000]; 2]; 795 | /// let mut outputs = vec![vec![0.0; 1000]; 2]; 796 | /// let mut audio_buffer = host_buffer.bind(&inputs, &mut outputs); 797 | /// plugin.process(&mut audio_buffer); 798 | /// # } 799 | /// ``` 800 | pub struct HostBuffer { 801 | inputs: Vec<*const T>, 802 | outputs: Vec<*mut T>, 803 | } 804 | 805 | impl HostBuffer { 806 | /// Create a `HostBuffer` for a given number of input and output channels. 807 | pub fn new(input_count: usize, output_count: usize) -> HostBuffer { 808 | HostBuffer { 809 | inputs: vec![ptr::null(); input_count], 810 | outputs: vec![ptr::null_mut(); output_count], 811 | } 812 | } 813 | 814 | /// Create a `HostBuffer` for the number of input and output channels 815 | /// specified in an `Info` struct. 816 | pub fn from_info(info: &Info) -> HostBuffer { 817 | HostBuffer::new(info.inputs as usize, info.outputs as usize) 818 | } 819 | 820 | /// Bind sample arrays to the `HostBuffer` to create an `AudioBuffer` to pass to a plugin. 821 | /// 822 | /// # Panics 823 | /// This function will panic if more inputs or outputs are supplied than the `HostBuffer` 824 | /// was created for, or if the sample arrays do not all have the same length. 825 | pub fn bind<'a, I, O>(&'a mut self, input_arrays: &[I], output_arrays: &mut [O]) -> AudioBuffer<'a, T> 826 | where 827 | I: AsRef<[T]> + 'a, 828 | O: AsMut<[T]> + 'a, 829 | { 830 | // Check that number of desired inputs and outputs fit in allocation 831 | if input_arrays.len() > self.inputs.len() { 832 | panic!("Too many inputs for HostBuffer"); 833 | } 834 | if output_arrays.len() > self.outputs.len() { 835 | panic!("Too many outputs for HostBuffer"); 836 | } 837 | 838 | // Initialize raw pointers and find common length 839 | let mut length = None; 840 | for (i, input) in input_arrays.iter().map(|r| r.as_ref()).enumerate() { 841 | self.inputs[i] = input.as_ptr(); 842 | match length { 843 | None => length = Some(input.len()), 844 | Some(old_length) => { 845 | if input.len() != old_length { 846 | panic!("Mismatching lengths of input arrays"); 847 | } 848 | } 849 | } 850 | } 851 | for (i, output) in output_arrays.iter_mut().map(|r| r.as_mut()).enumerate() { 852 | self.outputs[i] = output.as_mut_ptr(); 853 | match length { 854 | None => length = Some(output.len()), 855 | Some(old_length) => { 856 | if output.len() != old_length { 857 | panic!("Mismatching lengths of output arrays"); 858 | } 859 | } 860 | } 861 | } 862 | let length = length.unwrap_or(0); 863 | 864 | // Construct AudioBuffer 865 | unsafe { 866 | AudioBuffer::from_raw( 867 | input_arrays.len(), 868 | output_arrays.len(), 869 | self.inputs.as_ptr(), 870 | self.outputs.as_mut_ptr(), 871 | length, 872 | ) 873 | } 874 | } 875 | 876 | /// Number of input channels supported by this `HostBuffer`. 877 | pub fn input_count(&self) -> usize { 878 | self.inputs.len() 879 | } 880 | 881 | /// Number of output channels supported by this `HostBuffer`. 882 | pub fn output_count(&self) -> usize { 883 | self.outputs.len() 884 | } 885 | } 886 | 887 | /// HACK: a pointer to store the host so that it can be accessed from the `callback_wrapper` 888 | /// function passed to the plugin. 889 | /// 890 | /// When the plugin is being loaded, a `Box>>` is transmuted to a `*mut c_void` pointer 891 | /// and placed here. When the plugin calls the callback during initialization, the host refers to 892 | /// this pointer to get a handle to the Host. After initialization, this pointer is invalidated and 893 | /// the host pointer is placed into a [reserved field] in the instance `AEffect` struct. 894 | /// 895 | /// The issue with this approach is that if 2 plugins are simultaneously loaded with 2 different 896 | /// host instances, this might fail as one host may receive a pointer to the other one. In practice 897 | /// this is a rare situation as you normally won't have 2 separate host instances loading at once. 898 | /// 899 | /// [reserved field]: ../api/struct.AEffect.html#structfield.reserved1 900 | static mut LOAD_POINTER: *mut c_void = 0 as *mut c_void; 901 | 902 | /// Function passed to plugin to handle dispatching host opcodes. 903 | extern "C" fn callback_wrapper( 904 | effect: *mut AEffect, 905 | opcode: i32, 906 | index: i32, 907 | value: isize, 908 | ptr: *mut c_void, 909 | opt: f32, 910 | ) -> isize { 911 | unsafe { 912 | // If the effect pointer is not null and the host pointer is not null, the plugin has 913 | // already been initialized 914 | if !effect.is_null() && (*effect).reserved1 != 0 { 915 | let reserved = (*effect).reserved1 as *const Arc>; 916 | let host = &*reserved; 917 | 918 | let host = &mut *host.lock().unwrap(); 919 | 920 | interfaces::host_dispatch(host, effect, opcode, index, value, ptr, opt) 921 | // In this case, the plugin is still undergoing initialization and so `LOAD_POINTER` is 922 | // dereferenced 923 | } else { 924 | // Used only during the plugin initialization 925 | let host = LOAD_POINTER as *const Arc>; 926 | let host = &*host; 927 | let host = &mut *host.lock().unwrap(); 928 | 929 | interfaces::host_dispatch(host, effect, opcode, index, value, ptr, opt) 930 | } 931 | } 932 | } 933 | 934 | #[cfg(test)] 935 | mod tests { 936 | use crate::host::HostBuffer; 937 | 938 | #[test] 939 | fn host_buffer() { 940 | const LENGTH: usize = 1_000_000; 941 | let mut host_buffer: HostBuffer = HostBuffer::new(2, 2); 942 | let input_left = vec![1.0; LENGTH]; 943 | let input_right = vec![1.0; LENGTH]; 944 | let mut output_left = vec![0.0; LENGTH]; 945 | let mut output_right = vec![0.0; LENGTH]; 946 | { 947 | let mut audio_buffer = { 948 | // Slices given to `bind` need not persist, but the sample arrays do. 949 | let inputs = [&input_left, &input_right]; 950 | let mut outputs = [&mut output_left, &mut output_right]; 951 | host_buffer.bind(&inputs, &mut outputs) 952 | }; 953 | for (input, output) in audio_buffer.zip() { 954 | for (i, o) in input.iter().zip(output) { 955 | *o = *i * 2.0; 956 | } 957 | } 958 | } 959 | assert_eq!(output_left, vec![2.0; LENGTH]); 960 | assert_eq!(output_right, vec![2.0; LENGTH]); 961 | } 962 | } 963 | -------------------------------------------------------------------------------- /src/interfaces.rs: -------------------------------------------------------------------------------- 1 | //! Function interfaces for VST 2.4 API. 2 | 3 | #![doc(hidden)] 4 | 5 | use std::cell::Cell; 6 | use std::os::raw::{c_char, c_void}; 7 | use std::{mem, slice}; 8 | 9 | use crate::{ 10 | api::{self, consts::*, AEffect, TimeInfo}, 11 | buffer::AudioBuffer, 12 | editor::{Key, KeyCode, KnobMode, Rect}, 13 | host::Host, 14 | }; 15 | 16 | /// Deprecated process function. 17 | pub extern "C" fn process_deprecated( 18 | _effect: *mut AEffect, 19 | _raw_inputs: *const *const f32, 20 | _raw_outputs: *mut *mut f32, 21 | _samples: i32, 22 | ) { 23 | } 24 | 25 | /// VST2.4 replacing function. 26 | pub extern "C" fn process_replacing( 27 | effect: *mut AEffect, 28 | raw_inputs: *const *const f32, 29 | raw_outputs: *mut *mut f32, 30 | samples: i32, 31 | ) { 32 | // Handle to the VST 33 | let plugin = unsafe { (*effect).get_plugin() }; 34 | let info = unsafe { (*effect).get_info() }; 35 | let (input_count, output_count) = (info.inputs as usize, info.outputs as usize); 36 | let mut buffer = 37 | unsafe { AudioBuffer::from_raw(input_count, output_count, raw_inputs, raw_outputs, samples as usize) }; 38 | plugin.process(&mut buffer); 39 | } 40 | 41 | /// VST2.4 replacing function with `f64` values. 42 | pub extern "C" fn process_replacing_f64( 43 | effect: *mut AEffect, 44 | raw_inputs: *const *const f64, 45 | raw_outputs: *mut *mut f64, 46 | samples: i32, 47 | ) { 48 | let plugin = unsafe { (*effect).get_plugin() }; 49 | let info = unsafe { (*effect).get_info() }; 50 | let (input_count, output_count) = (info.inputs as usize, info.outputs as usize); 51 | let mut buffer = 52 | unsafe { AudioBuffer::from_raw(input_count, output_count, raw_inputs, raw_outputs, samples as usize) }; 53 | plugin.process_f64(&mut buffer); 54 | } 55 | 56 | /// VST2.4 set parameter function. 57 | pub extern "C" fn set_parameter(effect: *mut AEffect, index: i32, value: f32) { 58 | unsafe { (*effect).get_params() }.set_parameter(index, value); 59 | } 60 | 61 | /// VST2.4 get parameter function. 62 | pub extern "C" fn get_parameter(effect: *mut AEffect, index: i32) -> f32 { 63 | unsafe { (*effect).get_params() }.get_parameter(index) 64 | } 65 | 66 | /// Copy a string into a destination buffer. 67 | /// 68 | /// String will be cut at `max` characters. 69 | fn copy_string(dst: *mut c_void, src: &str, max: usize) -> isize { 70 | unsafe { 71 | use libc::{memcpy, memset}; 72 | use std::cmp::min; 73 | 74 | let dst = dst as *mut c_void; 75 | memset(dst, 0, max); 76 | memcpy(dst, src.as_ptr() as *const c_void, min(max, src.as_bytes().len())); 77 | } 78 | 79 | 1 // Success 80 | } 81 | 82 | /// VST2.4 dispatch function. This function handles dispatching all opcodes to the VST plugin. 83 | pub extern "C" fn dispatch( 84 | effect: *mut AEffect, 85 | opcode: i32, 86 | index: i32, 87 | value: isize, 88 | ptr: *mut c_void, 89 | opt: f32, 90 | ) -> isize { 91 | use crate::plugin::{CanDo, OpCode}; 92 | 93 | // Convert passed in opcode to enum 94 | let opcode = OpCode::try_from(opcode); 95 | // Only query plugin or editor when needed to avoid creating multiple 96 | // concurrent mutable references to the same object. 97 | let get_plugin = || unsafe { (*effect).get_plugin() }; 98 | let get_editor = || unsafe { (*effect).get_editor() }; 99 | let params = unsafe { (*effect).get_params() }; 100 | 101 | match opcode { 102 | Ok(OpCode::Initialize) => get_plugin().init(), 103 | Ok(OpCode::Shutdown) => unsafe { 104 | (*effect).drop_plugin(); 105 | drop(Box::from_raw(effect)) 106 | }, 107 | 108 | Ok(OpCode::ChangePreset) => params.change_preset(value as i32), 109 | Ok(OpCode::GetCurrentPresetNum) => return params.get_preset_num() as isize, 110 | Ok(OpCode::SetCurrentPresetName) => params.set_preset_name(read_string(ptr)), 111 | Ok(OpCode::GetCurrentPresetName) => { 112 | let num = params.get_preset_num(); 113 | return copy_string(ptr, ¶ms.get_preset_name(num), MAX_PRESET_NAME_LEN); 114 | } 115 | 116 | Ok(OpCode::GetParameterLabel) => { 117 | return copy_string(ptr, ¶ms.get_parameter_label(index), MAX_PARAM_STR_LEN) 118 | } 119 | Ok(OpCode::GetParameterDisplay) => { 120 | return copy_string(ptr, ¶ms.get_parameter_text(index), MAX_PARAM_STR_LEN) 121 | } 122 | Ok(OpCode::GetParameterName) => return copy_string(ptr, ¶ms.get_parameter_name(index), MAX_PARAM_STR_LEN), 123 | 124 | Ok(OpCode::SetSampleRate) => get_plugin().set_sample_rate(opt), 125 | Ok(OpCode::SetBlockSize) => get_plugin().set_block_size(value as i64), 126 | Ok(OpCode::StateChanged) => { 127 | if value == 1 { 128 | get_plugin().resume(); 129 | } else { 130 | get_plugin().suspend(); 131 | } 132 | } 133 | 134 | Ok(OpCode::EditorGetRect) => { 135 | if let Some(ref mut editor) = get_editor() { 136 | let size = editor.size(); 137 | let pos = editor.position(); 138 | 139 | unsafe { 140 | // Given a Rect** structure 141 | // TODO: Investigate whether we are given a valid Rect** pointer already 142 | *(ptr as *mut *mut c_void) = Box::into_raw(Box::new(Rect { 143 | left: pos.0 as i16, // x coord of position 144 | top: pos.1 as i16, // y coord of position 145 | right: (pos.0 + size.0) as i16, // x coord of pos + x coord of size 146 | bottom: (pos.1 + size.1) as i16, // y coord of pos + y coord of size 147 | })) as *mut _; // TODO: free memory 148 | } 149 | 150 | return 1; 151 | } 152 | } 153 | Ok(OpCode::EditorOpen) => { 154 | if let Some(ref mut editor) = get_editor() { 155 | // `ptr` is a window handle to the parent window. 156 | // See the documentation for `Editor::open` for details. 157 | if editor.open(ptr) { 158 | return 1; 159 | } 160 | } 161 | } 162 | Ok(OpCode::EditorClose) => { 163 | if let Some(ref mut editor) = get_editor() { 164 | editor.close(); 165 | } 166 | } 167 | 168 | Ok(OpCode::EditorIdle) => { 169 | if let Some(ref mut editor) = get_editor() { 170 | editor.idle(); 171 | } 172 | } 173 | 174 | Ok(OpCode::GetData) => { 175 | let mut chunks = if index == 0 { 176 | params.get_bank_data() 177 | } else { 178 | params.get_preset_data() 179 | }; 180 | 181 | chunks.shrink_to_fit(); 182 | let len = chunks.len() as isize; // eventually we should be using ffi::size_t 183 | 184 | unsafe { 185 | *(ptr as *mut *mut c_void) = chunks.as_ptr() as *mut c_void; 186 | } 187 | 188 | mem::forget(chunks); 189 | return len; 190 | } 191 | Ok(OpCode::SetData) => { 192 | let chunks = unsafe { slice::from_raw_parts(ptr as *mut u8, value as usize) }; 193 | 194 | if index == 0 { 195 | params.load_bank_data(chunks); 196 | } else { 197 | params.load_preset_data(chunks); 198 | } 199 | } 200 | 201 | Ok(OpCode::ProcessEvents) => { 202 | get_plugin().process_events(unsafe { &*(ptr as *const api::Events) }); 203 | } 204 | Ok(OpCode::CanBeAutomated) => return params.can_be_automated(index) as isize, 205 | Ok(OpCode::StringToParameter) => return params.string_to_parameter(index, read_string(ptr)) as isize, 206 | 207 | Ok(OpCode::GetPresetName) => return copy_string(ptr, ¶ms.get_preset_name(index), MAX_PRESET_NAME_LEN), 208 | 209 | Ok(OpCode::GetInputInfo) => { 210 | if index >= 0 && index < get_plugin().get_info().inputs { 211 | unsafe { 212 | let ptr = ptr as *mut api::ChannelProperties; 213 | *ptr = get_plugin().get_input_info(index).into(); 214 | } 215 | } 216 | } 217 | Ok(OpCode::GetOutputInfo) => { 218 | if index >= 0 && index < get_plugin().get_info().outputs { 219 | unsafe { 220 | let ptr = ptr as *mut api::ChannelProperties; 221 | *ptr = get_plugin().get_output_info(index).into(); 222 | } 223 | } 224 | } 225 | Ok(OpCode::GetCategory) => { 226 | return get_plugin().get_info().category.into(); 227 | } 228 | 229 | Ok(OpCode::GetEffectName) => return copy_string(ptr, &get_plugin().get_info().name, MAX_VENDOR_STR_LEN), 230 | 231 | Ok(OpCode::GetVendorName) => return copy_string(ptr, &get_plugin().get_info().vendor, MAX_VENDOR_STR_LEN), 232 | Ok(OpCode::GetProductName) => return copy_string(ptr, &get_plugin().get_info().name, MAX_PRODUCT_STR_LEN), 233 | Ok(OpCode::GetVendorVersion) => return get_plugin().get_info().version as isize, 234 | Ok(OpCode::VendorSpecific) => return get_plugin().vendor_specific(index, value, ptr, opt), 235 | Ok(OpCode::CanDo) => { 236 | let can_do = CanDo::from_str(&read_string(ptr)); 237 | return get_plugin().can_do(can_do).into(); 238 | } 239 | Ok(OpCode::GetTailSize) => { 240 | if get_plugin().get_tail_size() == 0 { 241 | return 1; 242 | } else { 243 | return get_plugin().get_tail_size(); 244 | } 245 | } 246 | 247 | //OpCode::GetParamInfo => { /*TODO*/ } 248 | Ok(OpCode::GetApiVersion) => return 2400, 249 | 250 | Ok(OpCode::EditorKeyDown) => { 251 | if let Some(ref mut editor) = get_editor() { 252 | if let Ok(key) = Key::try_from(value) { 253 | editor.key_down(KeyCode { 254 | character: index as u8 as char, 255 | key, 256 | modifier: opt.to_bits() as u8, 257 | }); 258 | } 259 | } 260 | } 261 | Ok(OpCode::EditorKeyUp) => { 262 | if let Some(ref mut editor) = get_editor() { 263 | if let Ok(key) = Key::try_from(value) { 264 | editor.key_up(KeyCode { 265 | character: index as u8 as char, 266 | key, 267 | modifier: opt.to_bits() as u8, 268 | }); 269 | } 270 | } 271 | } 272 | Ok(OpCode::EditorSetKnobMode) => { 273 | if let Some(ref mut editor) = get_editor() { 274 | if let Ok(knob_mode) = KnobMode::try_from(value) { 275 | editor.set_knob_mode(knob_mode); 276 | } 277 | } 278 | } 279 | 280 | Ok(OpCode::StartProcess) => get_plugin().start_process(), 281 | Ok(OpCode::StopProcess) => get_plugin().stop_process(), 282 | 283 | Ok(OpCode::GetNumMidiInputs) => return unsafe { (*effect).get_info() }.midi_inputs as isize, 284 | Ok(OpCode::GetNumMidiOutputs) => return unsafe { (*effect).get_info() }.midi_outputs as isize, 285 | 286 | _ => { 287 | debug!("Unimplemented opcode ({:?})", opcode); 288 | trace!( 289 | "Arguments; index: {}, value: {}, ptr: {:?}, opt: {}", 290 | index, 291 | value, 292 | ptr, 293 | opt 294 | ); 295 | } 296 | } 297 | 298 | 0 299 | } 300 | 301 | pub fn host_dispatch( 302 | host: &mut dyn Host, 303 | effect: *mut AEffect, 304 | opcode: i32, 305 | index: i32, 306 | value: isize, 307 | ptr: *mut c_void, 308 | opt: f32, 309 | ) -> isize { 310 | use crate::host::OpCode; 311 | 312 | let opcode = OpCode::try_from(opcode); 313 | match opcode { 314 | Ok(OpCode::Version) => return 2400, 315 | Ok(OpCode::Automate) => host.automate(index, opt), 316 | Ok(OpCode::BeginEdit) => host.begin_edit(index), 317 | Ok(OpCode::EndEdit) => host.end_edit(index), 318 | 319 | Ok(OpCode::Idle) => host.idle(), 320 | 321 | // ... 322 | Ok(OpCode::CanDo) => { 323 | info!("Plugin is asking if host can: {}.", read_string(ptr)); 324 | } 325 | 326 | Ok(OpCode::GetVendorVersion) => return host.get_info().0, 327 | Ok(OpCode::GetVendorString) => return copy_string(ptr, &host.get_info().1, MAX_VENDOR_STR_LEN), 328 | Ok(OpCode::GetProductString) => return copy_string(ptr, &host.get_info().2, MAX_PRODUCT_STR_LEN), 329 | Ok(OpCode::ProcessEvents) => { 330 | host.process_events(unsafe { &*(ptr as *const api::Events) }); 331 | } 332 | 333 | Ok(OpCode::GetTime) => { 334 | return match host.get_time_info(value as i32) { 335 | None => 0, 336 | Some(result) => { 337 | thread_local! { 338 | static TIME_INFO: Cell = 339 | Cell::new(TimeInfo::default()); 340 | } 341 | TIME_INFO.with(|time_info| { 342 | (*time_info).set(result); 343 | time_info.as_ptr() as isize 344 | }) 345 | } 346 | }; 347 | } 348 | Ok(OpCode::GetBlockSize) => return host.get_block_size(), 349 | 350 | _ => { 351 | trace!("VST: Got unimplemented host opcode ({:?})", opcode); 352 | trace!( 353 | "Arguments; effect: {:?}, index: {}, value: {}, ptr: {:?}, opt: {}", 354 | effect, 355 | index, 356 | value, 357 | ptr, 358 | opt 359 | ); 360 | } 361 | } 362 | 0 363 | } 364 | 365 | // Read a string from the `ptr` buffer 366 | fn read_string(ptr: *mut c_void) -> String { 367 | use std::ffi::CStr; 368 | 369 | String::from_utf8_lossy(unsafe { CStr::from_ptr(ptr as *mut c_char).to_bytes() }).into_owned() 370 | } 371 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs)] 2 | 3 | //! A rust implementation of the VST2.4 API. 4 | //! 5 | //! The VST API is multi-threaded. A VST host calls into a plugin generally from two threads - 6 | //! the *processing* thread and the *UI* thread. The organization of this crate reflects this 7 | //! structure to ensure that the threading assumptions of Safe Rust are fulfilled and data 8 | //! races are avoided. 9 | //! 10 | //! # Plugins 11 | //! All Plugins must implement the `Plugin` trait and `std::default::Default`. 12 | //! The `plugin_main!` macro must also be called in order to export the necessary functions 13 | //! for the plugin to function. 14 | //! 15 | //! ## `Plugin` Trait 16 | //! All methods in this trait have a default implementation except for the `get_info` method which 17 | //! must be implemented by the plugin. Any of the default implementations may be overridden for 18 | //! custom functionality; the defaults do nothing on their own. 19 | //! 20 | //! ## `PluginParameters` Trait 21 | //! The methods in this trait handle access to plugin parameters. Since the host may call these 22 | //! methods concurrently with audio processing, it needs to be separate from the main `Plugin` 23 | //! trait. 24 | //! 25 | //! To support parameters, a plugin must provide an implementation of the `PluginParameters` 26 | //! trait, wrap it in an `Arc` (so it can be accessed from both threads) and 27 | //! return a reference to it from the `get_parameter_object` method in the `Plugin`. 28 | //! 29 | //! ## `plugin_main!` macro 30 | //! `plugin_main!` will export the necessary functions to create a proper VST plugin. This must be 31 | //! called with your VST plugin struct name in order for the vst to work. 32 | //! 33 | //! ## Example plugin 34 | //! A barebones VST plugin: 35 | //! 36 | //! ```no_run 37 | //! #[macro_use] 38 | //! extern crate vst; 39 | //! 40 | //! use vst::plugin::{HostCallback, Info, Plugin}; 41 | //! 42 | //! struct BasicPlugin; 43 | //! 44 | //! impl Plugin for BasicPlugin { 45 | //! fn new(_host: HostCallback) -> Self { 46 | //! BasicPlugin 47 | //! } 48 | //! 49 | //! fn get_info(&self) -> Info { 50 | //! Info { 51 | //! name: "Basic Plugin".to_string(), 52 | //! unique_id: 1357, // Used by hosts to differentiate between plugins. 53 | //! 54 | //! ..Default::default() 55 | //! } 56 | //! } 57 | //! } 58 | //! 59 | //! plugin_main!(BasicPlugin); // Important! 60 | //! # fn main() {} // For `extern crate vst` 61 | //! ``` 62 | //! 63 | //! # Hosts 64 | //! 65 | //! ## `Host` Trait 66 | //! All hosts must implement the [`Host` trait](host/trait.Host.html). To load a VST plugin, you 67 | //! need to wrap your host in an `Arc>` wrapper for thread safety reasons. Along with the 68 | //! plugin path, this can be passed to the [`PluginLoader::load`] method to create a plugin loader 69 | //! which can spawn plugin instances. 70 | //! 71 | //! ## Example Host 72 | //! ```no_run 73 | //! extern crate vst; 74 | //! 75 | //! use std::sync::{Arc, Mutex}; 76 | //! use std::path::Path; 77 | //! 78 | //! use vst::host::{Host, PluginLoader}; 79 | //! use vst::plugin::Plugin; 80 | //! 81 | //! struct SampleHost; 82 | //! 83 | //! impl Host for SampleHost { 84 | //! fn automate(&self, index: i32, value: f32) { 85 | //! println!("Parameter {} had its value changed to {}", index, value); 86 | //! } 87 | //! } 88 | //! 89 | //! fn main() { 90 | //! let host = Arc::new(Mutex::new(SampleHost)); 91 | //! let path = Path::new("/path/to/vst"); 92 | //! 93 | //! let mut loader = PluginLoader::load(path, host.clone()).unwrap(); 94 | //! let mut instance = loader.instance().unwrap(); 95 | //! 96 | //! println!("Loaded {}", instance.get_info().name); 97 | //! 98 | //! instance.init(); 99 | //! println!("Initialized instance!"); 100 | //! 101 | //! println!("Closing instance..."); 102 | //! // Not necessary as the instance is shut down when it goes out of scope anyway. 103 | //! // drop(instance); 104 | //! } 105 | //! 106 | //! ``` 107 | //! 108 | //! [`PluginLoader::load`]: host/struct.PluginLoader.html#method.load 109 | //! 110 | 111 | extern crate libc; 112 | extern crate libloading; 113 | extern crate num_enum; 114 | extern crate num_traits; 115 | #[macro_use] 116 | extern crate log; 117 | #[macro_use] 118 | extern crate bitflags; 119 | 120 | use std::ptr; 121 | 122 | pub mod api; 123 | pub mod buffer; 124 | mod cache; 125 | pub mod channels; 126 | pub mod editor; 127 | pub mod event; 128 | pub mod host; 129 | mod interfaces; 130 | pub mod plugin; 131 | pub mod prelude; 132 | pub mod util; 133 | 134 | use api::consts::VST_MAGIC; 135 | use api::{AEffect, HostCallbackProc}; 136 | use cache::PluginCache; 137 | use plugin::{HostCallback, Plugin}; 138 | 139 | /// Exports the necessary symbols for the plugin to be used by a VST host. 140 | /// 141 | /// This macro takes a type which must implement the `Plugin` trait. 142 | #[macro_export] 143 | macro_rules! plugin_main { 144 | ($t:ty) => { 145 | #[cfg(target_os = "macos")] 146 | #[no_mangle] 147 | pub extern "system" fn main_macho(callback: $crate::api::HostCallbackProc) -> *mut $crate::api::AEffect { 148 | VSTPluginMain(callback) 149 | } 150 | 151 | #[cfg(target_os = "windows")] 152 | #[allow(non_snake_case)] 153 | #[no_mangle] 154 | pub extern "system" fn MAIN(callback: $crate::api::HostCallbackProc) -> *mut $crate::api::AEffect { 155 | VSTPluginMain(callback) 156 | } 157 | 158 | #[allow(non_snake_case)] 159 | #[no_mangle] 160 | pub extern "C" fn VSTPluginMain(callback: $crate::api::HostCallbackProc) -> *mut $crate::api::AEffect { 161 | $crate::main::<$t>(callback) 162 | } 163 | }; 164 | } 165 | 166 | /// Initializes a VST plugin and returns a raw pointer to an AEffect struct. 167 | #[doc(hidden)] 168 | pub fn main(callback: HostCallbackProc) -> *mut AEffect { 169 | // Initialize as much of the AEffect as we can before creating the plugin. 170 | // In particular, initialize all the function pointers, since initializing 171 | // these to zero is undefined behavior. 172 | let boxed_effect = Box::new(AEffect { 173 | magic: VST_MAGIC, 174 | dispatcher: interfaces::dispatch, // fn pointer 175 | 176 | _process: interfaces::process_deprecated, // fn pointer 177 | 178 | setParameter: interfaces::set_parameter, // fn pointer 179 | getParameter: interfaces::get_parameter, // fn pointer 180 | 181 | numPrograms: 0, // To be updated with plugin specific value. 182 | numParams: 0, // To be updated with plugin specific value. 183 | numInputs: 0, // To be updated with plugin specific value. 184 | numOutputs: 0, // To be updated with plugin specific value. 185 | 186 | flags: 0, // To be updated with plugin specific value. 187 | 188 | reserved1: 0, 189 | reserved2: 0, 190 | 191 | initialDelay: 0, // To be updated with plugin specific value. 192 | 193 | _realQualities: 0, 194 | _offQualities: 0, 195 | _ioRatio: 0.0, 196 | 197 | object: ptr::null_mut(), 198 | user: ptr::null_mut(), 199 | 200 | uniqueId: 0, // To be updated with plugin specific value. 201 | version: 0, // To be updated with plugin specific value. 202 | 203 | processReplacing: interfaces::process_replacing, // fn pointer 204 | processReplacingF64: interfaces::process_replacing_f64, //fn pointer 205 | 206 | future: [0u8; 56], 207 | }); 208 | let raw_effect = Box::into_raw(boxed_effect); 209 | 210 | let host = HostCallback::wrap(callback, raw_effect); 211 | if host.vst_version() == 0 { 212 | // TODO: Better criteria would probably be useful here... 213 | return ptr::null_mut(); 214 | } 215 | 216 | trace!("Creating VST plugin instance..."); 217 | let mut plugin = T::new(host); 218 | let info = plugin.get_info(); 219 | let params = plugin.get_parameter_object(); 220 | let editor = plugin.get_editor(); 221 | 222 | // Update AEffect in place 223 | let effect = unsafe { &mut *raw_effect }; 224 | effect.numPrograms = info.presets; 225 | effect.numParams = info.parameters; 226 | effect.numInputs = info.inputs; 227 | effect.numOutputs = info.outputs; 228 | effect.flags = { 229 | use api::PluginFlags; 230 | 231 | let mut flag = PluginFlags::CAN_REPLACING; 232 | 233 | if info.f64_precision { 234 | flag |= PluginFlags::CAN_DOUBLE_REPLACING; 235 | } 236 | 237 | if editor.is_some() { 238 | flag |= PluginFlags::HAS_EDITOR; 239 | } 240 | 241 | if info.preset_chunks { 242 | flag |= PluginFlags::PROGRAM_CHUNKS; 243 | } 244 | 245 | if let plugin::Category::Synth = info.category { 246 | flag |= PluginFlags::IS_SYNTH; 247 | } 248 | 249 | if info.silent_when_stopped { 250 | flag |= PluginFlags::NO_SOUND_IN_STOP; 251 | } 252 | 253 | flag.bits() 254 | }; 255 | effect.initialDelay = info.initial_delay; 256 | effect.object = Box::into_raw(Box::new(Box::new(plugin) as Box)) as *mut _; 257 | effect.user = Box::into_raw(Box::new(PluginCache::new(&info, params, editor))) as *mut _; 258 | effect.uniqueId = info.unique_id; 259 | effect.version = info.version; 260 | 261 | effect 262 | } 263 | 264 | #[cfg(test)] 265 | mod tests { 266 | use std::ptr; 267 | 268 | use std::os::raw::c_void; 269 | 270 | use crate::{ 271 | api::{consts::VST_MAGIC, AEffect}, 272 | interfaces, 273 | plugin::{HostCallback, Info, Plugin}, 274 | }; 275 | 276 | struct TestPlugin; 277 | 278 | impl Plugin for TestPlugin { 279 | fn new(_host: HostCallback) -> Self { 280 | TestPlugin 281 | } 282 | 283 | fn get_info(&self) -> Info { 284 | Info { 285 | name: "Test Plugin".to_string(), 286 | vendor: "overdrivenpotato".to_string(), 287 | 288 | presets: 1, 289 | parameters: 1, 290 | 291 | unique_id: 5678, 292 | version: 1234, 293 | 294 | initial_delay: 123, 295 | 296 | ..Default::default() 297 | } 298 | } 299 | } 300 | 301 | plugin_main!(TestPlugin); 302 | 303 | extern "C" fn pass_callback( 304 | _effect: *mut AEffect, 305 | _opcode: i32, 306 | _index: i32, 307 | _value: isize, 308 | _ptr: *mut c_void, 309 | _opt: f32, 310 | ) -> isize { 311 | 1 312 | } 313 | 314 | extern "C" fn fail_callback( 315 | _effect: *mut AEffect, 316 | _opcode: i32, 317 | _index: i32, 318 | _value: isize, 319 | _ptr: *mut c_void, 320 | _opt: f32, 321 | ) -> isize { 322 | 0 323 | } 324 | 325 | #[cfg(target_os = "windows")] 326 | #[test] 327 | fn old_hosts() { 328 | assert_eq!(MAIN(fail_callback), ptr::null_mut()); 329 | } 330 | 331 | #[cfg(target_os = "macos")] 332 | #[test] 333 | fn old_hosts() { 334 | assert_eq!(main_macho(fail_callback), ptr::null_mut()); 335 | } 336 | 337 | #[test] 338 | fn host_callback() { 339 | assert_eq!(VSTPluginMain(fail_callback), ptr::null_mut()); 340 | } 341 | 342 | #[test] 343 | fn aeffect_created() { 344 | let aeffect = VSTPluginMain(pass_callback); 345 | assert!(!aeffect.is_null()); 346 | } 347 | 348 | #[test] 349 | fn plugin_drop() { 350 | static mut DROP_TEST: bool = false; 351 | 352 | impl Drop for TestPlugin { 353 | fn drop(&mut self) { 354 | unsafe { 355 | DROP_TEST = true; 356 | } 357 | } 358 | } 359 | 360 | let aeffect = VSTPluginMain(pass_callback); 361 | assert!(!aeffect.is_null()); 362 | 363 | unsafe { (*aeffect).drop_plugin() }; 364 | 365 | // Assert that the VST is shut down and dropped. 366 | assert!(unsafe { DROP_TEST }); 367 | } 368 | 369 | #[test] 370 | fn plugin_no_drop() { 371 | let aeffect = VSTPluginMain(pass_callback); 372 | assert!(!aeffect.is_null()); 373 | 374 | // Make sure this doesn't crash. 375 | unsafe { (*aeffect).drop_plugin() }; 376 | } 377 | 378 | #[test] 379 | fn plugin_deref() { 380 | let aeffect = VSTPluginMain(pass_callback); 381 | assert!(!aeffect.is_null()); 382 | 383 | let plugin = unsafe { (*aeffect).get_plugin() }; 384 | // Assert that deref works correctly. 385 | assert!(plugin.get_info().name == "Test Plugin"); 386 | } 387 | 388 | #[test] 389 | fn aeffect_params() { 390 | // Assert that 2 function pointers are equal. 391 | macro_rules! assert_fn_eq { 392 | ($a:expr, $b:expr) => { 393 | assert_eq!($a as usize, $b as usize); 394 | }; 395 | } 396 | 397 | let aeffect = unsafe { &mut *VSTPluginMain(pass_callback) }; 398 | 399 | assert_eq!(aeffect.magic, VST_MAGIC); 400 | assert_fn_eq!(aeffect.dispatcher, interfaces::dispatch); 401 | assert_fn_eq!(aeffect._process, interfaces::process_deprecated); 402 | assert_fn_eq!(aeffect.setParameter, interfaces::set_parameter); 403 | assert_fn_eq!(aeffect.getParameter, interfaces::get_parameter); 404 | assert_eq!(aeffect.numPrograms, 1); 405 | assert_eq!(aeffect.numParams, 1); 406 | assert_eq!(aeffect.numInputs, 2); 407 | assert_eq!(aeffect.numOutputs, 2); 408 | assert_eq!(aeffect.reserved1, 0); 409 | assert_eq!(aeffect.reserved2, 0); 410 | assert_eq!(aeffect.initialDelay, 123); 411 | assert_eq!(aeffect.uniqueId, 5678); 412 | assert_eq!(aeffect.version, 1234); 413 | assert_fn_eq!(aeffect.processReplacing, interfaces::process_replacing); 414 | assert_fn_eq!(aeffect.processReplacingF64, interfaces::process_replacing_f64); 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! A collection of commonly used items for implement a Plugin 2 | 3 | #[doc(no_inline)] 4 | pub use crate::api::{Events, Supported}; 5 | #[doc(no_inline)] 6 | pub use crate::buffer::{AudioBuffer, SendEventBuffer}; 7 | #[doc(no_inline)] 8 | pub use crate::event::{Event, MidiEvent}; 9 | #[doc(no_inline)] 10 | pub use crate::plugin::{CanDo, Category, HostCallback, Info, Plugin, PluginParameters}; 11 | #[doc(no_inline)] 12 | pub use crate::util::{AtomicFloat, ParameterTransfer}; 13 | -------------------------------------------------------------------------------- /src/util/atomic_float.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicU32, Ordering}; 2 | 3 | /// Simple atomic floating point variable with relaxed ordering. 4 | /// 5 | /// Designed for the common case of sharing VST parameters between 6 | /// multiple threads when no synchronization or change notification 7 | /// is needed. 8 | pub struct AtomicFloat { 9 | atomic: AtomicU32, 10 | } 11 | 12 | impl AtomicFloat { 13 | /// New atomic float with initial value `value`. 14 | pub fn new(value: f32) -> AtomicFloat { 15 | AtomicFloat { 16 | atomic: AtomicU32::new(value.to_bits()), 17 | } 18 | } 19 | 20 | /// Get the current value of the atomic float. 21 | pub fn get(&self) -> f32 { 22 | f32::from_bits(self.atomic.load(Ordering::Relaxed)) 23 | } 24 | 25 | /// Set the value of the atomic float to `value`. 26 | pub fn set(&self, value: f32) { 27 | self.atomic.store(value.to_bits(), Ordering::Relaxed) 28 | } 29 | } 30 | 31 | impl Default for AtomicFloat { 32 | fn default() -> Self { 33 | AtomicFloat::new(0.0) 34 | } 35 | } 36 | 37 | impl std::fmt::Debug for AtomicFloat { 38 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 39 | std::fmt::Debug::fmt(&self.get(), f) 40 | } 41 | } 42 | 43 | impl std::fmt::Display for AtomicFloat { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | std::fmt::Display::fmt(&self.get(), f) 46 | } 47 | } 48 | 49 | impl From for AtomicFloat { 50 | fn from(value: f32) -> Self { 51 | AtomicFloat::new(value) 52 | } 53 | } 54 | 55 | impl From for f32 { 56 | fn from(value: AtomicFloat) -> Self { 57 | value.get() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Structures for easing the implementation of VST plugins. 2 | 3 | mod atomic_float; 4 | mod parameter_transfer; 5 | 6 | pub use self::atomic_float::AtomicFloat; 7 | pub use self::parameter_transfer::{ParameterTransfer, ParameterTransferIterator}; 8 | -------------------------------------------------------------------------------- /src/util/parameter_transfer.rs: -------------------------------------------------------------------------------- 1 | use std::mem::size_of; 2 | use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; 3 | 4 | const USIZE_BITS: usize = size_of::() * 8; 5 | 6 | fn word_and_bit(index: usize) -> (usize, usize) { 7 | (index / USIZE_BITS, 1usize << (index & (USIZE_BITS - 1))) 8 | } 9 | 10 | /// A set of parameters that can be shared between threads. 11 | /// 12 | /// Supports efficient iteration over parameters that changed since last iteration. 13 | #[derive(Default)] 14 | pub struct ParameterTransfer { 15 | values: Vec, 16 | changed: Vec, 17 | } 18 | 19 | impl ParameterTransfer { 20 | /// Create a new parameter set with `parameter_count` parameters. 21 | pub fn new(parameter_count: usize) -> Self { 22 | let bit_words = (parameter_count + USIZE_BITS - 1) / USIZE_BITS; 23 | ParameterTransfer { 24 | values: (0..parameter_count).map(|_| AtomicU32::new(0)).collect(), 25 | changed: (0..bit_words).map(|_| AtomicUsize::new(0)).collect(), 26 | } 27 | } 28 | 29 | /// Set the value of the parameter with index `index` to `value` and mark 30 | /// it as changed. 31 | pub fn set_parameter(&self, index: usize, value: f32) { 32 | let (word, bit) = word_and_bit(index); 33 | self.values[index].store(value.to_bits(), Ordering::Relaxed); 34 | self.changed[word].fetch_or(bit, Ordering::AcqRel); 35 | } 36 | 37 | /// Get the current value of the parameter with index `index`. 38 | pub fn get_parameter(&self, index: usize) -> f32 { 39 | f32::from_bits(self.values[index].load(Ordering::Relaxed)) 40 | } 41 | 42 | /// Iterate over all parameters marked as changed. If `acquire` is `true`, 43 | /// mark all returned parameters as no longer changed. 44 | /// 45 | /// The iterator returns a pair of `(index, value)` for each changed parameter. 46 | /// 47 | /// When parameters have been changed on the current thread, the iterator is 48 | /// precise: it reports all changed parameters with the values they were last 49 | /// changed to. 50 | /// 51 | /// When parameters are changed on a different thread, the iterator is 52 | /// conservative, in the sense that it is guaranteed to report changed 53 | /// parameters eventually, but if a parameter is changed multiple times in 54 | /// a short period of time, it may skip some of the changes (but never the 55 | /// last) and may report an extra, spurious change at the end. 56 | /// 57 | /// The changed parameters are reported in increasing index order, and the same 58 | /// parameter is never reported more than once in the same iteration. 59 | pub fn iterate(&self, acquire: bool) -> ParameterTransferIterator { 60 | ParameterTransferIterator { 61 | pt: self, 62 | word: 0, 63 | bit: 1, 64 | acquire, 65 | } 66 | } 67 | } 68 | 69 | /// An iterator over changed parameters. 70 | /// Returned by [`iterate`](struct.ParameterTransfer.html#method.iterate). 71 | pub struct ParameterTransferIterator<'pt> { 72 | pt: &'pt ParameterTransfer, 73 | word: usize, 74 | bit: usize, 75 | acquire: bool, 76 | } 77 | 78 | impl<'pt> Iterator for ParameterTransferIterator<'pt> { 79 | type Item = (usize, f32); 80 | 81 | fn next(&mut self) -> Option<(usize, f32)> { 82 | let bits = loop { 83 | if self.word == self.pt.changed.len() { 84 | return None; 85 | } 86 | let bits = self.pt.changed[self.word].load(Ordering::Acquire) & self.bit.wrapping_neg(); 87 | if bits != 0 { 88 | break bits; 89 | } 90 | self.word += 1; 91 | self.bit = 1; 92 | }; 93 | 94 | let bit_index = bits.trailing_zeros() as usize; 95 | let bit = 1usize << bit_index; 96 | let index = self.word * USIZE_BITS + bit_index; 97 | 98 | if self.acquire { 99 | self.pt.changed[self.word].fetch_and(!bit, Ordering::AcqRel); 100 | } 101 | 102 | let next_bit = bit << 1; 103 | if next_bit == 0 { 104 | self.word += 1; 105 | self.bit = 1; 106 | } else { 107 | self.bit = next_bit; 108 | } 109 | 110 | Some((index, self.pt.get_parameter(index))) 111 | } 112 | } 113 | 114 | #[cfg(test)] 115 | mod tests { 116 | extern crate rand; 117 | 118 | use crate::util::ParameterTransfer; 119 | 120 | use std::sync::mpsc::channel; 121 | use std::sync::Arc; 122 | use std::thread; 123 | use std::time::Duration; 124 | 125 | use self::rand::rngs::StdRng; 126 | use self::rand::{Rng, SeedableRng}; 127 | 128 | const THREADS: usize = 3; 129 | const PARAMETERS: usize = 1000; 130 | const UPDATES: usize = 1_000_000; 131 | 132 | #[test] 133 | fn parameter_transfer() { 134 | let transfer = Arc::new(ParameterTransfer::new(PARAMETERS)); 135 | let (tx, rx) = channel(); 136 | 137 | // Launch threads that change parameters 138 | for t in 0..THREADS { 139 | let t_transfer = Arc::clone(&transfer); 140 | let t_tx = tx.clone(); 141 | let mut t_rng = StdRng::seed_from_u64(t as u64); 142 | thread::spawn(move || { 143 | let mut values = vec![0f32; PARAMETERS]; 144 | for _ in 0..UPDATES { 145 | let p: usize = t_rng.gen_range(0..PARAMETERS); 146 | let v: f32 = t_rng.gen_range(0.0..1.0); 147 | values[p] = v; 148 | t_transfer.set_parameter(p, v); 149 | } 150 | t_tx.send(values).unwrap(); 151 | }); 152 | } 153 | 154 | // Continually receive updates from threads 155 | let mut values = vec![0f32; PARAMETERS]; 156 | let mut results = vec![]; 157 | let mut acquire_rng = StdRng::seed_from_u64(42); 158 | while results.len() < THREADS { 159 | let mut last_p = -1; 160 | for (p, v) in transfer.iterate(acquire_rng.gen_bool(0.9)) { 161 | assert!(p as isize > last_p); 162 | last_p = p as isize; 163 | values[p] = v; 164 | } 165 | thread::sleep(Duration::from_micros(100)); 166 | while let Ok(result) = rx.try_recv() { 167 | results.push(result); 168 | } 169 | } 170 | 171 | // One last iteration to pick up all updates 172 | let mut last_p = -1; 173 | for (p, v) in transfer.iterate(true) { 174 | assert!(p as isize > last_p); 175 | last_p = p as isize; 176 | values[p] = v; 177 | } 178 | 179 | // Now there should be no more updates 180 | assert!(transfer.iterate(true).next().is_none()); 181 | 182 | // Verify final values 183 | for p in 0..PARAMETERS { 184 | assert!((0..THREADS).any(|t| results[t][p] == values[p])); 185 | } 186 | } 187 | } 188 | --------------------------------------------------------------------------------