├── .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 | [](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 |
--------------------------------------------------------------------------------