├── .github └── workflows │ ├── rust.yml │ └── update-docs.yml ├── .gitignore ├── CONTRIBUTING.md ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-BSD.txt ├── LICENSE-MIT.txt ├── README.md ├── RELEASES.md ├── design.md ├── event-queue ├── Cargo.toml └── src │ └── lib.rs ├── examples ├── example_synth.rs ├── jack_synth.rs ├── offline_synth.rs └── vst_synth.rs └── src ├── backend ├── combined │ ├── dummy.rs │ ├── hound.rs │ ├── memory.rs │ ├── midly.rs │ └── mod.rs ├── jack_backend.rs ├── mod.rs └── vst_backend.rs ├── buffer └── mod.rs ├── envelope ├── mod.rs └── staircase_envelope.rs ├── event ├── event_queue.rs └── mod.rs ├── lib.rs ├── meta └── mod.rs ├── test_utilities └── mod.rs └── utilities ├── mod.rs └── polyphony.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Install rustfmt 18 | run: rustup component add rustfmt 19 | - name: Check formatting 20 | run: cargo fmt --all -- --check 21 | - name: install libjack-dev 22 | run: sudo apt-get install libjack-dev 23 | - name: Build 24 | run: cargo build --verbose --features all 25 | - name: Run tests 26 | run: cargo test --verbose --features all 27 | - name: Check rsor 28 | run: cargo check --tests --examples --features rsor-0-1 29 | - name: Check jack 0.6.2 30 | run: cargo update -p jack --precise 0.6.2; cargo check --tests --examples --features backend-jack 31 | - name: Check jack 0.7.0 32 | run: cargo update -p jack --precise 0.7.0; cargo check --tests --examples --features backend-jack 33 | - name: Check vst 34 | run: cargo check --tests --examples --features backend-vst 35 | - name: Check hound 36 | run: cargo check --tests --examples --features backend-combined-hound 37 | - name: Check wav 0.6 38 | run: cargo check --tests --examples --features backend-combined-wav-0-6 39 | - name: Check midly 40 | run: cargo check --tests --examples --features backend-combined-midly-0-5 41 | 42 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: Update-gh-pages 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v2 15 | with: 16 | persist-credentials: false 17 | - name: Rustdoc 18 | run: cargo rustdoc --features all 19 | - name: Publish 20 | uses: JamesIves/github-pages-deploy-action@releases/v3 21 | with: 22 | ACCESS_TOKEN: ${{ secrets.PUBLISH_GH_PAGES }} 23 | BRANCH: gh-pages 24 | FOLDER: target/doc 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | #Contributing 2 | This crate has been deprecated. 3 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the rsynth project are retained by their contributors. 2 | 3 | Authors of rsynth are: 4 | * Alexander Lozada 5 | * Pieter Penninckx 6 | 7 | Via private communication, Alexander Lozada has given Pieter Penninckx 8 | the permission to license rsynth as below. 9 | 10 | 11 | Rsynth is licensed under the MIT license or the 3-Clause BSD License, at your option. 12 | 13 | For the application of the MIT license, the examples included in the doc comments are not 14 | considered "substatial portions of this Software". 15 | 16 | 17 | License texts can be found: 18 | * for the 3-Clause BSD License: or 19 | 20 | * for the MIT license: or 21 | . 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rsynth" 3 | version = "0.1.1" 4 | authors = ["Alexander Lozada ", "Pieter Penninckx"] 5 | description = "A library for developing audio plugins and applications, with a focus on software synthesis." 6 | license = "MIT OR BSD-3-Clause" 7 | autoexamples = false 8 | edition = "2018" 9 | readme = "README.md" 10 | repository = "https://github.com/PieterPenninckx/rsynth" 11 | keywords = ["audio", "real-time", "synthesis"] 12 | categories = ["multimedia::audio"] 13 | exclude = [".github/"] 14 | 15 | [features] 16 | default = ["all"] 17 | all = ["backend-jack", "backend-vst", "backend-combined-all", "rsor-0-1"] 18 | backend-jack = ["jack"] 19 | backend-vst = ["vst"] 20 | backend-combined-all = ["backend-combined-hound", "backend-combined-midly-0-5", "backend-combined-wav-0-6"] 21 | backend-combined-hound = ["hound", "backend-combined", "dasp_sample"] 22 | backend-combined-wav-0-6 = ["wav-0-6", "backend-combined", "dasp_sample"] 23 | backend-combined-midly-0-5 = ["midly-0-5", "backend-combined"] 24 | backend-combined = ["itertools", "event-queue"] 25 | rsor-0-1 = ["rsor"] 26 | 27 | [dependencies] 28 | event-queue = {path = "./event-queue", optional = true} 29 | num-traits = "0.2" 30 | log = "0.4" 31 | jack = {version = ">= 0.6.2, < 0.8.0", optional = true} 32 | vst = {version = "0.2.0", optional = true} 33 | hound = {version = "3.4.0", optional = true} 34 | dasp_sample = {version = "0.11.0", optional = true} 35 | wav-0-6 = {package = "wav", version = "0.6.0", optional = true} 36 | vecstorage = "0.1.2" 37 | midi-consts = "0.1.0" 38 | gcd = "2.0.1" 39 | itertools = {version = "0.10.0", optional = true} 40 | rsor = {version = "0.1.2", optional = true} 41 | 42 | [dependencies.midly-0-5] 43 | package = "midly" 44 | version = "0.5.0" 45 | optional = true 46 | default-features = false 47 | features = ["std"] 48 | 49 | [dev-dependencies] 50 | rand = "0.3" 51 | polyphony = {version = "0.1.0", features = ["midi"]} 52 | 53 | [package.metadata.docs.rs] 54 | features = [ "all" ] 55 | default-target = "x86_64-unknown-linux-gnu" 56 | targets = [] 57 | 58 | [[example]] 59 | name = "vst_synth" 60 | crate-type = ["cdylib"] 61 | 62 | [[example]] 63 | name = "jack_synth" 64 | 65 | [[example]] 66 | name = "offline_synth" 67 | -------------------------------------------------------------------------------- /LICENSE-BSD.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Alexander Lozada 2 | Copyright 2020 Pieter Penninckx 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rsynth 2 | 3 | A deprecated API abstraction for API's for audio plugins and applications. 4 | You could use it to write real-time audio effects, software synthesizers, ... and target different platforms 5 | (jack, offline processing, ...). 6 | 7 | ## This crate has been deprecated 8 | This crate has been deprecated. See [this blog post](https://nckx.be/blog/rsynth-deprecation/) for more information. 9 | 10 | ### What should I do if I use this crate? 11 | At the time of writing, here are some options 12 | * Use [cpal](https://crates.io/crates/cpal) if you want to "just" play audio on various platforms. 13 | * Use [nih-plug](https://github.com/robbert-vdh/nih-plug) if this is a good solution for you. 14 | * Write the plugin as a “core” library (a Rust crate or module). This is anyhow something I'd recommend, also if you use [`nih-plug`](https://github.com/robbert-vdh/nih-plug), for instance. Per plugin standard that you want to support, create a separate crate that depends both on the “core” library and on an a library that is dedicated to that particular plugin standard (such as the [`lv2`](https://crates.io/crates/lv2) crate and the [`clack`](https://github.com/prokopyl/clack) crate (not (yet?) on crates.io). 15 | 16 | ## Old documentation 17 | ### Feature matrix 18 | 19 | We focus on API's that are typically used for audio effects and software synthesizers. 20 | If you want to "just" play audio on various platforms, [cpal](https://crates.io/crates/cpal) may 21 | be better suited for you. 22 | 23 | | feature | VST 2.4 via [`vst-rs`] | Jack via [`jack`] | Offline audio rendering | 24 | |---------|:------------------------------:|:-----------------:|:-----------------------:| 25 | | Full duplex audio input and output | ✓ | ✓ | ✓ | 26 | | Midi input | ✓ | ✓ | ✓ | 27 | | Midi output | N/A | ✓ | ✘ | 28 | | Sample accurate midi | N/A | ✓ | ✓ | 29 | | Multiple midi inputs and outputs | N/A | ✓ | ✘ | 30 | | Sampling frequency change | ✓ | ✘ | N/A | 31 | | Signal stopping the application | N/A | ✓ | ✓ | 32 | | Jack-specific events | N/A | ✘ | N/A | 33 | | Basic meta-data | ✓ | ✓ | N/A | 34 | | Access to the underlying host | ✓ | ✓ | N/A | 35 | | Parameter changes | ✘ | ✘ | ✘ | 36 | | GUI support | ✘ | ✘ | ✘ | 37 | 38 | #### Feature flags 39 | 40 | Many features are behind feature flags: 41 | * `all`: all the features below 42 | * `backend-jack`: create standalone `jack` applications. 43 | * `backend-vst`: create VST 2.4 plugins. 44 | * `backend-combined-all`: all the "combined" backends for offline processing and testing. This always include in-memory dummy and testing backends. 45 | * `backend-combined-hound`: read and write `.wav` files with the `hound` crate 46 | * `backend-combined-wav-0-6`: read and write `.wav` files with the `wav` crate 47 | * `backend-combined-midly-0-5`: read and write `.mid` files with the `midly` crate 48 | * `rsor-0-1`: add support for using the `rsor` crate for some methods (if you prefer `rsor` over `vecstorage`) 49 | 50 | ### Documentation 51 | 52 | The API documentation can be found 53 | * [on docs.rs](https://docs.rs/rsynth/) for the version that is distributed via crates.io. 54 | * [on GitHub pages](https://pieterpenninckx.github.io/rsynth/rsynth) for the documentation of the master branch 55 | * on your local machine after running `cargo rustdoc --features all` 56 | 57 | ### Philosophy and design 58 | `rsynth` presents itself as a library, rather than a framework. 59 | Rather than trying to solve every problem (which is not feasible for the small team), 60 | `rsynth` is designed to be easy to combine with other crates for specific tasks, such as 61 | * [`polyphony`](https://crates.io/crates/polyphony): the name says it all 62 | * [`wmidi`](https://crates.io/crates/wmidi): encode and decode midi messages in real-time 63 | * [`midi-consts`](https://crates.io/crates/midi-consts): constants for low-level handling of midi data 64 | * [`rtrb`](crates.io/crates/rtrb), a realtime-safe single-producer single-consumer ring buffer that can be used to communicate between threads. 65 | 66 | Background on the design can be found in the [design.md](design.md) document. 67 | 68 | ### Examples 69 | There are full examples in 70 | [the examples folder in the source code](https://github.com/PieterPenninckx/rsynth/tree/master/examples). 71 | 72 | ## Current State 73 | This crate has been deprecated. See [this blog post](https://nckx.be/blog/rsynth-deprecation/) for more information. 74 | 75 | # License 76 | 77 | The source code of `rsynth` is licensed under the MIT/BSD-3 License. 78 | 79 | Note that in order to use `rsynth` in combination with other crates (libraries), the combined work needs 80 | to comply with the license of that crate as well. In particular, the following optional dependencies may require your attention: 81 | * the `hound` crate (behind the `backend-combined-hound` feature) uses the Apache license, see [its readme](https://github.com/ruuda/hound#license) for more details 82 | * the `wav` crate (behind the `backend-combined-wav` feature) uses the LGPL license 83 | 84 | [`vst-rs`]: https://github.com/RustAudio/vst-rs 85 | [`jack`]:https://crates.io/crates/jack 86 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | Version 0.1.1 2 | ============= 3 | * Clarify copyright (MIT/3 clause BSD) (synchronise Cargo.toml with README.md). 4 | * Deprecate envelopes. 5 | * Polyphony has been deprecated in favour of the `polyphony` crate. 6 | * Move from the `sample` crate to `dasp_sample` to reduce compile times a little. 7 | * Re-export jack-specific data types to make it easier to upgrade `jack` without breaking things. 8 | * Give access to jack backend in callbacks. 9 | * Implement `Display` and `Error` for `HoundAudioError` and for `CombinedError`. 10 | 11 | Version 0.1.0 12 | ============= 13 | * Add support for jack as a backend 14 | * Add an abstraction for audio buffers 15 | * Add support for offline rendering 16 | * Add a backend independent mechanism for specifying meta-data. It also has a “user friendly” way for specifying the meta-data. 17 | * Lots of edits to the documentation 18 | * Lots and lots of refactoring big and small 19 | 20 | Version 0.0.1 21 | ============= 22 | Initial release. 23 | -------------------------------------------------------------------------------- /design.md: -------------------------------------------------------------------------------- 1 | Notes about the design 2 | ====================== 3 | 4 | The `Default` trait is not required 5 | ----------------------------------- 6 | Implementing `Default` is sometimes not possible with `#[derive(Default)]` and it feels 7 | awkward to implement setup (e.g. reading config files) in the `default()` method. 8 | For `rust-vst`, an extra macro wraps the setup in a `Default` implementation, so that at least it 9 | doesn't _feel_ awkward (but it's still a hack, of course). 10 | Also note that `rust-vst` only requires the `Default` trait to enable a default implementation 11 | for the `new()` function, it is not used directly by `rust-vst` itself. 12 | 13 | Not object safe 14 | --------------- 15 | Many of the traits are not object safe. In practice, this is not a problem for using `rust-vst` 16 | because an extra macro wraps it. 17 | 18 | Separate `EventHandler` trait 19 | ----------------------------- 20 | There is a separate trait for event handling: 21 | ```rust 22 | trait EventHandler { 23 | fn handle_event(&mut self, event: E); 24 | } 25 | ``` 26 | In this way, third party crates that define backends can define their own event types. 27 | 28 | No associated constants for plugin meta-data 29 | -------------------------------------------- 30 | The idea behind this was that it cannot change during the execution of the application. 31 | We got rid of this in order to enable a more dynamic approach and in order to enable the 32 | `Meta` trait. 33 | 34 | Separate `AudioRenderer` and `ContextualAudioRenderer` traits 35 | ------------------------------------------------------------- 36 | These methods were originally together with some meta-data in the `Plugin` trait, 37 | but we have split this off so that backends can have special meta-data, without 38 | interfering with the rendering. 39 | 40 | Generic trait instead of generic method 41 | --------------------------------------- 42 | The `AudioRenderer` and `ContextualAudioRenderer` traits are generic over the floating 43 | point type, instead of having a method that is generic over _all_ float types. 44 | In practice, backends only require renderers over `f32` and/or `f64`, not over _all_ floating 45 | point types. So in practice, for instance the vst backend can require 46 | `AudioRenderer` and `AudioRenderer`. These can be implemented separately, 47 | allowing for SIMD optimization, or together in one generic impl block. 48 | 49 | Separate method for `set_sample_rate` 50 | ------------------------------------- 51 | This is a separate method and not an "event type". The idea behind this is that it's guaranteed 52 | to be called before other methods and outside of the "realtime" path (whereas 53 | `handle_events` is called in the "realtime" path). 54 | I don't know if this is the best solution, though. Leaving as it is until we have a more clear 55 | understanding of it. 56 | 57 | Decisions behind `render_buffer` 58 | ------------------------------- 59 | `render_buffer` is at the core and some design decisions made it the way it is now. 60 | 61 | ### Push-based (instead of pull-based) 62 | The `render_buffer` gets the buffers it needs as parameters instead of getting a queue from which 63 | it has to "pull" the input buffers (like Jack works and if I'm not mistaken AudioUnits as well). 64 | The upside is that it's straightforward from a developer perspective, the downside is that it's 65 | less flexible. E.g. it's hard to implement real-time sample rate conversion in this way. 66 | Nevertheless, I've chosen this design because it's what is convenient for most plugin developers 67 | and developers wanting to write something like real-time sample rate conversion will probably 68 | not use high-level abstractions like rsynth. 69 | 70 | ### Buffers as slices of slices 71 | Somewhere an intermediate design was to have traits `InputBuffer<'a>` and `OutputBuffer<'a>`, 72 | but this lead to a cascade of fights with the borrow checker: 73 | 74 | * First it was problematic for the `Polyphonic` middleware (simplified pseudo-Rust of 75 | `Polyphonic`s `render_buffer` method): 76 | ```rust 77 | fn render_buffer<'a, I: InputBuffers<'a>, O: OutputBuffers<'a>>(&mut self, inputs: &I, outputs: &mut O) { 78 | for voice in self.voices { 79 | voice.render_buffer(inputs, outputs); // <-- the borrow of outputs needs to be shorter 80 | } 81 | } 82 | ``` 83 | The compiler didn't allow this because the borrow of `outputs` must be shorter than the 84 | "external" lifetime `'a` in order to avoid overlapping borrows. 85 | 86 | * Then we implemented it as follows: 87 | ```rust 88 | fn render_buffer(&mut self, inputs: &I, outputs: &mut O) 89 | where for<'a> I: InputBuffers<'a>, O: OutputBuffers<'a> 90 | { 91 | // ... 92 | } 93 | ``` 94 | That solved one problem, but introduced `for<'a>` which is not a frequently used feature 95 | in Rust and which is not supported in some contexts, so I ran into some trouble with this 96 | (I've forgotten which). 97 | 98 | For these reasons, I have abandoned this design and started using the slices instead. 99 | This in turn gives a problem for the API-wrappers, which will want to pre-allocate the buffer 100 | for the slices, but want to use this buffer for slices with different lifetimes. 101 | This has been solved by the `VecStorage` struct, which has moved to its own crate. 102 | 103 | One remaining issue is that the length of the buffer cannot be known when there are 0 inputs and 104 | 0 outputs. 105 | I tried to solve that by having a custom data _type_ (rather than a custom _trait_): `InputChunk` and 106 | `OutputChunk`, where `OutputChunk` is defined as follows: 107 | ```rust 108 | struct OutputChunk<'a, 'b, S> { 109 | number_of_frames: usize, 110 | channels: &'a mut [&'b mut [S]] 111 | } 112 | ``` 113 | Having a custom data type instead of a custom trait eliminates a number of the lifetime issues. 114 | In order to maintain the invariant that all channels 115 | have the same length (number_of_frames), `OutputChunk` cannot expose `channels` (because then somebody 116 | may use the `&'a mut` reference to replace a slice with a slice of a different length. 117 | So either this invariant needs to be given up, or `OutputChunk` needs to encapsulate everything, 118 | but this does not give such a straightforward and easy to use API. 119 | For this reason, I didn't keep the `OutputChunk` and continued to use the slices. 120 | 121 | Let on, I decided to use something like the `OutputChunk` struct anyway. I think the API is rather 122 | straightforward to use, but may need some small improvements here and there. 123 | 124 | Events 125 | ------ 126 | Currently, backends that support one MIDI-port use the `Timed` type 127 | and backends that support moree MIDI-ports use the `Indexed>` type. 128 | -------------------------------------------------------------------------------- /event-queue/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "event-queue" 3 | version = "0.1.0" 4 | authors = ["Pieter Penninckx"] 5 | edition = "2018" 6 | description = "A library for managing events, intended to be used for audio libraries and applications." 7 | license = "MIT OR Apache-2.0" 8 | autoexamples = false 9 | readme = "README.md" 10 | # repository = "https://github.com/PieterPenninckx/rsynth" 11 | # keywords = ["audio"] 12 | #categories = ["multimedia::audio"] 13 | 14 | [package.metadata.docs.rs] 15 | default-target = "x86_64-unknown-linux-gnu" 16 | targets = [] -------------------------------------------------------------------------------- /event-queue/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Queue events. 2 | use std::cmp::Ordering; 3 | use std::collections::vec_deque::{Drain, VecDeque}; 4 | use std::iter::FusedIterator; 5 | use std::ops::{Deref, Index, IndexMut, SubAssign}; 6 | 7 | /// A queue for timed events. 8 | pub struct EventQueue { 9 | queue: VecDeque<(T, E)>, 10 | } 11 | 12 | /// Determines what should happen when two events are queued with the same timing. 13 | pub enum EventCollisionHandling { 14 | /// Insert the newly queued event before the previously queued. 15 | InsertNewBeforeOld, 16 | /// Insert the newly queued event after the previously queued. 17 | InsertNewAfterOld, 18 | /// Ignore the newly queued event. 19 | IgnoreNew, 20 | /// Remove the previously queued event. 21 | RemoveOld, 22 | } 23 | 24 | /// Trait that describes how "event collision" (queuing two events with the same timestamp) should happen. 25 | pub trait HandleEventCollision { 26 | fn decide_on_collision(&self, old_event: &E, new_event: &E) -> EventCollisionHandling; 27 | } 28 | 29 | /// Always queue the new newly queued event before the previously queued in case of collision (same timestamp). 30 | pub struct AlwaysInsertNewBeforeOld; 31 | impl HandleEventCollision for AlwaysInsertNewBeforeOld { 32 | #[inline(always)] 33 | fn decide_on_collision(&self, _old_event: &E, _new_event: &E) -> EventCollisionHandling { 34 | EventCollisionHandling::InsertNewBeforeOld 35 | } 36 | } 37 | 38 | /// Always queue the new newly queued event after the previously queued in case of collision (same timestamp). 39 | pub struct AlwaysInsertNewAfterOld; 40 | impl HandleEventCollision for AlwaysInsertNewAfterOld { 41 | #[inline(always)] 42 | fn decide_on_collision(&self, _old_event: &E, _new_event: &E) -> EventCollisionHandling { 43 | EventCollisionHandling::InsertNewAfterOld 44 | } 45 | } 46 | 47 | /// Always ignore the newly queued event in case of collision (there's already an event with that timestamp). 48 | pub struct AlwaysIgnoreNew; 49 | impl HandleEventCollision for AlwaysIgnoreNew { 50 | #[inline(always)] 51 | fn decide_on_collision(&self, _old_event: &E, _new_event: &E) -> EventCollisionHandling { 52 | EventCollisionHandling::IgnoreNew 53 | } 54 | } 55 | 56 | /// Always remove the previously queued event in case of collision (there's already an event with that timestamp). 57 | pub struct AlwaysRemoveOld; 58 | impl HandleEventCollision for AlwaysRemoveOld { 59 | #[inline(always)] 60 | fn decide_on_collision(&self, _old_event: &E, _new_event: &E) -> EventCollisionHandling { 61 | EventCollisionHandling::RemoveOld 62 | } 63 | } 64 | 65 | impl Index for EventQueue { 66 | type Output = (T, E); 67 | 68 | fn index(&self, index: usize) -> &Self::Output { 69 | &self.queue[index] 70 | } 71 | } 72 | 73 | impl IndexMut for EventQueue { 74 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 75 | &mut self.queue[index] 76 | } 77 | } 78 | 79 | impl EventQueue { 80 | /// Create a new `EventQueue` fom a vector of events. 81 | /// _Note_: this may violate the invariants of the `EventQueue`, so it's only available for testing. 82 | #[cfg(test)] 83 | pub fn from_vec(events: Vec<(T, E)>) -> Self { 84 | Self { 85 | queue: events.into(), 86 | } 87 | } 88 | 89 | /// Create a new `EventQueue`. 90 | /// # Panics 91 | /// Panics if `capacity == 0`. 92 | pub fn new(capacity: usize) -> Self { 93 | assert!(capacity > 0); 94 | Self { 95 | queue: VecDeque::with_capacity(capacity), 96 | } 97 | } 98 | 99 | /// Queue a new event. 100 | /// When the buffer is full, an element may be removed from the queue to make some room. 101 | /// This element is returned. 102 | pub fn queue_event( 103 | &mut self, 104 | (new_time, new_event): (T, E), 105 | collision_decider: H, 106 | ) -> Option<(T, E)> 107 | where 108 | H: HandleEventCollision, 109 | T: Ord, 110 | { 111 | let mut new_event = new_event; 112 | let result; 113 | if self.queue.len() >= self.queue.capacity() { 114 | // Note: self.queue.capacity() > 0, so self.queue is not empty. 115 | // TODO: Log an error. 116 | // We remove the first event to come, in this way, 117 | // we are sure we are not skipping the "last" event, 118 | // because we assume that the state of the first event 119 | // is only temporarily, and the state of the last event 120 | // may remain forever. For this reason, it is safer to 121 | // remove the first event 122 | if new_time > self.queue[0].0 { 123 | result = self.queue.pop_front(); 124 | } else { 125 | return Some((new_time, new_event)); 126 | } 127 | } else { 128 | result = None; 129 | } 130 | // If we are at this point, we can assume that we can insert at least one more event. 131 | debug_assert!(self.queue.len() < self.queue.capacity()); 132 | 133 | let mut insert_index = 0; 134 | for read_event in self.queue.iter_mut() { 135 | match read_event.0.cmp(&new_time) { 136 | Ordering::Less => { 137 | insert_index += 1; 138 | } 139 | Ordering::Equal => { 140 | match collision_decider.decide_on_collision(&read_event.1, &new_event) { 141 | EventCollisionHandling::IgnoreNew => { 142 | return Some((new_time, new_event)); 143 | } 144 | EventCollisionHandling::InsertNewBeforeOld => { 145 | break; 146 | } 147 | EventCollisionHandling::InsertNewAfterOld => { 148 | insert_index += 1; 149 | } 150 | EventCollisionHandling::RemoveOld => { 151 | std::mem::swap(&mut read_event.1, &mut new_event); 152 | return Some((new_time, new_event)); 153 | } 154 | } 155 | } 156 | Ordering::Greater => { 157 | break; 158 | } 159 | } 160 | } 161 | self.queue.insert(insert_index, (new_time, new_event)); 162 | 163 | result 164 | } 165 | 166 | /// Remove all events before, but not on, this threshold. 167 | /// 168 | /// # Note about usage in real-time context 169 | /// If `T` implements drop, the elements that are removed are dropped. 170 | /// This may cause memory de-allocation, which you want to avoid in 171 | /// the real-time part of your library. 172 | pub fn forget_before(&mut self, threshold: T) 173 | where 174 | T: Copy + Ord, 175 | { 176 | self.queue.retain(|x| x.0 >= threshold); 177 | } 178 | 179 | /// Remove all events from the queue. 180 | /// 181 | /// # Note about usage in real-time context 182 | /// If `T` implements drop, the elements that are removed are dropped. 183 | /// This may cause memory de-allocation, which you want to avoid in 184 | /// the real-time part of your library. 185 | pub fn clear(&mut self) { 186 | self.queue.clear() 187 | } 188 | 189 | /// Shift time forward by `new_zero_time` frames. 190 | /// 191 | /// # Panics 192 | /// Panics in debug mode when at least one event has a `time_in_frames` 193 | /// that is < `new_zero_time`. 194 | pub fn shift_time(&mut self, new_zero_time: T) 195 | where 196 | T: Copy + SubAssign, 197 | { 198 | for event in self.queue.iter_mut() { 199 | event.0 -= new_zero_time; 200 | } 201 | } 202 | 203 | pub fn get_last_before(&self, time: T) -> Option<&(T, E)> 204 | where 205 | T: Ord, 206 | { 207 | if let Some(index) = self.queue.iter().rposition(|e| e.0 < time) { 208 | self.queue.get(index) 209 | } else { 210 | None 211 | } 212 | } 213 | 214 | /// Get the first event from the `EventQueue` if there is one and return `None` if the queue is empty. 215 | pub fn first(&self) -> Option<&(T, E)> { 216 | self.queue.get(0) 217 | } 218 | 219 | /// Create an iterator that drains all elements before but not on the given time. 220 | pub fn drain(&mut self, time: T) -> DrainingIter 221 | where 222 | T: Ord, 223 | { 224 | if let Some(index) = self.queue.iter().rposition(|e| e.0 < time) { 225 | DrainingIter { 226 | inner: self.queue.drain(0..=index), 227 | } 228 | } else { 229 | DrainingIter { 230 | inner: self.queue.drain(0..0), 231 | } 232 | } 233 | } 234 | 235 | /// Create an iterator that drains all elements. 236 | pub fn drain_all(&mut self) -> DrainingIter { 237 | DrainingIter { 238 | inner: self.queue.drain(0..), 239 | } 240 | } 241 | } 242 | 243 | impl Deref for EventQueue { 244 | type Target = VecDeque<(T, E)>; 245 | 246 | fn deref(&self) -> &Self::Target { 247 | &self.queue 248 | } 249 | } 250 | 251 | #[test] 252 | fn eventqueue_queue_event_new_event_ignored_when_already_full_and_new_event_comes_first() { 253 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 254 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 255 | // Check our assumption: 256 | assert_eq!(queue.queue.capacity(), queue.queue.len()); 257 | 258 | queue.queue_event((9, 3), AlwaysIgnoreNew); 259 | 260 | assert_eq!(queue.queue, initial_buffer); 261 | } 262 | 263 | #[test] 264 | fn event_queue_queue_event_first_event_removed_when_already_full_and_new_event_after_first() { 265 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 266 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 267 | // Check our assumption: 268 | assert_eq!(queue.queue.capacity(), queue.queue.len()); 269 | 270 | queue.queue_event((5, 25), AlwaysInsertNewAfterOld); 271 | 272 | assert_eq!(queue.queue, vec![(5, 25), (6, 36), (7, 49),]); 273 | } 274 | 275 | #[test] 276 | fn eventqueue_queue_event_new_event_inserted_at_correct_location() { 277 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 278 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 279 | queue.queue.reserve(1); 280 | 281 | queue.queue_event((5, 25), AlwaysInsertNewAfterOld); 282 | 283 | assert_eq!(queue.queue, vec![(4, 16), (5, 25), (6, 36), (7, 49),]); 284 | } 285 | 286 | #[test] 287 | fn eventqueue_queue_event_with_always_ignore_new_new_event_ignored_when_already_event_at_that_location( 288 | ) { 289 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 290 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 291 | queue.queue.reserve(1); 292 | 293 | // Act 294 | queue.queue_event((6, 25), AlwaysIgnoreNew); 295 | 296 | // Assert: 297 | assert_eq!(queue.queue, initial_buffer); 298 | } 299 | 300 | #[test] 301 | fn eventqueue_queue_event_with_always_ignore_old_old_event_ignored_when_already_event_at_that_location( 302 | ) { 303 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 304 | let expected_buffer = vec![(4, 16), (6, 25), (7, 49)]; 305 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 306 | queue.queue.reserve(1); 307 | 308 | // Act 309 | let result = queue.queue_event((6, 25), AlwaysRemoveOld); 310 | 311 | assert_eq!(result, Some((6, 36))); 312 | 313 | // Assert: 314 | assert_eq!(queue.queue, expected_buffer); 315 | } 316 | 317 | #[test] 318 | fn eventqueue_queue_event_with_always_insert_new_after_old() { 319 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 320 | let expected_buffer = vec![(4, 16), (6, 36), (6, 25), (7, 49)]; 321 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 322 | queue.queue.reserve(1); 323 | 324 | // Act 325 | let result = queue.queue_event((6, 25), AlwaysInsertNewAfterOld); 326 | 327 | assert_eq!(result, None); 328 | 329 | // Assert: 330 | assert_eq!(queue.queue, expected_buffer); 331 | } 332 | 333 | #[test] 334 | fn eventqueue_queue_event_with_always_insert_new_after_old_with_doubles() { 335 | let initial_buffer = vec![(6, 16), (6, 36), (7, 49)]; 336 | let expected_buffer = vec![(6, 16), (6, 36), (6, 25), (7, 49)]; 337 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 338 | queue.queue.reserve(1); 339 | 340 | // Act 341 | let result = queue.queue_event((6, 25), AlwaysInsertNewAfterOld); 342 | 343 | assert_eq!(result, None); 344 | 345 | // Assert: 346 | assert_eq!(queue.queue, expected_buffer); 347 | } 348 | 349 | #[test] 350 | fn eventqueue_queue_event_with_always_insert_new_before_old() { 351 | let initial_buffer = vec![(4, 16), (6, 36), (7, 49)]; 352 | let expected_buffer = vec![(4, 16), (6, 25), (6, 36), (7, 49)]; 353 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 354 | queue.queue.reserve(1); 355 | 356 | // Act 357 | let result = queue.queue_event((6, 25), AlwaysInsertNewBeforeOld); 358 | 359 | assert_eq!(result, None); 360 | 361 | // Assert: 362 | assert_eq!(queue.queue, expected_buffer); 363 | } 364 | 365 | #[test] 366 | fn eventqueue_forget_before() { 367 | let mut queue = EventQueue::from_vec({ vec![(4, 16), (6, 36), (7, 49), (8, 64)] }); 368 | queue.forget_before(7); 369 | assert_eq!(queue.queue, vec![(7, 49), (8, 64),]); 370 | } 371 | 372 | #[test] 373 | fn eventqueue_forget_everything() { 374 | let mut queue = EventQueue::from_vec({ vec![(4, 16), (6, 36), (7, 49), (8, 64)] }); 375 | queue.forget_before(9); 376 | assert_eq!(queue.queue, Vec::new()); 377 | } 378 | 379 | /// Draining iterator created by the [`EventQueue::drain`] method. 380 | pub struct DrainingIter<'a, T, E> { 381 | inner: Drain<'a, (T, E)>, 382 | } 383 | 384 | impl<'a, T, E> Iterator for DrainingIter<'a, T, E> { 385 | type Item = (T, E); 386 | fn next(&mut self) -> Option { 387 | self.inner.next() 388 | } 389 | 390 | fn size_hint(&self) -> (usize, Option) { 391 | self.inner.size_hint() 392 | } 393 | } 394 | 395 | impl<'a, T, E> DoubleEndedIterator for DrainingIter<'a, T, E> { 396 | fn next_back(&mut self) -> Option { 397 | self.inner.next_back() 398 | } 399 | } 400 | 401 | impl<'a, T, E> ExactSizeIterator for DrainingIter<'a, T, E> {} 402 | 403 | impl<'a, T, E> FusedIterator for DrainingIter<'a, T, E> {} 404 | -------------------------------------------------------------------------------- /examples/example_synth.rs: -------------------------------------------------------------------------------- 1 | // This file contains the actual sound generation of a plugin that is shared between all backends. 2 | // The integration with VST is in the `vst_synt.rs` file. 3 | // The integration with Jack is in the `jack_synth.rs` file. 4 | 5 | extern crate polyphony; 6 | 7 | use num_traits::Float; 8 | use polyphony::{ 9 | midi::{RawMidiEventToneIdentifierDispatchClassifier, ToneIdentifier}, 10 | simple_event_dispatching::{SimpleEventDispatcher, SimpleVoiceState}, 11 | EventDispatchClassifier, Voice, VoiceAssigner, 12 | }; 13 | use rand::{thread_rng, Rng}; 14 | use rsynth::event::{ 15 | ContextualEventHandler, EventHandler, Indexed, RawMidiEvent, SysExEvent, Timed, 16 | }; 17 | use rsynth::{AudioHandler, ContextualAudioRenderer}; 18 | 19 | use midi_consts::channel_event::*; 20 | use rsynth::backend::HostInterface; 21 | use rsynth::buffer::AudioBufferInOut; 22 | use rsynth::meta::{InOut, Meta, MetaData}; 23 | 24 | // The total number of samples to pre-calculate. 25 | // This is like recording a sample of white noise and then 26 | // using it as an oscillator. It saves on CPU overhead by 27 | // preventing us from having to use a random function each sample. 28 | static SAMPLE_SIZE: usize = 65536; 29 | static NUMBER_OF_VOICES: usize = 6; 30 | static AMPLIFY_MULTIPLIER: f32 = 1.0 / NUMBER_OF_VOICES as f32; 31 | 32 | // This struct defines the data that we will need to play one "noise" 33 | pub struct Noise { 34 | // Random data of the noise. 35 | white_noise: Vec, 36 | // At which sample in the noise we are. 37 | position: usize, 38 | // The amplitude. 39 | amplitude: f32, 40 | // This is used to know if this is currently playing and if so, what note. 41 | state: SimpleVoiceState, 42 | } 43 | 44 | impl Noise { 45 | fn new(sample_size: usize) -> Self { 46 | let mut rng = thread_rng(); 47 | let samples: Vec = rng 48 | .gen_iter::() 49 | .take(sample_size) 50 | .map(|r| { 51 | // The random generator generates noise between 0 and 1, 52 | // we map it to the range -1 to 1. 53 | 2.0 * r - 1.0 54 | }) 55 | .collect::>(); 56 | Noise { 57 | white_noise: samples, 58 | position: 0, 59 | amplitude: 0.0, 60 | state: SimpleVoiceState::Idle, 61 | } 62 | } 63 | 64 | // Here, we use one implementation over all floating point types. 65 | // If you want to use SIMD optimization, you can have separate implementations 66 | // for `f32` and `f64`. 67 | fn render_audio_buffer(&mut self, buffer: &mut AudioBufferInOut) 68 | where 69 | S: Float + From, 70 | { 71 | if self.state == SimpleVoiceState::Idle { 72 | return; 73 | } 74 | let outputs = buffer.outputs(); 75 | assert_eq!(2, outputs.number_of_channels()); 76 | for output_channel in outputs.channel_iter_mut() { 77 | for sample in output_channel.iter_mut() { 78 | // We "add" to the output. 79 | // In this way, various noises can be heard together. 80 | *sample = *sample 81 | + >::from(self.white_noise[self.position]) 82 | * >::from(self.amplitude); 83 | // Increment the position of our sound sample. 84 | // We loop this easily by using modulo. 85 | self.position = (self.position + 1) % self.white_noise.len(); 86 | } 87 | } 88 | } 89 | } 90 | 91 | // This enables using Sound in a polyphonic context. 92 | impl Voice> for Noise { 93 | fn state(&self) -> SimpleVoiceState { 94 | self.state 95 | } 96 | } 97 | 98 | impl EventHandler> for Noise { 99 | fn handle_event(&mut self, timed: Timed) { 100 | let state_and_channel = timed.event.data()[0]; 101 | 102 | // We are digging into the details of midi-messages here. 103 | // Alternatively, you could use the `wmidi` crate. 104 | let mut is_note_off_event = state_and_channel & EVENT_TYPE_MASK == NOTE_OFF; 105 | if state_and_channel & EVENT_TYPE_MASK == NOTE_ON { 106 | let velocity = timed.event.data()[2]; 107 | if velocity != 0 { 108 | self.amplitude = velocity as f32 / 127.0 * AMPLIFY_MULTIPLIER; 109 | self.state = SimpleVoiceState::Active(ToneIdentifier(timed.event.data()[1])); 110 | } else { 111 | is_note_off_event = true; 112 | } 113 | } 114 | if is_note_off_event { 115 | self.amplitude = 0.0; 116 | self.state = SimpleVoiceState::Idle; 117 | } 118 | } 119 | } 120 | 121 | pub struct NoisePlayer { 122 | meta_data: MetaData<&'static str, &'static str, &'static str>, 123 | voices: Vec, 124 | } 125 | 126 | impl NoisePlayer { 127 | fn meta_data() -> MetaData<&'static str, &'static str, &'static str> { 128 | MetaData { 129 | general_meta: "Noise generator", // The name of the plugin 130 | audio_port_meta: InOut { 131 | inputs: Vec::new(), // No audio inputs 132 | outputs: vec!["left", "right"], // Two audio outputs 133 | }, 134 | midi_port_meta: InOut { 135 | inputs: vec!["midi in"], // One midi in port 136 | outputs: Vec::new(), // No midi out port 137 | }, 138 | } 139 | } 140 | 141 | pub fn new() -> Self { 142 | let mut voices = Vec::new(); 143 | for _ in 0..NUMBER_OF_VOICES { 144 | voices.push(Noise::new(SAMPLE_SIZE)); 145 | } 146 | Self { 147 | meta_data: Self::meta_data(), 148 | voices: voices, 149 | } 150 | } 151 | } 152 | 153 | impl Meta for NoisePlayer { 154 | type MetaData = MetaData<&'static str, &'static str, &'static str>; 155 | 156 | fn meta(&self) -> &Self::MetaData { 157 | &self.meta_data 158 | } 159 | } 160 | 161 | impl AudioHandler for NoisePlayer { 162 | fn set_sample_rate(&mut self, sample_rate: f64) { 163 | trace!("set_sample_rate(sample_rate={})", sample_rate); 164 | // We are not doing anything with this right now. 165 | } 166 | } 167 | 168 | #[allow(unused_variables)] 169 | impl ContextualAudioRenderer for NoisePlayer 170 | where 171 | S: Float + From, 172 | Context: HostInterface, 173 | { 174 | fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut Context) { 175 | if !context.output_initialized() { 176 | // Initialize the output buffer. 177 | buffer.outputs().set(S::zero()); 178 | } 179 | for noise in self.voices.iter_mut() { 180 | noise.render_audio_buffer(buffer); 181 | } 182 | } 183 | } 184 | 185 | impl EventHandler> for NoisePlayer { 186 | fn handle_event(&mut self, event: Timed) { 187 | let classifier = RawMidiEventToneIdentifierDispatchClassifier; 188 | let classification = classifier.classify(event.event.data()); 189 | let mut dispatcher = SimpleEventDispatcher; 190 | let assignment = dispatcher.assign(classification, &mut self.voices); 191 | assignment.dispatch(event, &mut self.voices, Noise::handle_event); 192 | } 193 | } 194 | 195 | impl ContextualEventHandler, Context> for NoisePlayer { 196 | fn handle_event(&mut self, event: Timed, _context: &mut Context) { 197 | EventHandler::handle_event(self, event); 198 | } 199 | } 200 | 201 | // Only needed for Jack: delegate to the normal event handler. 202 | impl ContextualEventHandler>, Context> for NoisePlayer { 203 | fn handle_event(&mut self, event: Indexed>, _context: &mut Context) { 204 | EventHandler::handle_event(self, event.event) 205 | } 206 | } 207 | 208 | impl<'a, Context> ContextualEventHandler>, Context> for NoisePlayer { 209 | fn handle_event(&mut self, _event: Timed>, _context: &mut Context) { 210 | // We don't do anything with SysEx events. 211 | } 212 | } 213 | 214 | // Only needed for Jack. 215 | impl<'a, Context> ContextualEventHandler>>, Context> for NoisePlayer { 216 | fn handle_event(&mut self, _event: Indexed>, _context: &mut Context) { 217 | // We don't do anything with SysEx events 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /examples/jack_synth.rs: -------------------------------------------------------------------------------- 1 | // An example of a software synthesizer using the JACK back-end. 2 | // The code that is shared between all backends is in the `example_synth.rs` file. 3 | // 4 | // Compiling 5 | // ========= 6 | // You can compile this example with 7 | // ``` 8 | // cargo build --release --examples --features backend-jack 9 | // ``` 10 | // This generates a standalone application that you can find 11 | // 12 | // * in `target/release/examples/jack_synth` when you're using Linux 13 | // * under the `target/release/examples/` folder when you're using Windows or MacOs 14 | // 15 | // Running 16 | // ======= 17 | // 18 | // In order to run, you need three steps. Below, we discuss for each platform 19 | // (only Linux for now) how you can do this. 20 | // 21 | // 1. Start the `jack` daemon. 22 | // 2. Start the application generated during compiling. 23 | // 3. Connect the audio output of the synthesizer to the "system" audio input 24 | // 4. (Optionally) start a midi keyboard simulator 25 | // 5. Connect the midi keyboard output to the synthesizer midi input 26 | // 6. Start making some noise! 27 | // 28 | // ## Running under Linux 29 | // I think the easiest way is to use `qjackctl` and maybe `jack-keyboard`. 30 | // Then the steps become 31 | // 32 | // 1. Start `qjackctl`. Optionally configure some stuff. Click the "start" button. 33 | // 2. Start the synthesizer generated during compiling. This needs to be done after 34 | // the jack daemon was started in step 1 because it automatically registers its ports upon 35 | // startup. 36 | // 3. In Qjackctl, click on the "Connect" button. Under the "Audio" tab, connect the 37 | // synthesizer to the system. 38 | // 4. (optionally) start `jack-keyboard`. 39 | // 5. In Qjackctl, in the "Connections" window, under the "Midi" tab, connect the 40 | // midi keyboard to the synthesizer. 41 | // 6. Press keys on the midi keyboard. 42 | // 43 | // ## Logging 44 | // In order to enable logging, set the environment variable `RSYNTH_LOG_LEVEL` to 45 | // one of the supported log levels. 46 | // Recognized log levels are: 'off', 'error', 'warning', 'info', 'debug' and 'trace'. 47 | // 48 | // You can set the environment variable `RSYNTH_LOG_FILE` to the file name of the file in which 49 | // you want to log. 50 | // 51 | // Note that these environment variables need to be visible to the host. 52 | // Note that the example is also logging to a file in the realtime thread, which may cause clipping. 53 | #[macro_use] 54 | extern crate log; 55 | extern crate num_traits; 56 | extern crate rand; 57 | extern crate rsynth; 58 | 59 | mod example_synth; 60 | use example_synth::*; 61 | 62 | #[cfg(feature = "backend-jack")] 63 | use rsynth::backend::jack_backend::run; 64 | 65 | #[cfg(feature = "backend-jack")] 66 | fn main() { 67 | if let Err(e) = run(NoisePlayer::new()) { 68 | println!("Unexpected error: {}", e); 69 | } 70 | } 71 | 72 | #[cfg(not(feature = "backend-jack"))] 73 | fn main() { 74 | println!("This example was compiled without support for jack."); 75 | println!("Compile with passing `--features backend-jack`"); 76 | println!("as parameter to `cargo`."); 77 | } 78 | -------------------------------------------------------------------------------- /examples/offline_synth.rs: -------------------------------------------------------------------------------- 1 | // An example of a software synthesizer using the offline ("combined") back-end. 2 | // The code that is shared between all backends is in the `example_synth.rs` file. 3 | // 4 | // Compiling 5 | // ========= 6 | // You can compile this example with 7 | // ```bash 8 | // cargo build --release --examples --features backend-combined-midly-0-5,backend-combined-wav-0-6 9 | // ``` 10 | // This generates a standalone application that you can find 11 | // 12 | // * in `target/release/examples/offline_synth` when you're using Linux 13 | // * under the `target/release/examples/` folder when you're using Windows or MacOs 14 | // 15 | #[macro_use] 16 | extern crate log; 17 | #[cfg(feature = "backend-combined-midly-0-5")] 18 | extern crate midly_0_5; 19 | extern crate num_traits; 20 | extern crate rand; 21 | extern crate rsynth; 22 | 23 | mod example_synth; 24 | use example_synth::*; 25 | 26 | #[cfg(feature = "backend-combined")] 27 | use rsynth::backend::combined::dummy::{AudioDummy, MidiDummy}; 28 | #[cfg(feature = "backend-combined-wav-0-6")] 29 | use rsynth::backend::combined::memory::wav_0_6::{read, write, BitDepth, Header}; 30 | #[cfg(feature = "backend-combined")] 31 | use rsynth::backend::combined::memory::AudioBufferWriter; 32 | #[cfg(feature = "backend-combined-midly-0-5")] 33 | use rsynth::backend::combined::midly::{midly_0_5::Smf, MidlyMidiReader}; 34 | #[cfg(feature = "backend-combined")] 35 | use rsynth::backend::combined::run; 36 | use rsynth::buffer::AudioChunk; 37 | use std::fs::OpenOptions; 38 | use std::{env, fs}; 39 | 40 | #[cfg(all( 41 | feature = "backend-combined-midly-0-5", 42 | feature = "backend-combined-wav-0-6" 43 | ))] 44 | fn main() { 45 | let args: Vec = env::args().collect(); 46 | if args.len() < 3 { 47 | println!("Missing command line argument."); 48 | } else { 49 | let samplerate = 44100; 50 | let input_midi_filename = args[1].clone(); 51 | println!("Reading midi input file."); 52 | let input_midi_data = fs::read(input_midi_filename).unwrap(); 53 | println!("Parsing midi input file."); 54 | let smf = Smf::parse(&input_midi_data).unwrap(); 55 | let mut output_buffer = AudioChunk::::new(2); 56 | let audio_buffer_writer = AudioBufferWriter::new(&mut output_buffer); 57 | let mut plugin = NoisePlayer::new(); 58 | let buffer_size_in_frames = 256; // Quite arbitrarily. 59 | 60 | let number_of_seconds = 2; 61 | let audio_in = AudioDummy::with_sample_rate_and_length( 62 | samplerate, 63 | number_of_seconds * samplerate as usize, 64 | ); 65 | let midi_event_reader = MidlyMidiReader::new(&smf).unwrap(); 66 | let midi_out = MidiDummy::new(); 67 | println!("Rendering {} tracks of audio.", number_of_seconds); 68 | run( 69 | &mut plugin, 70 | buffer_size_in_frames, 71 | audio_in, 72 | audio_buffer_writer, 73 | midi_event_reader, 74 | midi_out, 75 | ) 76 | .unwrap(); 77 | 78 | // Now output_buffer contains the data. 79 | let output_data_interlaced = output_buffer 80 | .interlaced() 81 | .map(|s| (s * (i16::MAX as f32)) as i16) 82 | .collect(); 83 | let header = Header::new(1, 2, samplerate, 16); 84 | let track = BitDepth::Sixteen(output_data_interlaced); 85 | 86 | println!("Opening output file."); 87 | let output_wav_filename = args[2].clone(); 88 | let mut output_file = OpenOptions::new() 89 | .write(true) 90 | .create_new(true) 91 | .open(output_wav_filename) 92 | .unwrap(); 93 | println!("Writing to output file."); 94 | // Note: normally you will probably want to use a buffered writer. 95 | write(header, &track, &mut output_file).unwrap(); 96 | } 97 | } 98 | 99 | #[cfg(not(all( 100 | feature = "backend-combined-midly-0-5", 101 | feature = "backend-combined-wav-0-6" 102 | )))] 103 | fn main() { 104 | println!("This example was compiled without support for midly and wav."); 105 | println!("Compile with passing `--backend-combined-midly-0-5,backend-combined-wav-0-6`"); 106 | println!("as parameter to `cargo`."); 107 | } 108 | -------------------------------------------------------------------------------- /examples/vst_synth.rs: -------------------------------------------------------------------------------- 1 | // An example of a software synthesizer using the JACK back-end. 2 | // The code that is shared between all backends is in the `example_synth` file. 3 | // 4 | // Compiling 5 | // ========= 6 | // You can compile this example with 7 | // ``` 8 | // cargo build --release --examples --features backend-vst 9 | // ``` 10 | // This generates a library that you can find 11 | // 12 | // * under `target/release/examples/libvst_synth.so` for linux 13 | // * in the `target/release/examples/` folder for other operating systems. 14 | // 15 | // Running 16 | // ======= 17 | // 18 | // ## Under Linux 19 | // Copy the `.so` file to a folder that is in the `VST_PATH` environment variable. 20 | // 21 | // ## Under Windows 22 | // TODO 23 | // 24 | // ## Under MacOs 25 | // TODO 26 | // Note: the `rust-vst` repository contains a file `osx_vst_bundler.sh`, you will probably 27 | // need this. 28 | // 29 | // ## Logging 30 | // In order to enable logging, set the environment variable `RSYNTH_LOG_LEVEL` to 31 | // one of the supported log levels. 32 | // Recognized log levels are: 'off', 'error', 'warning', 'info', 'debug' and 'trace'. 33 | // 34 | // You can set the environment variable `RSYNTH_LOG_FILE` to the file name of the file in which 35 | // you want to log. 36 | // 37 | // Note that these environment variables need to be visible to the host. 38 | // Note that the example is also logging to a file in the realtime thread, which may cause clipping. 39 | 40 | #[cfg(feature = "backend-vst")] 41 | #[macro_use] 42 | extern crate vst; 43 | #[macro_use] 44 | extern crate log; 45 | extern crate num_traits; 46 | extern crate rand; 47 | #[macro_use] 48 | extern crate rsynth; 49 | 50 | mod example_synth; 51 | use example_synth::*; 52 | 53 | #[cfg(feature = "backend-vst")] 54 | use rsynth::backend::vst_backend::VstPluginMeta; 55 | 56 | #[cfg(feature = "backend-vst")] 57 | use vst::plugin::Category; 58 | 59 | #[cfg(feature = "backend-vst")] 60 | impl VstPluginMeta for NoisePlayer { 61 | fn plugin_id(&self) -> i32 { 62 | 123 63 | } 64 | fn category(&self) -> Category { 65 | Category::Synth 66 | } 67 | } 68 | 69 | #[rustfmt::skip::macros(vst_init)] 70 | #[cfg(feature = "backend-vst")] 71 | vst_init!( 72 | fn init() -> NoisePlayer { 73 | NoisePlayer::new() 74 | } 75 | ); 76 | -------------------------------------------------------------------------------- /src/backend/combined/dummy.rs: -------------------------------------------------------------------------------- 1 | //! Dummy backend that does nothing, useful for testing. 2 | use super::{AudioReader, AudioWriter, MidiWriter}; 3 | use crate::buffer::{AudioBufferIn, AudioBufferOut}; 4 | use crate::event::{DeltaEvent, RawMidiEvent}; 5 | use core::cmp; 6 | use std::marker::PhantomData; 7 | 8 | /// Dummy backend that does nothing, useful for testing and e.g. for offline renderers 9 | /// that have no audio input or output. 10 | pub struct AudioDummy { 11 | _phantom: PhantomData, 12 | frames_per_second: u32, 13 | length_in_frames: usize, 14 | } 15 | 16 | impl AudioDummy { 17 | /// Create a new `AudioDummy` with the given sample rate, in frames per second. 18 | pub fn with_sample_rate_and_length(frames_per_second: u32, length_in_frames: usize) -> Self { 19 | Self { 20 | frames_per_second, 21 | length_in_frames, 22 | _phantom: PhantomData, 23 | } 24 | } 25 | 26 | #[deprecated(since = "0.1.2", note = "Use `with_sample_rate_and_length` instead.")] 27 | /// Create a new `AudioDummy` with the "default" sample rate 28 | /// of 44100 frames per second (CD quality) and a length of 0 samples. 29 | pub fn new() -> Self { 30 | Self::with_sample_rate_and_length(44100, 0) 31 | } 32 | } 33 | 34 | impl AudioReader for AudioDummy 35 | where 36 | S: Copy, 37 | { 38 | type Err = std::convert::Infallible; 39 | fn number_of_channels(&self) -> usize { 40 | 0 41 | } 42 | 43 | fn frames_per_second(&self) -> u64 { 44 | self.frames_per_second as u64 45 | } 46 | 47 | fn fill_buffer(&mut self, output: &mut AudioBufferOut) -> Result { 48 | let number_of_frames_written = cmp::min(self.length_in_frames, output.number_of_frames()); 49 | self.length_in_frames -= number_of_frames_written; 50 | Ok(number_of_frames_written) 51 | } 52 | } 53 | 54 | impl AudioWriter for AudioDummy 55 | where 56 | S: Copy, 57 | { 58 | type Err = std::convert::Infallible; 59 | fn write_buffer(&mut self, _buffer: &AudioBufferIn) -> Result<(), Self::Err> { 60 | Ok(()) 61 | } 62 | } 63 | 64 | pub struct MidiDummy {} 65 | 66 | impl MidiDummy { 67 | pub fn new() -> Self { 68 | MidiDummy {} 69 | } 70 | } 71 | 72 | impl Iterator for MidiDummy { 73 | type Item = DeltaEvent; 74 | 75 | fn next(&mut self) -> Option { 76 | None 77 | } 78 | } 79 | 80 | impl MidiWriter for MidiDummy { 81 | fn write_event(&mut self, _event: DeltaEvent) {} 82 | } 83 | -------------------------------------------------------------------------------- /src/backend/combined/hound.rs: -------------------------------------------------------------------------------- 1 | //! Backend for reading and writing `.wav` files, based on the `hound` crate. 2 | use super::{AudioReader, AudioWriter}; 3 | use crate::buffer::{AudioBufferIn, AudioBufferOut}; 4 | use dasp_sample::conv::{FromSample, ToSample}; 5 | use hound::{WavReader, WavSamples, WavWriter}; 6 | use std::error::Error; 7 | use std::fmt::{Display, Formatter}; 8 | use std::io::{Read, Seek, Write}; 9 | 10 | pub struct HoundAudioReader<'wr, S> 11 | where 12 | S: FromSample + FromSample + FromSample, 13 | { 14 | hound_sample_reader: Box + 'wr>, 15 | number_of_channels: usize, 16 | frames_per_second: u64, 17 | } 18 | 19 | #[derive(Debug)] 20 | pub enum HoundAudioError { 21 | UnsupportedAudioFormat, 22 | } 23 | 24 | impl Display for HoundAudioError { 25 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 26 | write!(f, "Unsupported audio format") 27 | } 28 | } 29 | 30 | impl Error for HoundAudioError { 31 | fn source(&self) -> Option<&(dyn Error + 'static)> { 32 | None 33 | } 34 | } 35 | 36 | impl<'wr, S> HoundAudioReader<'wr, S> 37 | where 38 | S: FromSample + FromSample + FromSample, 39 | { 40 | fn reader( 41 | r: &'wr mut WavReader, 42 | ) -> Result + 'wr>, HoundAudioError> { 43 | let spec = r.spec(); 44 | Ok(match spec.sample_format { 45 | hound::SampleFormat::Float => match spec.bits_per_sample { 46 | 32 => Box::new(F32SampleReader { 47 | samples: r.samples(), 48 | }), 49 | _ => { 50 | return Err(HoundAudioError::UnsupportedAudioFormat); 51 | } 52 | }, 53 | hound::SampleFormat::Int => match spec.bits_per_sample { 54 | 24 | 32 => Box::new(I32SampleReader { 55 | samples: r.samples(), 56 | }), 57 | 8 | 16 => Box::new(I16SampleReader { 58 | samples: r.samples(), 59 | }), 60 | _ => { 61 | // Note: until 3.4.0, Hound only supports 8, 16, 24, 32 bits/sample. 62 | // Something else (e.g. 12 bits) would result in an error at runtime, 63 | // so it does not make sense to allow this at this point. 64 | return Err(HoundAudioError::UnsupportedAudioFormat); 65 | } 66 | }, 67 | }) 68 | } 69 | 70 | pub fn new(reader: &'wr mut WavReader) -> Result { 71 | let spec = reader.spec(); 72 | 73 | let number_of_channels = spec.channels as usize; 74 | let hound_sample_reader = Self::reader(reader)?; 75 | Ok(Self { 76 | number_of_channels, 77 | frames_per_second: spec.sample_rate as u64, 78 | hound_sample_reader, 79 | }) 80 | } 81 | } 82 | 83 | impl<'wr, S> AudioReader for HoundAudioReader<'wr, S> 84 | where 85 | S: Copy + FromSample + FromSample + FromSample, 86 | { 87 | type Err = hound::Error; 88 | 89 | fn number_of_channels(&self) -> usize { 90 | self.number_of_channels 91 | } 92 | 93 | fn frames_per_second(&self) -> u64 { 94 | self.frames_per_second 95 | } 96 | 97 | fn fill_buffer(&mut self, outputs: &mut AudioBufferOut) -> Result { 98 | assert_eq!(outputs.number_of_channels(), self.number_of_channels()); 99 | let length = outputs.number_of_frames(); 100 | let mut frame_index = 0; 101 | while frame_index < length { 102 | for output in outputs.channel_iter_mut() { 103 | if let Some(sample) = self.hound_sample_reader.read_sample()? { 104 | output[frame_index] = sample; 105 | } else { 106 | return Ok(frame_index); 107 | } 108 | } 109 | frame_index += 1; 110 | } 111 | Ok(frame_index) 112 | } 113 | } 114 | 115 | trait HoundSampleReader { 116 | fn read_sample(&mut self) -> Result, hound::Error>; 117 | } 118 | 119 | struct F32SampleReader<'wr, R: Read> { 120 | samples: WavSamples<'wr, R, f32>, 121 | } 122 | 123 | impl<'wr, R: Read, S> HoundSampleReader for F32SampleReader<'wr, R> 124 | where 125 | S: FromSample, 126 | { 127 | fn read_sample(&mut self) -> Result, hound::Error> { 128 | if let Some(n) = self.samples.next() { 129 | Ok(Some(S::from_sample_(n?))) 130 | } else { 131 | Ok(None) 132 | } 133 | } 134 | } 135 | 136 | struct I32SampleReader<'wr, R: Read> { 137 | samples: WavSamples<'wr, R, i32>, 138 | } 139 | 140 | impl<'wr, R: Read, S> HoundSampleReader for I32SampleReader<'wr, R> 141 | where 142 | S: FromSample, 143 | { 144 | fn read_sample(&mut self) -> Result, hound::Error> { 145 | if let Some(n) = self.samples.next() { 146 | Ok(Some(S::from_sample_(n?))) 147 | } else { 148 | Ok(None) 149 | } 150 | } 151 | } 152 | 153 | struct I16SampleReader<'wr, R: Read> { 154 | samples: WavSamples<'wr, R, i16>, 155 | } 156 | 157 | impl<'wr, R: Read, S> HoundSampleReader for I16SampleReader<'wr, R> 158 | where 159 | S: FromSample, 160 | { 161 | fn read_sample(&mut self) -> Result, hound::Error> { 162 | if let Some(n) = self.samples.next() { 163 | Ok(Some(S::from_sample_(n?))) 164 | } else { 165 | Ok(None) 166 | } 167 | } 168 | } 169 | 170 | pub struct HoundAudioWriter<'ww, S> 171 | where 172 | S: ToSample + ToSample + ToSample, 173 | { 174 | hound_sample_writer: Box + 'ww>, 175 | number_of_channels: usize, 176 | } 177 | 178 | impl<'ww, S> HoundAudioWriter<'ww, S> 179 | where 180 | S: ToSample + ToSample + ToSample, 181 | { 182 | fn hound_sample_writer( 183 | writer: &'ww mut WavWriter, 184 | ) -> Result + 'ww>, HoundAudioError> { 185 | let spec = writer.spec(); 186 | Ok(match spec.sample_format { 187 | hound::SampleFormat::Float => match spec.bits_per_sample { 188 | 32 => Box::new(F32SampleWriter { writer }), 189 | _ => { 190 | return Err(HoundAudioError::UnsupportedAudioFormat); 191 | } 192 | }, 193 | hound::SampleFormat::Int => match spec.bits_per_sample { 194 | 22 | 32 => Box::new(I32SampleWriter { writer }), 195 | 8 | 16 => Box::new(I16SampleWriter { writer }), 196 | _ => { 197 | // Note: until 3.4.0, Hound only supports 8, 16, 24, 32 bits/sample. 198 | // Something else (e.g. 12 bits) would result in an error while writing 199 | // a sample, so it does not make sense to allow this at this point. 200 | return Err(HoundAudioError::UnsupportedAudioFormat); 201 | } 202 | }, 203 | }) 204 | } 205 | 206 | pub fn new(writer: &'ww mut WavWriter) -> Result { 207 | let spec = writer.spec(); 208 | let hound_sample_writer = Self::hound_sample_writer(writer)?; 209 | Ok(Self { 210 | hound_sample_writer, 211 | number_of_channels: spec.channels as usize, 212 | }) 213 | } 214 | } 215 | 216 | impl<'ww, S> AudioWriter for HoundAudioWriter<'ww, S> 217 | where 218 | S: ToSample + ToSample + ToSample + Copy, 219 | { 220 | type Err = hound::Error; 221 | 222 | fn write_buffer(&mut self, inputs: &AudioBufferIn) -> Result<(), Self::Err> { 223 | assert_eq!(inputs.number_of_channels(), self.number_of_channels); 224 | assert!(self.number_of_channels > 0); 225 | let length = inputs.number_of_frames(); 226 | 227 | let mut frame_index = 0; 228 | while frame_index < length { 229 | for input in inputs.channels().iter() { 230 | self.hound_sample_writer.write_sample(input[frame_index])?; 231 | } 232 | frame_index += 1; 233 | } 234 | 235 | self.hound_sample_writer.flush() 236 | } 237 | 238 | fn specifies_number_of_channels(&self) -> bool { 239 | true 240 | } 241 | 242 | fn number_of_channels(&self) -> usize { 243 | self.number_of_channels 244 | } 245 | } 246 | 247 | trait HoundSampleWriter { 248 | fn write_sample(&mut self, sample: S) -> Result<(), hound::Error>; 249 | fn flush(&mut self) -> Result<(), hound::Error>; 250 | } 251 | 252 | struct F32SampleWriter<'ww, W> 253 | where 254 | W: Write + Seek, 255 | { 256 | writer: &'ww mut WavWriter, 257 | } 258 | 259 | impl<'ww, S, W> HoundSampleWriter for F32SampleWriter<'ww, W> 260 | where 261 | S: ToSample, 262 | W: Write + Seek, 263 | { 264 | fn write_sample(&mut self, sample: S) -> Result<(), hound::Error> { 265 | self.writer.write_sample::(sample.to_sample_()) 266 | } 267 | fn flush(&mut self) -> Result<(), hound::Error> { 268 | self.writer.flush() 269 | } 270 | } 271 | 272 | struct I32SampleWriter<'ww, W> 273 | where 274 | W: Write + Seek, 275 | { 276 | writer: &'ww mut WavWriter, 277 | } 278 | 279 | impl<'ww, S, W> HoundSampleWriter for I32SampleWriter<'ww, W> 280 | where 281 | S: ToSample, 282 | W: Write + Seek, 283 | { 284 | fn write_sample(&mut self, sample: S) -> Result<(), hound::Error> { 285 | self.writer.write_sample::(sample.to_sample_()) 286 | } 287 | 288 | fn flush(&mut self) -> Result<(), hound::Error> { 289 | self.writer.flush() 290 | } 291 | } 292 | 293 | struct I16SampleWriter<'ww, W> 294 | where 295 | W: Write + Seek, 296 | { 297 | writer: &'ww mut WavWriter, 298 | } 299 | 300 | impl<'ww, S, W> HoundSampleWriter for I16SampleWriter<'ww, W> 301 | where 302 | S: ToSample, 303 | W: Write + Seek, 304 | { 305 | fn write_sample(&mut self, sample: S) -> Result<(), hound::Error> { 306 | self.writer.write_sample::(sample.to_sample_()) 307 | } 308 | 309 | fn flush(&mut self) -> Result<(), hound::Error> { 310 | self.writer.flush() 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/backend/combined/memory.rs: -------------------------------------------------------------------------------- 1 | //! In-memory backend, useful for testing. 2 | use super::{AudioReader, AudioWriter}; 3 | use crate::buffer::{AudioBufferIn, AudioBufferOut, AudioChunk}; 4 | #[cfg(feature = "dasp_sample")] 5 | // Re-exports from the `dasp-sample` crate 6 | pub mod dasp_sample { 7 | pub use dasp_sample::*; 8 | } 9 | use std::borrow::Borrow; 10 | use std::marker::PhantomData; 11 | 12 | /// An [`AudioReader`] that reads from a given [`AudioChunk`]. 13 | /// The generic parameter type `S` represents the sample type. 14 | /// 15 | #[cfg_attr( 16 | feature = "backend-combined-wav-0-6", 17 | doc = "\ 18 | # Example 19 | 20 | The following example illustrates how an [`AudioChunk`] can be generated from a wav file. 21 | 22 | _Remark_ the example assumes the `rsynth` crate is compiled with the `backend-combined-wav-0-6` feature. 23 | 24 | _Remark_ the example does not use proper error handling. 25 | ``` 26 | use std::fs::File; 27 | use std::path::Path; 28 | use rsynth::backend::combined::memory::{AudioChunkReader, wav_0_6::read}; 29 | 30 | fn create_reader_from_file(filename: &str) { 31 | let mut file = File::open(Path::new(filename)).unwrap(); 32 | let (header, samples) = read(&mut file).unwrap(); 33 | let reader = AudioChunkReader::::from((header, samples)); 34 | } 35 | ``` 36 | " 37 | )] 38 | /// 39 | /// [`AudioReader`]: ../trait.AudioReader.html 40 | /// [`AudioChunk`]: ../../../buffer/struct.AudioChunk.html 41 | pub struct AudioChunkReader 42 | where 43 | T: Borrow>, 44 | S: Copy, 45 | { 46 | frames_per_second: u64, 47 | frame: usize, 48 | chunk: T, 49 | phantom: PhantomData, 50 | } 51 | 52 | impl AudioChunkReader 53 | where 54 | T: Borrow>, 55 | S: Copy, 56 | { 57 | /// Construct a new `AudioChunkReader` with the given [`AudioChunk`] and 58 | /// sample rate in frames per second. 59 | /// 60 | /// [`AudioChunk`]: ../../../buffer/struct.AudioChunk.html 61 | pub fn new(buffer: T, frames_per_second: u64) -> Self { 62 | Self { 63 | chunk: buffer, 64 | frames_per_second, 65 | frame: 0, 66 | phantom: PhantomData::, 67 | } 68 | } 69 | } 70 | 71 | impl AudioReader for AudioChunkReader 72 | where 73 | T: Borrow>, 74 | S: Copy, 75 | { 76 | type Err = std::convert::Infallible; 77 | fn number_of_channels(&self) -> usize { 78 | self.chunk.borrow().channels().len() 79 | } 80 | fn frames_per_second(&self) -> u64 { 81 | self.frames_per_second 82 | } 83 | 84 | fn fill_buffer(&mut self, output: &mut AudioBufferOut) -> Result { 85 | assert_eq!(output.number_of_channels(), self.number_of_channels()); 86 | // Note: `self.number_of_channels() > 0` 87 | let buffer_size = output.number_of_frames(); 88 | let remainder = self.chunk.borrow().channels()[0].len() - self.frame; 89 | let frames_to_copy = std::cmp::min(buffer_size, remainder); 90 | 91 | for (output_channel, input_channel) in output 92 | .channel_iter_mut() 93 | .zip(self.chunk.borrow().channels().iter()) 94 | { 95 | assert_eq!(buffer_size, output_channel.len()); 96 | output_channel[0..frames_to_copy] 97 | .copy_from_slice(&input_channel[self.frame..self.frame + frames_to_copy]); 98 | } 99 | self.frame += frames_to_copy; 100 | Ok(frames_to_copy) 101 | } 102 | } 103 | 104 | /// An [`AudioReader`] that reads from a given [`AudioChunk`]. 105 | /// The generic parameter type `S` represents the sample type. 106 | /// 107 | /// [`AudioReader`]: ../trait.AudioReader.html 108 | /// [`AudioChunk`]: ../../../buffer/struct.AudioChunk.html 109 | pub type AudioBufferReader<'b, S> = AudioChunkReader>; 110 | 111 | #[cfg(test)] 112 | mod AudioBufferReaderTests { 113 | mod fill_buffer { 114 | use super::super::super::AudioReader; 115 | use super::super::AudioBufferReader; 116 | use crate::buffer::{AudioBufferOut, AudioChunk}; 117 | 118 | #[test] 119 | fn works_as_expected() { 120 | let audio_buffer = 121 | audio_chunk![[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]; 122 | let mut reader = AudioBufferReader::new(&audio_buffer, 16); 123 | let mut output_buffer = AudioChunk::zero(3, 2); 124 | let mut slices = output_buffer.as_mut_slices(); 125 | { 126 | let mut buffers = AudioBufferOut::new(&mut slices, 2); 127 | assert_eq!(Ok(2), reader.fill_buffer(&mut buffers)); 128 | } 129 | assert_eq!(slices[0], vec![1, 2].as_slice()); 130 | assert_eq!(slices[1], vec![6, 7].as_slice()); 131 | assert_eq!(slices[2], vec![11, 12].as_slice()); 132 | { 133 | let mut buffers = AudioBufferOut::new(&mut slices, 2); 134 | assert_eq!(Ok(2), reader.fill_buffer(&mut buffers)); 135 | } 136 | assert_eq!(slices[0], vec![3, 4].as_slice()); 137 | assert_eq!(slices[1], vec![8, 9].as_slice()); 138 | assert_eq!(slices[2], vec![13, 14].as_slice()); 139 | { 140 | let mut buffers = AudioBufferOut::new(&mut slices, 2); 141 | assert_eq!(Ok(1), reader.fill_buffer(&mut buffers)); 142 | } 143 | assert_eq!(slices[0], vec![5, 4].as_slice()); 144 | assert_eq!(slices[1], vec![10, 9].as_slice()); 145 | assert_eq!(slices[2], vec![15, 14].as_slice()); 146 | } 147 | } 148 | } 149 | 150 | #[cfg(feature = "backend-combined-wav-0-6")] 151 | pub mod wav_0_6 { 152 | /// Re-exports from we `wav` crate (version range 0.6.x). 153 | pub use wav_0_6::*; 154 | 155 | use super::dasp_sample::{FromSample, I24}; 156 | use super::{AudioChunk, AudioChunkReader}; 157 | use std::marker::PhantomData; 158 | 159 | impl From<(Header, BitDepth)> for AudioChunkReader> 160 | where 161 | S: Copy + FromSample + FromSample + FromSample + FromSample, 162 | { 163 | fn from((header, samples): (Header, BitDepth)) -> Self { 164 | let chunk = match samples { 165 | BitDepth::Eight(s) => AudioChunk::from_interlaced_iterator( 166 | s.iter().map(|a| S::from_sample_(*a)), 167 | header.channel_count as usize, 168 | ), 169 | BitDepth::Sixteen(s) => AudioChunk::from_interlaced_iterator( 170 | s.iter().map(|a| S::from_sample_(*a)), 171 | header.channel_count as usize, 172 | ), 173 | BitDepth::TwentyFour(s) => AudioChunk::from_interlaced_iterator( 174 | s.iter().map(|a| { 175 | S::from_sample_(I24::new(*a).expect("24 bits sample should be 24 bits")) 176 | }), 177 | header.channel_count as usize, 178 | ), 179 | BitDepth::ThirtyTwoFloat(s) => AudioChunk::from_interlaced_iterator( 180 | s.iter().map(|a| S::from_sample_(*a)), 181 | header.channel_count as usize, 182 | ), 183 | BitDepth::Empty => AudioChunk::new(header.channel_count as usize), 184 | }; 185 | Self { 186 | frames_per_second: header.sampling_rate as u64, 187 | frame: 0, 188 | chunk, 189 | phantom: PhantomData, 190 | } 191 | } 192 | } 193 | } 194 | 195 | /// An [`AudioWriter`] that appends to a given [`AudioChunk`]. 196 | /// The generic parameter type `S` represents the sample type. 197 | /// 198 | /// Note about using in a real-time context 199 | /// ======================================= 200 | /// Because this appends to an [`AudioChunk`], it may allocate memory 201 | /// when the capacity of the [`AudioChunk`] is exceeded. 202 | /// 203 | /// [`AudioWriter`]: ../trait.AudioWriter.html 204 | /// [`AudioChunk`]: ../../../buffer/struct.AudioChunk.html 205 | pub struct AudioBufferWriter<'b, S> { 206 | buffer: &'b mut AudioChunk, 207 | } 208 | 209 | impl<'b, S> AudioBufferWriter<'b, S> { 210 | pub fn new(buffer: &'b mut AudioChunk) -> Self { 211 | Self { buffer } 212 | } 213 | } 214 | 215 | impl<'b, S> AudioWriter for AudioBufferWriter<'b, S> 216 | where 217 | S: Copy, 218 | { 219 | type Err = std::convert::Infallible; 220 | fn write_buffer(&mut self, buffer: &AudioBufferIn) -> Result<(), Self::Err> { 221 | self.buffer.append_sliced_chunk(buffer.channels()); 222 | Ok(()) 223 | } 224 | 225 | fn specifies_number_of_channels(&self) -> bool { 226 | true 227 | } 228 | 229 | fn number_of_channels(&self) -> usize { 230 | self.buffer.number_of_channels() 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/backend/combined/midly.rs: -------------------------------------------------------------------------------- 1 | //! Read midi files. 2 | use super::MICROSECONDS_PER_SECOND; 3 | use crate::event::{DeltaEvent, RawMidiEvent, TimeStretcher}; 4 | 5 | /// Re-exports from the `midly` crate. 6 | pub mod midly_0_5 { 7 | pub use midly_0_5::*; 8 | } 9 | 10 | use self::midly_0_5::Timing; 11 | #[cfg(test)] 12 | use self::midly_0_5::{ 13 | num::{u15, u24, u28, u4, u7}, 14 | Format, Header, MidiMessage, Track, TrackEvent, 15 | }; 16 | use self::midly_0_5::{MetaMessage, TrackEventKind}; 17 | use crate::backend::combined::midly::midly_0_5::Smf; 18 | use itertools::Itertools; 19 | use std::convert::TryFrom; 20 | use std::num::NonZeroU64; 21 | 22 | const SECONDS_PER_MINUTE: u64 = 60; 23 | const MICROSECONDS_PER_MINUTE: u64 = SECONDS_PER_MINUTE * MICROSECONDS_PER_SECOND; 24 | const DEFAULT_BEATS_PER_MINUTE: u64 = 120; 25 | 26 | /// Read from midi events as parsed by the `midly` crate. 27 | pub struct MidlyMidiReader<'a, 'b> { 28 | event_iter: Box)> + 'b>, 29 | timestretcher: TimeStretcher, 30 | previous_time_in_microseconds: u64, 31 | ticks_per_beat: NonZeroU64, 32 | } 33 | 34 | impl<'a, 'b> MidlyMidiReader<'a, 'b> 35 | where 36 | 'b: 'a, 37 | { 38 | /// Create a new `MidlyMidiReader` that will read all tracks together (interleaved). 39 | pub fn new(smf: &'b Smf<'a>) -> Result { 40 | let track_mask: Vec<_> = smf.tracks.iter().map(|_| true).collect(); 41 | Self::new_with_track_mask(smf, &track_mask) 42 | } 43 | 44 | /// Create a new `MidlyMidiReader` that will read only the masked tracks (interleaved). 45 | /// 46 | /// # Parameters 47 | /// `smf`: the [`Smf`] for reading the midi file 48 | /// `track_mask`: a slice of booleans, only the tracks that correspond to `true` will be read. 49 | pub fn new_with_track_mask(smf: &'b Smf<'a>, track_mask: &[bool]) -> Result { 50 | let mut event_iter: Box)> + 'b> = 51 | Box::new(Vec::new().into_iter()); 52 | for (must_include, track) in track_mask.iter().zip(smf.tracks.iter()) { 53 | if *must_include { 54 | let mut offset = 0; 55 | let iter = track.iter().map(move |e| { 56 | offset += e.delta.as_int() as u64; 57 | (offset, e.kind) 58 | }); 59 | event_iter = Box::new(event_iter.merge_by(iter, |(t1, _), (t2, _)| t1 < t2)); 60 | } 61 | } 62 | let ticks_per_beat = match smf.header.timing { 63 | Timing::Metrical(t) => NonZeroU64::new(t.as_int() as u64).ok_or(())?, 64 | Timing::Timecode(_, _) => return Err(()), 65 | }; 66 | // ticks * microseconds_per_beat 67 | // microseconds = ----------------------------------- 68 | // ticks_per_beat 69 | let timestretcher = TimeStretcher::new( 70 | MICROSECONDS_PER_MINUTE / DEFAULT_BEATS_PER_MINUTE, 71 | ticks_per_beat, 72 | ); 73 | Ok(Self { 74 | ticks_per_beat, 75 | event_iter, 76 | previous_time_in_microseconds: 0, 77 | timestretcher, 78 | }) 79 | } 80 | } 81 | 82 | impl<'a, 'b> Iterator for MidlyMidiReader<'a, 'b> { 83 | type Item = DeltaEvent; 84 | 85 | fn next(&mut self) -> Option { 86 | loop { 87 | let (t, event) = self.event_iter.next()?; 88 | let new_factor = if let TrackEventKind::Meta(MetaMessage::Tempo(tempo)) = event { 89 | Some((tempo.as_int() as u64, self.ticks_per_beat)) 90 | } else { 91 | None 92 | }; 93 | let time = self.timestretcher.stretch(t, new_factor); 94 | if let TrackEventKind::Midi { .. } = event { 95 | if let Ok(e) = RawMidiEvent::try_from(event) { 96 | let difference = time - self.previous_time_in_microseconds; 97 | self.previous_time_in_microseconds = time; 98 | return Some(DeltaEvent { 99 | microseconds_since_previous_event: difference, 100 | event: e, 101 | }); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | #[test] 109 | pub fn iterator_correctly_returns_one_event() { 110 | // 120 beats per minute 111 | // = 120 beats per 60 seconds 112 | // = 120 beats per 60 000 000 microseconds 113 | // so the tempo is 114 | // 60 000 000 / 120 microseconds per beat 115 | // = 10 000 000 / 20 microseconds per beat 116 | // = 500 000 microseconds per beat 117 | let tempo_in_microseconds_per_beat = 500000; 118 | let ticks_per_beat = 32; 119 | // One event after 1 second. 120 | // One second corresponds to two beats, so to 64 ticks. 121 | let event_time_in_ticks = 64; 122 | let events = vec![ 123 | TrackEvent { 124 | delta: u28::from(0), 125 | kind: TrackEventKind::Meta(MetaMessage::Tempo(u24::from( 126 | tempo_in_microseconds_per_beat, 127 | ))), 128 | }, 129 | TrackEvent { 130 | delta: u28::from(event_time_in_ticks), 131 | kind: TrackEventKind::Midi { 132 | channel: u4::from(0), 133 | message: MidiMessage::NoteOn { 134 | key: u7::from(60), 135 | vel: u7::from(90), 136 | }, 137 | }, 138 | }, 139 | ]; 140 | let tracks = vec![events]; 141 | let header = Header { 142 | timing: Timing::Metrical(u15::from(ticks_per_beat)), 143 | format: Format::SingleTrack, 144 | }; 145 | let smf = Smf { header, tracks }; 146 | let mut mr = MidlyMidiReader::new(&smf).expect("No errors should occur now."); 147 | let observed = mr.next().expect("MidlyMidiReader should return one event."); 148 | assert_eq!(observed.microseconds_since_previous_event, 1000000); 149 | assert_eq!(mr.next(), None); 150 | } 151 | 152 | #[cfg(test)] 153 | pub fn iterator_correctly_returns_two_events() { 154 | // 120 beats per minute 155 | // = 120 beats per 60 seconds 156 | // = 120 beats per 60 000 000 microseconds 157 | // so the tempo is 158 | // 60 000 000 / 120 microseconds per beat 159 | // = 10 000 000 / 20 microseconds per beat 160 | // = 500 000 microseconds per beat 161 | let tempo_in_microseconds_per_beat = 500000; 162 | let ticks_per_beat = 32; 163 | // One event after 1 second. 164 | // One second corresponds to two beats, so to 64 ticks. 165 | let event_delta_time_in_ticks = 64; 166 | let events = vec![ 167 | TrackEvent { 168 | delta: u28::from(0), 169 | kind: TrackEventKind::Meta(MetaMessage::Tempo(u24::from( 170 | tempo_in_microseconds_per_beat, 171 | ))), 172 | }, 173 | TrackEvent { 174 | delta: u28::from(event_delta_time_in_ticks), 175 | kind: TrackEventKind::Midi { 176 | channel: u4::from(0), 177 | message: MidiMessage::NoteOn { 178 | key: u7::from(60), 179 | vel: u7::from(90), 180 | }, 181 | }, 182 | }, 183 | TrackEvent { 184 | delta: u28::from(event_delta_time_in_ticks), 185 | kind: TrackEventKind::Midi { 186 | channel: u4::from(0), 187 | message: MidiMessage::NoteOn { 188 | key: u7::from(60), 189 | vel: u7::from(90), 190 | }, 191 | }, 192 | }, 193 | ]; 194 | let header = Header { 195 | timing: Timing::Metrical(u15::from(ticks_per_beat)), 196 | format: Format::SingleTrack, 197 | }; 198 | let tracks = vec![events]; 199 | let smf = Smf { header, tracks }; 200 | let mut mr = MidlyMidiReader::new(&smf).expect("No errors should occur now"); 201 | let observed = mr.next().expect("MidlyMidiReader should return one event."); 202 | assert_eq!(observed.microseconds_since_previous_event, 1000000); 203 | let observed = mr 204 | .next() 205 | .expect("MidlyMidiReader should return a second event."); 206 | assert_eq!(observed.microseconds_since_previous_event, 1000000); 207 | assert_eq!(mr.next(), None); 208 | } 209 | -------------------------------------------------------------------------------- /src/backend/jack_backend.rs: -------------------------------------------------------------------------------- 1 | //! Wrapper for the [JACK] backend (behind the `backend-jack` feature). 2 | //! 3 | //! Support is only enabled if you compile with the "backend-jack" feature, see 4 | //! [the cargo reference] for more information on setting cargo features. 5 | //! 6 | //! For an example, see `jack_synth.rs` in the `examples` folder. 7 | //! `examples/example_synth` contains the code that is shared for all backends and 8 | //! `examples/jack_synth.rs` contains the jack-specific code. 9 | //! 10 | //! # Usage 11 | //! See the documentation of the [`run`] function. 12 | //! 13 | //! [JACK]: http://www.jackaudio.org/ 14 | //! [the cargo reference]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section 15 | //! [`run`]: ./fn.run.html 16 | use crate::backend::{HostInterface, Stop}; 17 | use crate::buffer::AudioBufferInOut; 18 | use crate::event::{ 19 | ContextualEventHandler, EventHandler, Indexed, RawMidiEvent, SysExEvent, Timed, 20 | }; 21 | use crate::{ 22 | AudioHandler, CommonAudioPortMeta, CommonMidiPortMeta, CommonPluginMeta, 23 | ContextualAudioRenderer, 24 | }; 25 | use std::io; 26 | use vecstorage::VecStorage; 27 | 28 | /// Re-exports of the [`jack`](https://crates.io/crates/jack) crate. 29 | /// Use this so that your code doesn't break when `rsynth` upgrades its dependency on `jack`. 30 | pub mod jack { 31 | pub use jack::*; 32 | } 33 | 34 | use self::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, ProcessScope, RawMidi}; 35 | use self::jack::{Client, ClientOptions, Control, ProcessHandler}; 36 | 37 | /// Used to communicate with `Jack`. 38 | /// 39 | /// You don't need to instantiate this yourself: it is passed as the `context` 40 | /// parameter to the [`render_audio`] method when using the [`run`] function. 41 | /// 42 | /// [`render_audio`]: ../../trait.ContextualAudioRenderer.html#tymethod.render_buffer 43 | /// [`run`]: ./fn.run.html 44 | pub struct JackHost<'c, 'mp, 'mw> { 45 | client: &'c Client, 46 | midi_out_ports: &'mp mut [jack::MidiWriter<'mw>], 47 | control: jack::Control, 48 | } 49 | 50 | impl<'c, 'mp, 'mw> JackHost<'c, 'mp, 'mw> { 51 | /// Get access to the underlying [`Client`] so that you can use Jack-specific features. 52 | /// 53 | /// ['Client`]: ./jack/struct.Client.html 54 | pub fn client(&self) -> &'c Client { 55 | self.client 56 | } 57 | } 58 | 59 | impl<'c, 'mp, 'mw> HostInterface for JackHost<'c, 'mp, 'mw> { 60 | fn output_initialized(&self) -> bool { 61 | false 62 | } 63 | 64 | fn stop(&mut self) { 65 | self.control = jack::Control::Quit 66 | } 67 | } 68 | 69 | impl<'c, 'mp, 'mw> Stop for JackHost<'c, 'mp, 'mw> {} 70 | 71 | impl<'c, 'mp, 'mw> EventHandler>> for JackHost<'c, 'mp, 'mw> { 72 | fn handle_event(&mut self, event: Indexed>) { 73 | let Indexed { index, event } = event; 74 | if let Some(ref mut midi_out_port) = self.midi_out_ports.get_mut(index).as_mut() { 75 | let raw_midi = RawMidi { 76 | time: event.time_in_frames, 77 | bytes: event.event.bytes(), 78 | }; 79 | midi_out_port.write(&raw_midi); // TODO: error handling. 80 | } else { 81 | error!( 82 | "midi port out of bounds: port index is {}, but only {} ports are available", 83 | index, 84 | self.midi_out_ports.len() 85 | ); 86 | } 87 | } 88 | } 89 | 90 | impl<'c, 'mp, 'mw, 'e> EventHandler>>> for JackHost<'c, 'mp, 'mw> { 91 | fn handle_event(&mut self, event: Indexed>) { 92 | let Indexed { index, event } = event; 93 | if let Some(ref mut midi_out_port) = self.midi_out_ports.get_mut(index).as_mut() { 94 | let raw_midi = RawMidi { 95 | time: event.time_in_frames, 96 | bytes: event.event.data(), 97 | }; 98 | midi_out_port.write(&raw_midi); // TODO: error handling. 99 | } else { 100 | error!( 101 | "midi port out of bounds: port index is {}, but only {} ports are available", 102 | index, 103 | self.midi_out_ports.len() 104 | ); 105 | } 106 | } 107 | } 108 | 109 | fn audio_in_ports

(client: &Client, plugin: &P) -> Vec> 110 | where 111 | P: CommonAudioPortMeta, 112 | { 113 | let mut in_ports = Vec::with_capacity(plugin.max_number_of_audio_inputs()); 114 | for index in 0..plugin.max_number_of_audio_inputs() { 115 | let mut name = String::new(); 116 | if let Err(e) = plugin.input_name(&mut name, index) { 117 | error!( 118 | "Failed to get the name of audio input port with index {}: {}.", 119 | index, e 120 | ); 121 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" audio input port? 122 | continue; 123 | } 124 | info!("Registering audio input port with name {}", name); 125 | let port = client.register_port(&name, AudioIn::default()); 126 | match port { 127 | Ok(p) => { 128 | in_ports.push(p); 129 | } 130 | Err(e) => { 131 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" audio input 132 | // TODO: port that always contains silence? 133 | error!("Failed to open audio input port with index {} and name {}: {:?}. Skipping this port.", index, name, e); 134 | } 135 | } 136 | } 137 | in_ports 138 | } 139 | 140 | fn audio_out_ports

(client: &Client, plugin: &P) -> Vec> 141 | where 142 | P: CommonAudioPortMeta, 143 | { 144 | let mut out_ports = Vec::with_capacity(plugin.max_number_of_audio_outputs()); 145 | for index in 0..plugin.max_number_of_audio_outputs() { 146 | let mut name = String::new(); 147 | if let Err(e) = plugin.output_name(&mut name, index) { 148 | error!( 149 | "Failed to get the name of audio output port with index {}: {}.", 150 | index, e 151 | ); 152 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" audio output port? 153 | continue; 154 | } 155 | info!("Registering audio output port with name {}", name); 156 | let port = client.register_port(&name, AudioOut::default()); 157 | match port { 158 | Ok(p) => { 159 | out_ports.push(p); 160 | } 161 | Err(e) => { 162 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" audio output 163 | // TODO: port that is in fact unused? 164 | error!("Failed to open audio output port with index {} and name {}: {:?}. Skipping this port.", index, name, e); 165 | } 166 | } 167 | } 168 | out_ports 169 | } 170 | 171 | fn midi_in_ports

(client: &Client, plugin: &P) -> Vec> 172 | where 173 | P: CommonMidiPortMeta, 174 | { 175 | let mut in_ports = Vec::with_capacity(plugin.max_number_of_midi_inputs()); 176 | for index in 0..plugin.max_number_of_midi_inputs() { 177 | let mut name = String::new(); 178 | if let Err(e) = plugin.input_name(&mut name, index) { 179 | error!( 180 | "Failed to get the name of midi input port with index {}: {}.", 181 | index, e 182 | ); 183 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" midi input port? 184 | continue; 185 | } 186 | info!("Registering midi input port with name {}", name); 187 | let port = client.register_port(&name, MidiIn::default()); 188 | match port { 189 | Ok(p) => { 190 | in_ports.push(p); 191 | } 192 | Err(e) => { 193 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" midi input port? 194 | error!("Failed to open midi input port with index {} and name {}: {:?}. Skipping this port.", index, name, e); 195 | } 196 | } 197 | } 198 | in_ports 199 | } 200 | 201 | fn midi_out_ports

(client: &Client, plugin: &P) -> Vec> 202 | where 203 | P: CommonMidiPortMeta, 204 | { 205 | let mut out_ports = Vec::with_capacity(plugin.max_number_of_midi_outputs()); 206 | for index in 0..plugin.max_number_of_midi_outputs() { 207 | let mut name = String::new(); 208 | if let Err(e) = plugin.output_name(&mut name, index) { 209 | error!( 210 | "Failed to get the name of midi output port with index {}: {}.", 211 | index, e 212 | ); 213 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" midi output port? 214 | continue; 215 | } 216 | let port = client.register_port(&name, MidiOut::default()); 217 | match port { 218 | Ok(p) => { 219 | out_ports.push(p); 220 | } 221 | Err(e) => { 222 | // TODO: Maybe instead of skipping, it is better to provide a "dummy" midi output port? 223 | error!("Failed to open midi output port with index {} and name {}: {:?}. Skipping this port.", index, name, e); 224 | } 225 | } 226 | } 227 | out_ports 228 | } 229 | 230 | // `MidiWriter` does not implement `Send`, but we do want `JackProcessHandler` to implement `Send`. 231 | // `JackProcessHandler` contains only `VecStorage` of `MidiWriter`s, not a real `MidiWriter`. 232 | // So we solve this by creating a data type that is guaranteed to have the same alignment and 233 | // size as a `MidiWriter`. 234 | struct MidiWriterWrapper { 235 | _inner: jack::MidiWriter<'static>, 236 | } 237 | 238 | unsafe impl Send for MidiWriterWrapper {} 239 | unsafe impl Sync for MidiWriterWrapper {} 240 | 241 | struct JackProcessHandler

{ 242 | audio_in_ports: Vec>, 243 | audio_out_ports: Vec>, 244 | midi_in_ports: Vec>, 245 | midi_out_ports: Vec>, 246 | plugin: P, 247 | inputs: VecStorage<&'static [f32]>, 248 | outputs: VecStorage<&'static [f32]>, 249 | midi_writer: VecStorage, // We cannot use rsor for this one. 250 | } 251 | 252 | impl

JackProcessHandler

253 | where 254 | P: CommonAudioPortMeta + CommonMidiPortMeta + CommonPluginMeta + Send, 255 | for<'c, 'mp, 'mw> P: ContextualAudioRenderer> 256 | + ContextualEventHandler>, JackHost<'c, 'mp, 'mw>>, 257 | for<'c, 'mp, 'mw, 'a> P: 258 | ContextualEventHandler>>, JackHost<'c, 'mp, 'mw>>, 259 | { 260 | fn new(client: &Client, plugin: P) -> Self { 261 | trace!("JackProcessHandler::new()"); 262 | let audio_in_ports = audio_in_ports::

(&client, &plugin); 263 | let audio_out_ports = audio_out_ports::

(&client, &plugin); 264 | 265 | let midi_in_ports = midi_in_ports::

(&client, &plugin); 266 | let midi_out_ports = midi_out_ports::

(&client, &plugin); 267 | 268 | let inputs = VecStorage::with_capacity(plugin.max_number_of_audio_inputs()); 269 | let outputs = VecStorage::with_capacity(plugin.max_number_of_audio_outputs()); 270 | 271 | let midi_writer = VecStorage::with_capacity(plugin.max_number_of_midi_outputs()); 272 | 273 | JackProcessHandler { 274 | audio_in_ports, 275 | audio_out_ports, 276 | midi_in_ports, 277 | midi_out_ports, 278 | plugin, 279 | inputs, 280 | outputs, 281 | midi_writer, 282 | } 283 | } 284 | 285 | fn handle_events<'c, 'mp, 'mw>( 286 | midi_in_ports: &[Port], 287 | plugin: &mut P, 288 | process_scope: &ProcessScope, 289 | jack_host: &mut JackHost<'c, 'mp, 'mw>, 290 | ) { 291 | // No tracing here, because this is called in the `process` function, 292 | // and we do not want to trace that. 293 | for (index, midi_in_port) in midi_in_ports.iter().enumerate() { 294 | trace!("handle_events for input port {}", index); 295 | for input_event in midi_in_port.iter(process_scope) { 296 | trace!("handle_events found event: {:?}", &input_event.bytes); 297 | if input_event.bytes.len() <= 3 { 298 | if let Some(raw_event) = RawMidiEvent::try_new(&input_event.bytes) { 299 | let event = Indexed { 300 | index, 301 | event: Timed { 302 | time_in_frames: input_event.time, 303 | event: raw_event, 304 | }, 305 | }; 306 | plugin.handle_event(event, jack_host); 307 | } else { 308 | warn!( 309 | "Strange event of length {}; ignoring this event.", 310 | input_event.bytes.len() 311 | ); 312 | } 313 | } else { 314 | let event = Indexed { 315 | index, 316 | event: Timed { 317 | time_in_frames: input_event.time, 318 | event: SysExEvent::new(input_event.bytes), 319 | }, 320 | }; 321 | plugin.handle_event(event, jack_host); 322 | } 323 | } 324 | } 325 | } 326 | } 327 | 328 | impl

ProcessHandler for JackProcessHandler

329 | where 330 | P: CommonAudioPortMeta + CommonMidiPortMeta + CommonPluginMeta + Send, 331 | for<'c, 'mp, 'mw> P: ContextualAudioRenderer> 332 | + ContextualEventHandler>, JackHost<'c, 'mp, 'mw>>, 333 | for<'c, 'mp, 'mw, 'a> P: 334 | ContextualEventHandler>>, JackHost<'c, 'mp, 'mw>>, 335 | { 336 | fn process(&mut self, client: &Client, process_scope: &ProcessScope) -> Control { 337 | let mut midi_writer_guard = self.midi_writer.vec_guard(); 338 | for midi_output in self.midi_out_ports.iter_mut() { 339 | midi_writer_guard.push(midi_output.writer(process_scope)); 340 | } 341 | let mut jack_host: JackHost = JackHost { 342 | client, 343 | midi_out_ports: midi_writer_guard.as_mut_slice(), 344 | control: jack::Control::Continue, 345 | }; 346 | Self::handle_events( 347 | &self.midi_in_ports, 348 | &mut self.plugin, 349 | process_scope, 350 | &mut jack_host, 351 | ); 352 | 353 | let mut inputs = self.inputs.vec_guard(); 354 | for port in self.audio_in_ports.iter().take(inputs.capacity()) { 355 | inputs.push(port.as_slice(process_scope)); 356 | } 357 | 358 | let mut outputs = self.outputs.vec_guard(); 359 | for port in self.audio_out_ports.iter_mut().take(outputs.capacity()) { 360 | outputs.push(port.as_mut_slice(process_scope)); 361 | } 362 | 363 | let mut buffer = AudioBufferInOut::new( 364 | inputs.as_slice(), 365 | outputs.as_mut_slice(), 366 | client.buffer_size() as usize, 367 | ); 368 | self.plugin.render_buffer(&mut buffer, &mut jack_host); 369 | jack_host.control 370 | } 371 | } 372 | 373 | /// Run the plugin until the user presses a key on the computer keyboard or the plugin 374 | /// requests the `JackHost` to stop. 375 | pub fn run

(mut plugin: P) -> Result 376 | where 377 | P: CommonPluginMeta 378 | + AudioHandler 379 | + CommonAudioPortMeta 380 | + CommonMidiPortMeta 381 | + Send 382 | + Sync 383 | + 'static, 384 | for<'c, 'mp, 'mw> P: ContextualAudioRenderer>, 385 | for<'c, 'mp, 'mw> P: 386 | ContextualEventHandler>, JackHost<'c, 'mp, 'mw>>, 387 | for<'c, 'mp, 'mw, 'a> P: 388 | ContextualEventHandler>>, JackHost<'c, 'mp, 'mw>>, 389 | { 390 | let (client, _status) = Client::new(plugin.name(), ClientOptions::NO_START_SERVER)?; 391 | 392 | let sample_rate = client.sample_rate(); 393 | plugin.set_sample_rate(sample_rate as f64); 394 | 395 | let jack_process_handler = JackProcessHandler::new(&client, plugin); 396 | let active_client = client.activate_async((), jack_process_handler)?; 397 | 398 | println!("Press any key to quit"); 399 | let mut user_input = String::new(); 400 | io::stdin().read_line(&mut user_input).ok(); 401 | 402 | info!("Deactivating client..."); 403 | 404 | let (_, _, plugin) = active_client.deactivate()?; 405 | return Ok(plugin.plugin); 406 | } 407 | -------------------------------------------------------------------------------- /src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | //! Backends. 2 | //! 3 | //! Pre-defined backends 4 | //! ==================== 5 | //! `rsynth` currently supports the following back-ends: 6 | //! * [`combined`] combine different back-ends for audio input, audio output, midi input and 7 | //! midi output, mostly for offline rendering and testing (behind various features) 8 | //! * [`jack`] (behind the `backend-jack` feature) 9 | //! * [`vst`] (behind the `backend-vst` feature) 10 | //! 11 | //! These backends are currently in the `rsynth` crate, but we may eventually move them to 12 | //! separate crates. 13 | //! 14 | //! Custom backends 15 | //! =============== 16 | //! You can write a backend in a separate crate. If you encounter problems that prevent you 17 | //! from writing your backend in a separate crate (e.g., we have forgotten to 18 | //! mark something as `pub`), let us know by opening an issue. 19 | //! 20 | //! Publishing a backend crate 21 | //! -------------------------- 22 | //! 23 | //! When you publish a backend crate, let us know by opening an issue or pull request 24 | //! so that we can link to it in the documentation of rsynth. 25 | //! 26 | //! [`jack`]: ./jack_backend/index.html 27 | //! [`vst`]: ./vst_backend/index.html 28 | //! [`combined`]: ./combined/index.html 29 | #[cfg(feature = "backend-combined")] 30 | pub mod combined; 31 | #[cfg(feature = "backend-jack")] 32 | pub mod jack_backend; 33 | #[cfg(feature = "backend-vst")] 34 | pub mod vst_backend; 35 | 36 | /// Defines an interface for communicating with the host or server of the backend, 37 | /// e.g. the VST host when using VST or the Jack server when using Jack. 38 | pub trait HostInterface { 39 | /// Return whether the output buffers are zero-initialized. 40 | /// Returns `false` when in doubt. 41 | /// 42 | /// # Example 43 | /// 44 | /// The following example illustrates how `output_initialized()` can be used in 45 | /// combination with the `set` method on `AudioBufferOut` to initialize the output 46 | /// buffers to zero in an implementation of the [`crate::ContextualAudioRenderer`] trait. 47 | /// 48 | /// ``` 49 | /// use rsynth::ContextualAudioRenderer; 50 | /// use rsynth::backend::HostInterface; 51 | /// use rsynth::buffer::AudioBufferInOut; 52 | /// struct MyPlugin { /* ... */ } 53 | /// impl ContextualAudioRenderer for MyPlugin 54 | /// where H: HostInterface 55 | /// { 56 | /// fn render_buffer( 57 | /// &mut self, 58 | /// buffer: &mut AudioBufferInOut, 59 | /// context: &mut H) 60 | /// { 61 | /// if ! context.output_initialized() { 62 | /// buffer.outputs().set(0.0); 63 | /// } 64 | /// // The rest of the audio rendering. 65 | /// } 66 | /// } 67 | /// ``` 68 | /// 69 | /// [`ContextualEventHandler`]: ../event/trait.ContextualEventHandler.html 70 | /// [`rsynth::utilities::zero_init`]: ../utilities/fn.initialize_to_zero.html 71 | fn output_initialized(&self) -> bool; 72 | 73 | /// Stop processing. 74 | /// For backends that do not support stopping, this is a no-op. 75 | /// For back-ends that do support stopping and that implement the `Stop` trait, 76 | /// this stops the processing. 77 | fn stop(&mut self) {} 78 | } 79 | 80 | /// A marker trait that indicates that the backend can be stopped. 81 | /// 82 | /// # Example 83 | /// The following illustrates a plugin that works with backends that support stopping. 84 | /// Based on some condition (`plugin_has_finished`), the plugin can signal to the backend 85 | /// that processing is finished by calling `stop()`. 86 | /// 87 | /// ``` 88 | /// use rsynth::ContextualAudioRenderer; 89 | /// use rsynth::backend::{HostInterface, Stop}; 90 | /// use rsynth::buffer::AudioBufferInOut; 91 | /// struct MyPlugin { /* ... */ } 92 | /// impl ContextualAudioRenderer for MyPlugin 93 | /// where H: HostInterface + Stop 94 | /// { 95 | /// fn render_buffer( 96 | /// &mut self, 97 | /// buffer: &mut AudioBufferInOut, 98 | /// context: &mut H) 99 | /// { 100 | /// let plugin_has_finished = unimplemented!(); 101 | /// if plugin_has_finished { 102 | /// context.stop(); 103 | /// } 104 | /// } 105 | /// } 106 | /// ``` 107 | pub trait Stop: HostInterface {} 108 | -------------------------------------------------------------------------------- /src/backend/vst_backend.rs: -------------------------------------------------------------------------------- 1 | //! Wrapper for the VST backend (behind the `backend-vst` feature). 2 | //! 3 | //! Support is only enabled if you compile with the "backend-vst" feature, see 4 | //! [the cargo reference] for more information on setting cargo features. 5 | //! 6 | //! For an example, see `vst_synth.rs` in the `examples` folder. 7 | //! `examples/example_synth` contains the code that is shared for all backends and 8 | //! `examples/vst_synth.rs` contains the jack-specific code. 9 | //! 10 | //! # Usage 11 | //! See the documentation of the [`vst_init`] macro. 12 | //! 13 | //! [`vst_init`]: ../../macro.vst_init.html 14 | //! [the cargo reference]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section 15 | use crate::backend::HostInterface; 16 | use crate::buffer::AudioBufferInOut; 17 | use crate::event::{ContextualEventHandler, RawMidiEvent, SysExEvent, Timed}; 18 | use crate::{ 19 | AudioHandler, AudioHandlerMeta, CommonAudioPortMeta, CommonPluginMeta, ContextualAudioRenderer, 20 | }; 21 | use core::cmp; 22 | use vecstorage::VecStorage; 23 | 24 | /// Re-exports from the [`vst-rs`](https://github.com/RustAudio/vst-rs) crate. 25 | /// Use this in libraries so that your library does not break when `rsynth` upgrades to another 26 | /// version of the `vst-rs` crate. 27 | pub mod vst { 28 | pub use vst::*; 29 | } 30 | 31 | use self::vst::{ 32 | api::Events, 33 | buffer::AudioBuffer, 34 | channels::ChannelInfo, 35 | event::{Event as VstEvent, MidiEvent as VstMidiEvent, SysExEvent as VstSysExEvent}, 36 | plugin::{Category, HostCallback, Info}, 37 | }; 38 | 39 | /// Define some VST-specific meta-data for a VST plugin. 40 | pub trait VstPluginMeta: CommonPluginMeta + AudioHandlerMeta { 41 | fn plugin_id(&self) -> i32; 42 | fn category(&self) -> Category; 43 | } 44 | 45 | /// A struct used internally by the [`vst_init`] macro. Normally, plugin's do not need to use this. 46 | // //! [`vst_init`]: ../../macro.vst_init.html 47 | pub struct VstPluginWrapper

{ 48 | plugin: P, 49 | host: HostCallback, 50 | inputs_f32: VecStorage<&'static [f32]>, 51 | outputs_f32: VecStorage<&'static [f32]>, 52 | inputs_f64: VecStorage<&'static [f64]>, 53 | outputs_f64: VecStorage<&'static [f64]>, 54 | } 55 | 56 | impl

VstPluginWrapper

57 | where 58 | P: CommonAudioPortMeta 59 | + VstPluginMeta 60 | + AudioHandler 61 | + ContextualEventHandler, HostCallback> 62 | + ContextualAudioRenderer 63 | + ContextualAudioRenderer, 64 | for<'a> P: ContextualEventHandler>, HostCallback>, 65 | { 66 | pub fn get_info(&self) -> Info { 67 | trace!("get_info"); 68 | Info { 69 | name: self.plugin.name().to_string(), 70 | inputs: self.plugin.max_number_of_audio_inputs() as i32, 71 | outputs: self.plugin.max_number_of_audio_outputs() as i32, 72 | unique_id: self.plugin.plugin_id(), 73 | category: self.plugin.category(), 74 | ..Info::default() 75 | } 76 | } 77 | 78 | /// Create a new `VstPluginWrapper`. 79 | /// _Note_ Normally, plugin's do not need to use [`VstPluginWrapper`] and use the 80 | /// [`vst_init`] macro instead. 81 | /// 82 | /// [`vst_init`]: ../../macro.vst_init.html 83 | /// [`VstPluginWrapper`]: ./ 84 | pub fn new(plugin: P, host: HostCallback) -> Self { 85 | Self { 86 | inputs_f32: VecStorage::with_capacity(plugin.max_number_of_audio_inputs()), 87 | outputs_f32: VecStorage::with_capacity(plugin.max_number_of_audio_outputs()), 88 | inputs_f64: VecStorage::with_capacity(plugin.max_number_of_audio_inputs()), 89 | outputs_f64: VecStorage::with_capacity(plugin.max_number_of_audio_outputs()), 90 | plugin, 91 | host, 92 | } 93 | } 94 | 95 | pub fn host(&self) -> &HostCallback { 96 | &self.host 97 | } 98 | 99 | pub fn process<'b>(&mut self, buffer: &mut AudioBuffer<'b, f32>) { 100 | let number_of_frames = buffer.samples(); 101 | let (input_buffers, mut output_buffers) = buffer.split(); 102 | 103 | let mut inputs = self.inputs_f32.vec_guard(); 104 | for input_buffer in input_buffers.into_iter().take(inputs.capacity()) { 105 | inputs.push(input_buffer); 106 | } 107 | 108 | let mut outputs = self.outputs_f32.vec_guard(); 109 | for output_buffer in output_buffers.into_iter().take(outputs.capacity()) { 110 | outputs.push(output_buffer); 111 | } 112 | 113 | let mut audio_buffer = 114 | AudioBufferInOut::new(inputs.as_slice(), outputs.as_mut_slice(), number_of_frames); 115 | self.plugin.render_buffer(&mut audio_buffer, &mut self.host); 116 | } 117 | 118 | pub fn process_f64<'b>(&mut self, buffer: &mut AudioBuffer<'b, f64>) { 119 | let number_of_frames = buffer.samples(); 120 | let (input_buffers, mut output_buffers) = buffer.split(); 121 | 122 | let mut inputs = self.inputs_f64.vec_guard(); 123 | for input_buffer in input_buffers.into_iter().take(inputs.capacity()) { 124 | inputs.push(input_buffer); 125 | } 126 | 127 | let mut outputs = self.outputs_f64.vec_guard(); 128 | for output_buffer in output_buffers.into_iter().take(outputs.capacity()) { 129 | outputs.push(output_buffer); 130 | } 131 | 132 | let mut audio_buffer = 133 | AudioBufferInOut::new(inputs.as_slice(), outputs.as_mut_slice(), number_of_frames); 134 | self.plugin.render_buffer(&mut audio_buffer, &mut self.host); 135 | } 136 | 137 | pub fn get_input_info(&self, input_index: i32) -> ChannelInfo { 138 | trace!("get_input_info({})", input_index); 139 | let mut name = String::new(); 140 | if let Err(e) = self.plugin.input_name(&mut name, input_index as usize) { 141 | error!( 142 | "Failed to get the name of input with index {}: {}.", 143 | input_index, e 144 | ); 145 | } 146 | ChannelInfo::new(name, None, true, None) 147 | } 148 | 149 | pub fn get_output_info(&self, output_index: i32) -> ChannelInfo { 150 | trace!("get_output_info({})", output_index); 151 | let mut name = String::new(); 152 | if let Err(e) = self.plugin.output_name(&mut name, output_index as usize) { 153 | error!( 154 | "Failed to get the name of output with index {}: {}.", 155 | output_index, e 156 | ); 157 | } 158 | ChannelInfo::new(name, None, true, None) 159 | } 160 | 161 | pub fn process_events(&mut self, events: &Events) { 162 | trace!("process_events"); 163 | for e in events.events() { 164 | match e { 165 | VstEvent::SysEx(VstSysExEvent { 166 | payload, 167 | delta_frames, 168 | .. 169 | }) => { 170 | let event = Timed { 171 | time_in_frames: delta_frames as u32, 172 | event: SysExEvent::new(payload), 173 | }; 174 | self.plugin.handle_event(event, &mut self.host); 175 | } 176 | VstEvent::Midi(VstMidiEvent { 177 | data, delta_frames, .. 178 | }) => { 179 | let event = Timed { 180 | time_in_frames: delta_frames as u32, 181 | event: RawMidiEvent::new(&data), 182 | }; 183 | self.plugin.handle_event(event, &mut self.host); 184 | } 185 | _ => (), 186 | } 187 | } 188 | } 189 | 190 | pub fn set_sample_rate(&mut self, sample_rate: f64) { 191 | trace!("sample_rate: {}", sample_rate); 192 | self.plugin.set_sample_rate(sample_rate); 193 | } 194 | } 195 | 196 | impl HostInterface for HostCallback { 197 | fn output_initialized(&self) -> bool { 198 | // TODO: Some hosts do initialize the output to zero. 199 | // TODO: Return true for these hosts. 200 | false 201 | } 202 | } 203 | 204 | /// A wrapper around the `plugin_main!` macro from the `vst` crate. 205 | /// You call this with one parameter, which is the function declaration of a function 206 | /// that creates your plugin. 207 | /// This function may also do some setup (e.g. initialize logging). 208 | /// The plugin is typically a custom data type and should implement 209 | /// the following traits: 210 | /// 211 | /// **Traits for meta-data** (Note: you can use the [`Meta`] trait for this. 212 | /// * [`CommonPluginMeta`] (name of the plugin etc), 213 | /// * [`AudioHandlerMeta`] (number of audio ports), 214 | /// * [`CommonAudioPortMeta`] (names of the audio in and out ports) and 215 | /// * [`VstPluginMeta`], (VST-specific meta-data) 216 | /// 217 | /// **Traits for rendering audio** 218 | /// * [`AudioHandler`], 219 | /// * [`ContextualAudioRenderer`]`` and 220 | /// * [`ContextualAudioRenderer`]`` 221 | /// 222 | /// **Traits for handling midi events** 223 | /// * [`ContextualEventHandler`]`<`[`Timed`]`<`[`RawMidiEvent`]`>, `[`HostCallback`]`>` and 224 | /// * [`ContextualEventHandler`]`<`[`Timed`]`<`[`SysExEvent`]`>, `[`HostCallback`]`>`. 225 | /// 226 | /// 227 | /// 228 | /// # Example using generic code 229 | /// ``` 230 | /// # #[macro_use] extern crate rsynth; 231 | /// # extern crate num_traits; 232 | /// # #[macro_use] extern crate vst; 233 | /// use rsynth::{ 234 | /// meta::{Meta, MetaData, Port, MidiPort, AudioPort, InOut}, 235 | /// event::{ 236 | /// ContextualEventHandler, 237 | /// Timed, 238 | /// RawMidiEvent, 239 | /// SysExEvent 240 | /// }, 241 | /// backend::{ 242 | /// HostInterface, 243 | /// vst_backend::VstPluginMeta 244 | /// }, 245 | /// ContextualAudioRenderer, 246 | /// AudioHandler 247 | /// }; 248 | /// 249 | /// struct MyPlugin { 250 | /// meta: MetaData<&'static str, &'static str, &'static str> 251 | /// // Define other fields here 252 | /// } 253 | /// 254 | /// impl Meta for MyPlugin { 255 | /// type MetaData = MetaData<&'static str, &'static str, &'static str>; 256 | /// fn meta(&self) -> &Self::MetaData { 257 | /// &self.meta 258 | /// } 259 | /// } 260 | /// 261 | /// // Use the re-exports from `rsynth` so that your code doesn't break when `rsynth` upgrades 262 | /// // its dependency on `vst-rs` 263 | /// use rsynth::backend::vst_backend::vst::plugin::Category; 264 | /// impl VstPluginMeta for MyPlugin { 265 | /// fn plugin_id(&self) -> i32 { 123 } 266 | /// fn category(&self) -> Category { Category::Synth } 267 | /// } 268 | /// 269 | /// use num_traits::Float; 270 | /// # use rsynth::buffer::AudioBufferInOut; 271 | /// 272 | /// impl AudioHandler for MyPlugin { 273 | /// // Implementation omitted for brevity. 274 | /// # fn set_sample_rate(&mut self, new_sample_rate: f64) {} 275 | /// } 276 | /// 277 | /// 278 | /// impl ContextualAudioRenderer for MyPlugin 279 | /// where 280 | /// S: Float, 281 | /// H: HostInterface, 282 | /// { 283 | /// // Implementation omitted for brevity. 284 | /// # fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut H) 285 | /// # { 286 | /// # unimplemented!() 287 | /// # } 288 | /// } 289 | /// 290 | /// impl ContextualEventHandler, H> for MyPlugin 291 | /// where 292 | /// H: HostInterface, 293 | /// { 294 | /// # fn handle_event(&mut self, event: Timed, context: &mut H) {} 295 | /// // Implementation omitted for brevity. 296 | /// } 297 | /// 298 | /// impl<'a, H> ContextualEventHandler>, H> for MyPlugin 299 | /// where 300 | /// H: HostInterface, 301 | /// { 302 | /// # fn handle_event(&mut self, event: Timed>, context: &mut H) {} 303 | /// // Implementation omitted for brevity. 304 | /// } 305 | /// 306 | /// vst_init!( 307 | /// fn init() -> MyPlugin { 308 | /// MyPlugin { 309 | /// meta: MetaData { 310 | /// general_meta: "my_plugin", 311 | /// audio_port_meta: InOut { 312 | /// inputs: vec!["audio in 1", "audio in 2"], 313 | /// outputs: vec!["audio out 1", "audio out 2"], 314 | /// }, 315 | /// midi_port_meta: InOut { 316 | /// inputs: vec!["midi in 1"], 317 | /// outputs: vec![], 318 | /// }, 319 | /// } 320 | /// } 321 | /// } 322 | /// ); 323 | /// ``` 324 | /// # Example using VST-specific code 325 | /// ``` 326 | /// # #[macro_use] extern crate rsynth; 327 | /// # extern crate num_traits; 328 | /// # #[macro_use] extern crate vst; 329 | /// use rsynth::{ 330 | /// meta::{Meta, MetaData, Port, MidiPort, AudioPort, InOut}, 331 | /// event::{ 332 | /// ContextualEventHandler, 333 | /// Timed, 334 | /// RawMidiEvent, 335 | /// SysExEvent 336 | /// }, 337 | /// backend::{ 338 | /// HostInterface, 339 | /// vst_backend::VstPluginMeta 340 | /// }, 341 | /// ContextualAudioRenderer, 342 | /// AudioHandler 343 | /// }; 344 | /// 345 | /// struct MyPlugin { 346 | /// meta: MetaData<&'static str, &'static str, &'static str> 347 | /// // Define other fields here 348 | /// } 349 | /// 350 | /// impl Meta for MyPlugin { 351 | /// type MetaData = MetaData<&'static str, &'static str, &'static str>; 352 | /// fn meta(&self) -> &Self::MetaData { 353 | /// &self.meta 354 | /// } 355 | /// } 356 | /// 357 | /// // Use the re-exports from `rsynth` so that your code doesn't break when `rsynth` upgrades 358 | /// // its dependency on `vst-rs` 359 | /// use rsynth::backend::vst_backend::vst::plugin::Category; 360 | /// impl VstPluginMeta for MyPlugin { 361 | /// fn plugin_id(&self) -> i32 { 123 } 362 | /// fn category(&self) -> Category { Category::Synth } 363 | /// } 364 | /// 365 | /// use num_traits::Float; 366 | /// # use rsynth::buffer::AudioBufferInOut; 367 | /// 368 | /// impl AudioHandler for MyPlugin { 369 | /// // Implementation omitted for brevity. 370 | /// # fn set_sample_rate(&mut self, new_sample_rate: f64) {} 371 | /// } 372 | /// 373 | /// // Use the re-exports from `rsynth` so that your code doesn't break when `rsynth` upgrades 374 | /// // its dependency on `vst-rs` 375 | /// use rsynth::backend::vst_backend::vst::plugin::HostCallback; 376 | /// impl ContextualAudioRenderer for MyPlugin 377 | /// where 378 | /// S: Float, 379 | /// { 380 | /// fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut HostCallback) 381 | /// { 382 | /// // Here you can call functions on the context if you want. 383 | /// # unimplemented!() 384 | /// } 385 | /// } 386 | /// 387 | /// impl ContextualEventHandler, HostCallback> for MyPlugin 388 | /// { 389 | /// fn handle_event(&mut self, event: Timed, context: &mut HostCallback) { 390 | /// // Here you can call functions on the context if you want. 391 | /// } 392 | /// } 393 | /// 394 | /// impl<'a> ContextualEventHandler>, HostCallback> for MyPlugin 395 | /// { 396 | /// fn handle_event(&mut self, event: Timed>, context: &mut HostCallback) { 397 | /// // Here you can call functions on the context if you want. 398 | /// } 399 | /// } 400 | /// 401 | /// vst_init!( 402 | /// fn init() -> MyPlugin { 403 | /// MyPlugin { 404 | /// meta: MetaData { 405 | /// general_meta: "my_plugin", 406 | /// audio_port_meta: InOut { 407 | /// inputs: vec!["audio in 1", "audio in 2"], 408 | /// outputs: vec!["audio out 1", "audio out 2"], 409 | /// }, 410 | /// midi_port_meta: InOut { 411 | /// inputs: vec!["midi in 1"], 412 | /// outputs: vec![], 413 | /// }, 414 | /// } 415 | /// } 416 | /// } 417 | /// ); 418 | /// ``` 419 | /// [`RawMidiEvent`]: ./event/struct.RawMidiEvent.html 420 | /// [`SysExEvent`]: ./event/struct.SysExEvent.html 421 | /// [`Timed`]: ./event/struct.Timed.html 422 | /// [`Timed`]: ./event/struct.Timed.html 423 | /// [`Indexed`]: ./event/struct.Indexed.html 424 | /// [`Indexed`]: ./event/struct.Indexed.html 425 | /// [`CommonPluginMeta`]: ./trait.CommonPluginMeta.html 426 | /// [`AudioHandlerMeta`]: ./trait.AudioHandlerMeta.html 427 | /// [`MidiHandlerMeta`]: ./trait.MidiHandlerMeta.html 428 | /// [`CommonAudioPortMeta`]: ./trait.CommonAudioPortMeta.html 429 | /// [`Meta`]: ./meta/trait.Meta.html 430 | /// [`ContextualAudioRenderer`]: trait.ContextualAudioRenderer.html 431 | /// [`ContextualEventHandler`]: ./event/trait.ContextualEventHandler.html 432 | /// [`HostCallback`]: ./backend/vst_backend/vst/plugin/struct.HostCallback.html 433 | /// [`HostInterface`]: ./backend/trait.HostInterface.html 434 | /// [`CommonMidiPortMeta`]: ./trait.CommonMidiPortMeta.html 435 | /// [`VstPluginMeta`]: ./backend/vst_backend/trait.VstPluginMeta.html 436 | /// [`AudioHandler`]: ./trait.AudioHandler.html 437 | // 438 | // We define this macro so that plugins do not have to implement th `Default` trait. 439 | // 440 | // We will need the return type (as type parameter for `VstWrapperWrapper`) 441 | // and we need to call the function in the `vst::plugin::Plugin::new()` function 442 | // to which we cannot supply an extra parameter. 443 | // This is the reason why we use a macro instead of a normal function that gets 444 | // a `FnOnce` or something like that. 445 | #[macro_export] 446 | macro_rules! vst_init { 447 | (fn $function_name:ident() -> $return_type:ty 448 | $body:block 449 | ) => { 450 | 451 | fn $function_name () -> $return_type 452 | $body 453 | 454 | struct VstWrapperWrapper { 455 | wrapper: $crate::backend::vst_backend::VstPluginWrapper<$return_type> 456 | } 457 | 458 | impl Default for VstWrapperWrapper { 459 | fn default() -> Self { 460 | // We only need this so that the `Plugin` trait from the vst crate 461 | // can have a default implementation for its `new` function, 462 | // it is not actually used by the `vst` crate. 463 | unreachable!() 464 | } 465 | } 466 | 467 | // This macro is expanded in the context of the plugin. 468 | // For this reason, we do not use any "use" statements here, 469 | // as this may mess up the plugin's namespaces. 470 | // This is why you see `vst::` namespace repeated all over in this macro. 471 | impl vst::plugin::Plugin for VstWrapperWrapper 472 | { 473 | fn get_info(&self) -> vst::plugin::Info { 474 | self.wrapper.get_info() 475 | } 476 | 477 | fn new(host: vst::plugin::HostCallback) -> Self 478 | where 479 | Self: Sized + Default 480 | { 481 | VstWrapperWrapper 482 | { 483 | wrapper: $crate::backend::vst_backend::VstPluginWrapper::new($function_name(), host) 484 | } 485 | } 486 | 487 | fn init(&mut self) { 488 | // Get the sample rate from the host and set it in the plugin. 489 | let sample_rate = 490 | if let Some(vst::api::TimeInfo{sample_rate: sr, ..}) = 491 | vst::host::Host::get_time_info( 492 | self.wrapper.host(), 493 | 0 // equivalent to `vst::api::TimeInfoFlags::empty().bits()` 494 | ) 495 | { 496 | Some(sr) 497 | } else { 498 | None 499 | }; 500 | if let Some(sr) = sample_rate { 501 | self.wrapper.set_sample_rate(sr); 502 | } 503 | } 504 | 505 | #[inline] 506 | fn process<'b>(&mut self, buffer: &mut vst::buffer::AudioBuffer<'b, f32>) { 507 | self.wrapper.process(buffer); 508 | } 509 | 510 | #[inline] 511 | fn process_f64<'b>(&mut self, buffer: &mut vst::buffer::AudioBuffer<'b, f64>) { 512 | self.wrapper.process_f64(buffer); 513 | } 514 | 515 | fn get_input_info(&self, input_index: i32) -> vst::channels::ChannelInfo { 516 | self.wrapper.get_input_info(input_index) 517 | } 518 | 519 | fn get_output_info(&self, output_index: i32) -> vst::channels::ChannelInfo { 520 | self.wrapper.get_output_info(output_index) 521 | } 522 | 523 | #[inline] 524 | fn process_events(&mut self, events: &vst::api::Events) { 525 | self.wrapper.process_events(events) 526 | } 527 | } 528 | 529 | plugin_main!(VstWrapperWrapper); 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /src/envelope/mod.rs: -------------------------------------------------------------------------------- 1 | #![deprecated( 2 | since = "0.1.1", 3 | note = "This was never really worked out and should best be improved in a separate crate." 4 | )] 5 | //! This module has not been thoroughly tested, so expect some rough edges here and there. 6 | //! 7 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 8 | pub struct EnvelopeIteratorItem { 9 | pub item: T, 10 | pub has_updated: bool, 11 | } 12 | 13 | /// Defines the behaviour of an envelope. 14 | /// An envelope allows to get an iterator. 15 | /// The returned iterator allows to iterator over the frames, starting from 16 | /// the current position, and for each frame, returns the envelope value at that frame. 17 | // Note about the lifetime: ideally, we would use higher-kinded-types for this, 18 | // but right now, that's not yet supported in Rust, so we do it this way. 19 | pub trait Envelope<'a, T> { 20 | /// The type of the iterator. 21 | type Iter: Iterator>; 22 | type EventType; 23 | /// Get the iterator. 24 | fn iter(&'a self) -> Self::Iter; 25 | fn insert_event(&mut self, event: Self::EventType); 26 | fn forget_past(&mut self, number_of_frames_to_forget: u32); 27 | } 28 | 29 | pub mod staircase_envelope; 30 | -------------------------------------------------------------------------------- /src/envelope/staircase_envelope.rs: -------------------------------------------------------------------------------- 1 | use super::{Envelope, EnvelopeIteratorItem}; 2 | use crate::event::event_queue::{AlwaysRemoveOld, EventQueue}; 3 | use crate::event::Timed; 4 | 5 | pub struct StairCaseEnvelopeIterator<'a, T> 6 | where 7 | T: Copy, 8 | { 9 | envelope: &'a StairCaseEnvelope, 10 | index: usize, 11 | // Time to live 12 | ttl: usize, 13 | current_value: T, 14 | } 15 | 16 | impl<'a, T> StairCaseEnvelopeIterator<'a, T> 17 | where 18 | T: Copy + 'a, 19 | { 20 | fn new(envelope: &'a StairCaseEnvelope) -> Self { 21 | Self { 22 | envelope, 23 | index: 0, 24 | ttl: envelope 25 | .event_queue 26 | .first() 27 | .map(|x| x.time_in_frames as usize) 28 | .unwrap_or(usize::max_value()), 29 | current_value: envelope.initial_value, 30 | } 31 | } 32 | } 33 | 34 | impl<'a, T> Iterator for StairCaseEnvelopeIterator<'a, T> 35 | where 36 | T: Copy + 'a, 37 | { 38 | type Item = EnvelopeIteratorItem; 39 | 40 | // TODO: see if this cannot be moved to `EventQueue`. 41 | fn next(&mut self) -> Option { 42 | let has_updated; 43 | if self.ttl == 0 { 44 | has_updated = true; 45 | self.current_value = self.envelope.event_queue[self.index].event; 46 | self.index += 1; 47 | self.ttl = if self.index < self.envelope.event_queue.len() { 48 | (self.envelope.event_queue[self.index].time_in_frames 49 | - self.envelope.event_queue[self.index - 1].time_in_frames) 50 | as usize 51 | } else { 52 | usize::max_value() 53 | }; 54 | } else { 55 | has_updated = false; 56 | } 57 | 58 | self.ttl -= 1; 59 | 60 | Some(EnvelopeIteratorItem { 61 | item: self.current_value, 62 | has_updated, 63 | }) 64 | } 65 | } 66 | 67 | pub struct StairCaseEnvelope 68 | where 69 | T: Copy, 70 | { 71 | initial_value: T, 72 | event_queue: EventQueue, 73 | } 74 | 75 | impl<'a, T> Envelope<'a, T> for StairCaseEnvelope 76 | where 77 | T: Copy + 'a, 78 | { 79 | type Iter = StairCaseEnvelopeIterator<'a, T>; 80 | type EventType = Timed; 81 | 82 | fn iter(&'a self) -> Self::Iter { 83 | StairCaseEnvelopeIterator::new(self) 84 | } 85 | 86 | fn insert_event(&mut self, new_event: Timed) { 87 | self.event_queue.queue_event(new_event, AlwaysRemoveOld); 88 | } 89 | 90 | fn forget_past(&mut self, number_of_frames_to_forget: u32) { 91 | if let Some(ref event) = self.event_queue.get_last_before(number_of_frames_to_forget) { 92 | self.initial_value = event.event; 93 | } 94 | self.event_queue.forget_before(number_of_frames_to_forget); 95 | self.event_queue.shift_time(number_of_frames_to_forget); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/event/event_queue.rs: -------------------------------------------------------------------------------- 1 | #![deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 2 | //! Queue events. 3 | use super::Timed; 4 | use crate::buffer::AudioBufferInOut; 5 | use crate::event::EventHandler; 6 | #[cfg(test)] 7 | use crate::test_utilities::{DummyEventHandler, TestPlugin}; 8 | use crate::vecstorage::VecStorage; 9 | use crate::ContextualAudioRenderer; 10 | use std::cmp::Ordering; 11 | use std::collections::vec_deque::{Drain, VecDeque}; 12 | use std::iter::FusedIterator; 13 | use std::ops::{Deref, Index, IndexMut}; 14 | #[cfg_attr(test, allow(deprecated))] 15 | 16 | /// A queue for timed events. 17 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 18 | pub struct EventQueue { 19 | queue: VecDeque>, 20 | } 21 | 22 | /// Determines what should happen when two events are queued with the same timing. 23 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 24 | pub enum EventCollisionHandling { 25 | /// Insert the newly queued event before the previously queued. 26 | InsertNewBeforeOld, 27 | /// Insert the newly queued event after the previously queued. 28 | InsertNewAfterOld, 29 | /// Ignore the newly queued event. 30 | IgnoreNew, 31 | /// Remove the previously queued event. 32 | RemoveOld, 33 | } 34 | 35 | /// Trait that describes how "event collision" (queing two events with the same timestamp) should happen. 36 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 37 | pub trait HandleEventCollision { 38 | fn decide_on_collision(&self, old_event: &T, new_event: &T) -> EventCollisionHandling; 39 | } 40 | 41 | /// Always queue the new newly queued event before the previously queued in case of collision (same timestamp). 42 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 43 | pub struct AlwaysInsertNewBeforeOld; 44 | impl HandleEventCollision for AlwaysInsertNewBeforeOld { 45 | #[inline(always)] 46 | fn decide_on_collision(&self, _old_event: &T, _new_event: &T) -> EventCollisionHandling { 47 | EventCollisionHandling::InsertNewBeforeOld 48 | } 49 | } 50 | 51 | /// Always queue the new newly queued event after the previously queued in case of collision (same timestamp). 52 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 53 | pub struct AlwaysInsertNewAfterOld; 54 | impl HandleEventCollision for AlwaysInsertNewAfterOld { 55 | #[inline(always)] 56 | fn decide_on_collision(&self, _old_event: &T, _new_event: &T) -> EventCollisionHandling { 57 | EventCollisionHandling::InsertNewAfterOld 58 | } 59 | } 60 | 61 | /// Always ignore the newly queued event in case of collision (there's already an event with that timestamp). 62 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 63 | pub struct AlwaysIgnoreNew; 64 | impl HandleEventCollision for AlwaysIgnoreNew { 65 | #[inline(always)] 66 | fn decide_on_collision(&self, _old_event: &T, _new_event: &T) -> EventCollisionHandling { 67 | EventCollisionHandling::IgnoreNew 68 | } 69 | } 70 | 71 | /// Always remove the previously queued event in case of collision (there's already an event with that timestamp). 72 | #[deprecated(since = "0.1.2", note = "Use the `event_queue` crate instead.")] 73 | pub struct AlwaysRemoveOld; 74 | impl HandleEventCollision for AlwaysRemoveOld { 75 | #[inline(always)] 76 | fn decide_on_collision(&self, _old_event: &T, _new_event: &T) -> EventCollisionHandling { 77 | EventCollisionHandling::RemoveOld 78 | } 79 | } 80 | 81 | impl Index for EventQueue { 82 | type Output = Timed; 83 | 84 | fn index(&self, index: usize) -> &Self::Output { 85 | &self.queue[index] 86 | } 87 | } 88 | 89 | impl IndexMut for EventQueue { 90 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 91 | &mut self.queue[index] 92 | } 93 | } 94 | 95 | impl EventQueue { 96 | /// Create a new `EventQueue` fom a vector of events. 97 | /// _Note_: this may violate the invariants of the `EventQueue`, so it's only available for testing. 98 | #[cfg(test)] 99 | pub fn from_vec(events: Vec>) -> Self { 100 | Self { 101 | queue: events.into(), 102 | } 103 | } 104 | 105 | /// Create a new `EventQueue`. 106 | /// # Panics 107 | /// Panics if `capacity == 0`. 108 | pub fn new(capacity: usize) -> Self { 109 | assert!(capacity > 0); 110 | Self { 111 | queue: VecDeque::with_capacity(capacity), 112 | } 113 | } 114 | 115 | /// Queue a new event. 116 | /// When the buffer is full, an element may be removed from the queue to make some room. 117 | /// This element is returned. 118 | pub fn queue_event(&mut self, new_event: Timed, collision_decider: H) -> Option> 119 | where 120 | H: HandleEventCollision, 121 | { 122 | let mut new_event = new_event; 123 | let result; 124 | if self.queue.len() >= self.queue.capacity() { 125 | // Note: self.queue.capacity() > 0, so self.queue is not empty. 126 | // TODO: Log an error. 127 | // We remove the first event to come, in this way, 128 | // we are sure we are not skipping the "last" event, 129 | // because we assume that the state of the first event 130 | // is only temporarily, and the state of the last event 131 | // may remain forever. For this reason, it is safer to 132 | // remove the first event 133 | if new_event.time_in_frames > self.queue[0].time_in_frames { 134 | result = self.queue.pop_front(); 135 | } else { 136 | return Some(new_event); 137 | } 138 | } else { 139 | result = None; 140 | } 141 | // If we are at this point, we can assume that we can insert at least one more event. 142 | debug_assert!(self.queue.len() < self.queue.capacity()); 143 | 144 | let mut insert_index = 0; 145 | for read_event in self.queue.iter_mut() { 146 | match read_event.time_in_frames.cmp(&new_event.time_in_frames) { 147 | Ordering::Less => { 148 | insert_index += 1; 149 | } 150 | Ordering::Equal => { 151 | match collision_decider.decide_on_collision(&read_event.event, &new_event.event) 152 | { 153 | EventCollisionHandling::IgnoreNew => { 154 | return Some(new_event); 155 | } 156 | EventCollisionHandling::InsertNewBeforeOld => { 157 | break; 158 | } 159 | EventCollisionHandling::InsertNewAfterOld => { 160 | insert_index += 1; 161 | } 162 | EventCollisionHandling::RemoveOld => { 163 | std::mem::swap(&mut read_event.event, &mut new_event.event); 164 | return Some(new_event); 165 | } 166 | } 167 | } 168 | Ordering::Greater => { 169 | break; 170 | } 171 | } 172 | } 173 | self.queue.insert(insert_index, new_event); 174 | 175 | result 176 | } 177 | 178 | /// Remove all events before, but not on, this threshold. 179 | /// 180 | /// # Note about usage in real-time context 181 | /// If `T` implements drop, the elements that are removed are dropped. 182 | /// This may cause memory de-allocation, which you want to avoid in 183 | /// the real-time part of your library. 184 | pub fn forget_before(&mut self, threshold: u32) 185 | where 186 | T: Copy, 187 | { 188 | self.queue.retain(|x| x.time_in_frames >= threshold); 189 | } 190 | 191 | /// Remove all events from the queue. 192 | /// 193 | /// # Note about usage in real-time context 194 | /// If `T` implements drop, the elements that are removed are dropped. 195 | /// This may cause memory de-allocation, which you want to avoid in 196 | /// the real-time part of your library. 197 | pub fn clear(&mut self) { 198 | self.queue.clear() 199 | } 200 | 201 | /// Shift time forward by `new_zero_time` frames. 202 | /// 203 | /// # Panics 204 | /// Panics in debug mode when at least one event has a `time_in_frames` 205 | /// that is < `new_zero_time`. 206 | pub fn shift_time(&mut self, new_zero_time: u32) { 207 | for event in self.queue.iter_mut() { 208 | event.time_in_frames -= new_zero_time; 209 | } 210 | } 211 | 212 | pub fn get_last_before(&self, time: u32) -> Option<&Timed> { 213 | if let Some(index) = self.queue.iter().rposition(|e| e.time_in_frames < time) { 214 | self.queue.get(index) 215 | } else { 216 | None 217 | } 218 | } 219 | 220 | /// Get the first event from the `EventQueue` if there is one and return `None` if the queue is empty. 221 | pub fn first(&self) -> Option<&Timed> { 222 | self.queue.get(0) 223 | } 224 | 225 | /// Go through the `EventQueue` and alternatingly handle events and render audio. 226 | /// 227 | /// # Note about using in a realtime context. 228 | /// There will be as many elements pushed to `input_storage` as there are 229 | /// input channels. 230 | /// There will be as many elements pushed to `output_storage` as there are 231 | /// output channels. 232 | #[deprecated( 233 | since = "0.1.2", 234 | note = "Use the `interleave` method on `AudioBufferInOut` instead." 235 | )] 236 | pub fn split<'in_storage, 'out_storage, 'in_channels, 's, 'chunk, S, R, C>( 237 | &mut self, 238 | input_storage: &'in_storage mut VecStorage<&'static [S]>, 239 | output_storage: &'out_storage mut VecStorage<&'static mut [S]>, 240 | buffer: &mut AudioBufferInOut<'in_channels, '_, '_, '_, S>, 241 | renderer: &mut R, 242 | context: &mut C, 243 | ) where 244 | S: Copy + 'static, 245 | R: ContextualAudioRenderer + EventHandler, 246 | { 247 | let buffer_length = buffer.number_of_frames(); 248 | let mut last_event_time = 0; 249 | loop { 250 | if let Some(ref first) = self.queue.get(0) { 251 | if first.time_in_frames as usize >= buffer_length { 252 | break; 253 | } 254 | } else { 255 | break; 256 | }; 257 | let Timed { 258 | time_in_frames: event_time, 259 | event, 260 | } = self.queue.pop_front().expect("event queue is not empty"); 261 | if event_time == last_event_time { 262 | renderer.handle_event(event); 263 | continue; 264 | } 265 | 266 | let mut input_guard = input_storage.vec_guard(); 267 | let mut output_guard = output_storage.vec_guard(); 268 | let mut sub_buffer = buffer.index_frames( 269 | (last_event_time as usize)..(event_time as usize), 270 | &mut input_guard, 271 | &mut output_guard, 272 | ); 273 | renderer.render_buffer(&mut sub_buffer, context); 274 | renderer.handle_event(event); 275 | last_event_time = event_time; 276 | } 277 | if (last_event_time as usize) < buffer_length { 278 | let mut input_guard = input_storage.vec_guard(); 279 | let mut output_guard = output_storage.vec_guard(); 280 | let mut sub_buffer = buffer.index_frames( 281 | (last_event_time as usize)..buffer_length, 282 | &mut input_guard, 283 | &mut output_guard, 284 | ); 285 | renderer.render_buffer(&mut sub_buffer, context); 286 | }; 287 | } 288 | 289 | /// Create an iterator that drains all elements before but not on the given time. 290 | pub fn drain(&mut self, time: u32) -> DrainingIter { 291 | if let Some(index) = self.queue.iter().rposition(|e| e.time_in_frames < time) { 292 | DrainingIter { 293 | inner: self.queue.drain(0..=index), 294 | } 295 | } else { 296 | DrainingIter { 297 | inner: self.queue.drain(0..0), 298 | } 299 | } 300 | } 301 | 302 | /// Create an iterator that drains all elements. 303 | pub fn drain_all(&mut self) -> DrainingIter { 304 | DrainingIter { 305 | inner: self.queue.drain(0..), 306 | } 307 | } 308 | } 309 | 310 | impl Deref for EventQueue { 311 | type Target = VecDeque>; 312 | 313 | fn deref(&self) -> &Self::Target { 314 | &self.queue 315 | } 316 | } 317 | 318 | #[test] 319 | fn eventqueue_queue_event_new_event_ignored_when_already_full_and_new_event_comes_first() { 320 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 321 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 322 | // Check our assumption: 323 | assert_eq!(queue.queue.capacity(), queue.queue.len()); 324 | 325 | // Act 326 | queue.queue_event(Timed::new(3, 9), AlwaysIgnoreNew); 327 | 328 | // Assert: 329 | assert_eq!(queue.queue, initial_buffer); 330 | } 331 | 332 | #[test] 333 | fn event_queue_queue_event_first_event_removed_when_already_full_and_new_event_after_first() { 334 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 335 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 336 | // Check our assumption: 337 | assert_eq!(queue.queue.capacity(), queue.queue.len()); 338 | 339 | // Act 340 | queue.queue_event(Timed::new(5, 25), AlwaysInsertNewAfterOld); 341 | 342 | // Assert: 343 | assert_eq!( 344 | queue.queue, 345 | vec![Timed::new(5, 25), Timed::new(6, 36), Timed::new(7, 49),] 346 | ); 347 | } 348 | 349 | #[test] 350 | fn eventqueue_queue_event_new_event_inserted_at_correct_location() { 351 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 352 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 353 | queue.queue.reserve(1); 354 | 355 | // Act 356 | queue.queue_event(Timed::new(5, 25), AlwaysInsertNewAfterOld); 357 | 358 | // Assert: 359 | assert_eq!( 360 | queue.queue, 361 | vec![ 362 | Timed::new(4, 16), 363 | Timed::new(5, 25), 364 | Timed::new(6, 36), 365 | Timed::new(7, 49), 366 | ] 367 | ); 368 | } 369 | 370 | #[test] 371 | fn eventqueue_queue_event_with_always_ignore_new_new_event_ignored_when_already_event_at_that_location( 372 | ) { 373 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 374 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 375 | queue.queue.reserve(1); 376 | 377 | // Act 378 | queue.queue_event(Timed::new(6, 25), AlwaysIgnoreNew); 379 | 380 | // Assert: 381 | assert_eq!(queue.queue, initial_buffer); 382 | } 383 | 384 | #[test] 385 | fn eventqueue_queue_event_with_always_ignore_old_old_event_ignored_when_already_event_at_that_location( 386 | ) { 387 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 388 | let expected_buffer = vec![Timed::new(4, 16), Timed::new(6, 25), Timed::new(7, 49)]; 389 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 390 | queue.queue.reserve(1); 391 | 392 | // Act 393 | let result = queue.queue_event(Timed::new(6, 25), AlwaysRemoveOld); 394 | 395 | assert_eq!(result, Some(Timed::new(6, 36))); 396 | 397 | // Assert: 398 | assert_eq!(queue.queue, expected_buffer); 399 | } 400 | 401 | #[test] 402 | fn eventqueue_queue_event_with_always_insert_new_after_old() { 403 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 404 | let expected_buffer = vec![ 405 | Timed::new(4, 16), 406 | Timed::new(6, 36), 407 | Timed::new(6, 25), 408 | Timed::new(7, 49), 409 | ]; 410 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 411 | queue.queue.reserve(1); 412 | 413 | // Act 414 | let result = queue.queue_event(Timed::new(6, 25), AlwaysInsertNewAfterOld); 415 | 416 | assert_eq!(result, None); 417 | 418 | // Assert: 419 | assert_eq!(queue.queue, expected_buffer); 420 | } 421 | 422 | #[test] 423 | fn eventqueue_queue_event_with_always_insert_new_after_old_with_doubles() { 424 | let initial_buffer = vec![Timed::new(6, 16), Timed::new(6, 36), Timed::new(7, 49)]; 425 | let expected_buffer = vec![ 426 | Timed::new(6, 16), 427 | Timed::new(6, 36), 428 | Timed::new(6, 25), 429 | Timed::new(7, 49), 430 | ]; 431 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 432 | queue.queue.reserve(1); 433 | 434 | // Act 435 | let result = queue.queue_event(Timed::new(6, 25), AlwaysInsertNewAfterOld); 436 | 437 | assert_eq!(result, None); 438 | 439 | // Assert: 440 | assert_eq!(queue.queue, expected_buffer); 441 | } 442 | 443 | #[test] 444 | fn eventqueue_queue_event_with_always_insert_new_before_old() { 445 | let initial_buffer = vec![Timed::new(4, 16), Timed::new(6, 36), Timed::new(7, 49)]; 446 | let expected_buffer = vec![ 447 | Timed::new(4, 16), 448 | Timed::new(6, 25), 449 | Timed::new(6, 36), 450 | Timed::new(7, 49), 451 | ]; 452 | let mut queue = EventQueue::from_vec(initial_buffer.clone()); 453 | queue.queue.reserve(1); 454 | 455 | // Act 456 | let result = queue.queue_event(Timed::new(6, 25), AlwaysInsertNewBeforeOld); 457 | 458 | assert_eq!(result, None); 459 | 460 | // Assert: 461 | assert_eq!(queue.queue, expected_buffer); 462 | } 463 | 464 | #[test] 465 | fn eventqueue_forget_before() { 466 | let mut queue = EventQueue::from_vec({ 467 | vec![ 468 | Timed::new(4, 16), 469 | Timed::new(6, 36), 470 | Timed::new(7, 49), 471 | Timed::new(8, 64), 472 | ] 473 | }); 474 | queue.forget_before(7); 475 | assert_eq!(queue.queue, vec![Timed::new(7, 49), Timed::new(8, 64),]); 476 | } 477 | 478 | #[test] 479 | fn eventqueue_forget_everything() { 480 | let mut queue = EventQueue::from_vec({ 481 | vec![ 482 | Timed::new(4, 16), 483 | Timed::new(6, 36), 484 | Timed::new(7, 49), 485 | Timed::new(8, 64), 486 | ] 487 | }); 488 | queue.forget_before(9); 489 | assert_eq!(queue.queue, Vec::new()); 490 | } 491 | 492 | /// Draining iterator created by the [`EventQueue::drain`] method. 493 | pub struct DrainingIter<'a, T> { 494 | inner: Drain<'a, Timed>, 495 | } 496 | 497 | impl<'a, T> Iterator for DrainingIter<'a, T> { 498 | type Item = Timed; 499 | fn next(&mut self) -> Option { 500 | self.inner.next() 501 | } 502 | 503 | fn size_hint(&self) -> (usize, Option) { 504 | self.inner.size_hint() 505 | } 506 | } 507 | 508 | impl<'a, T> DoubleEndedIterator for DrainingIter<'a, T> { 509 | fn next_back(&mut self) -> Option { 510 | self.inner.next_back() 511 | } 512 | } 513 | 514 | impl<'a, T> ExactSizeIterator for DrainingIter<'a, T> {} 515 | 516 | impl<'a, T> FusedIterator for DrainingIter<'a, T> {} 517 | -------------------------------------------------------------------------------- /src/event/mod.rs: -------------------------------------------------------------------------------- 1 | //! Event handling 2 | //! 3 | //! This module defines the `EventHandler` trait and some event types: `RawMidiEvent`, 4 | //! `SysExEvent`, ... 5 | //! 6 | //! Custom events 7 | //! ============= 8 | //! 9 | //! Implement `Copy` if possible 10 | //! ---------------------------- 11 | //! 12 | //! If possible, implement the `Copy` trait for the event, 13 | //! so that the event can be dispatched to different voices in a polyphonic context. 14 | #[cfg(feature = "backend-combined-midly-0-5")] 15 | use crate::backend::combined::midly::midly_0_5::TrackEventKind; 16 | #[cfg(all(test, feature = "backend-combined-midly-0-5"))] 17 | use crate::backend::combined::midly::midly_0_5::{ 18 | num::{u4, u7}, 19 | MidiMessage, 20 | }; 21 | use core::num::NonZeroU64; 22 | use gcd::Gcd; 23 | use std::convert::{AsMut, AsRef, TryFrom}; 24 | use std::error::Error; 25 | use std::fmt::{Debug, Display, Formatter, Write}; 26 | 27 | pub mod event_queue; 28 | 29 | /// The trait that plugins should implement in order to handle the given type of events. 30 | /// 31 | /// The type parameter `E` corresponds to the type of the event. 32 | pub trait EventHandler { 33 | fn handle_event(&mut self, event: E); 34 | } 35 | 36 | /// An extension trait for [`EventHandler`] providing some convenient combinator functions. 37 | pub trait EventHandlerExt { 38 | /// Create a new event handler that first applies the given function to the event 39 | /// and then lets the "self" event handler handle the event. 40 | /// 41 | /// # Example 42 | /// ``` 43 | /// use rsynth::event::EventHandler; 44 | /// use rsynth::event::EventHandlerExt; 45 | /// 46 | /// struct Printer; 47 | /// impl EventHandler for Printer { 48 | /// fn handle_event(&mut self,event: u32) { 49 | /// println!("{}", event) 50 | /// } 51 | /// } 52 | /// 53 | /// fn main() { 54 | /// let mut printer = Printer; 55 | /// printer.handle_event(3); // Prints "3" 56 | /// let mut increased_printer = printer.map(|i| i+1_u32); 57 | /// increased_printer.handle_event(3); // Prints "4" 58 | /// } 59 | /// ``` 60 | fn map(&mut self, function: F) -> Map 61 | where 62 | F: FnMut(EE) -> E, 63 | { 64 | Map { 65 | inner: self, 66 | function, 67 | } 68 | } 69 | } 70 | 71 | impl EventHandlerExt for T where T: EventHandler + ?Sized {} 72 | 73 | /// An [`EventHandler`] from the [`EventHandlerExt::map`] method. 74 | pub struct Map<'a, H, F> 75 | where 76 | H: ?Sized, 77 | { 78 | inner: &'a mut H, 79 | function: F, 80 | } 81 | 82 | impl<'a, E, EE, F, H> EventHandler for Map<'a, H, F> 83 | where 84 | H: EventHandler, 85 | F: FnMut(EE) -> E, 86 | { 87 | fn handle_event(&mut self, event: EE) { 88 | self.inner.handle_event((self.function)(event)) 89 | } 90 | } 91 | 92 | /// The trait that plugins should implement in order to handle the given type of events. 93 | /// 94 | /// The type parameter `E` corresponds to the type of the event. 95 | /// The type parameter `Context` refers to the context that is passed to the event handler. 96 | pub trait ContextualEventHandler { 97 | fn handle_event(&mut self, event: E, context: &mut Context); 98 | } 99 | 100 | /// A System Exclusive ("SysEx") event. 101 | #[derive(Clone, Copy, PartialEq, Eq)] 102 | pub struct SysExEvent<'a> { 103 | data: &'a [u8], 104 | } 105 | 106 | impl<'a> Debug for SysExEvent<'a> { 107 | fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { 108 | write!(f, "SysExEvent{{data (length: {:?}): &[", self.data.len())?; 109 | for byte in self.data { 110 | write!(f, "{:X} ", byte)?; 111 | } 112 | write!(f, "]}}") 113 | } 114 | } 115 | 116 | impl<'a> SysExEvent<'a> { 117 | /// Create a new `SysExEvent` with the given `data`. 118 | pub fn new(data: &'a [u8]) -> Self { 119 | Self { data } 120 | } 121 | /// Get the data from the `SysExEvent` 122 | pub fn data(&self) -> &'a [u8] { 123 | self.data 124 | } 125 | } 126 | 127 | /// A raw midi event. 128 | #[derive(Clone, Copy, PartialEq, Eq)] 129 | pub struct RawMidiEvent { 130 | data: [u8; 3], 131 | length: usize, 132 | } 133 | 134 | impl Debug for RawMidiEvent { 135 | fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { 136 | match self.length { 137 | 1 => write!(f, "RawMidiEvent({:X})", self.data[0]), 138 | 2 => write!(f, "RawMidiEvent({:X} {:X})", self.data[0], self.data[1]), 139 | 3 => write!( 140 | f, 141 | "RawMidiEvent({:X} {:X} {:X})", 142 | self.data[0], self.data[1], self.data[2] 143 | ), 144 | _ => unreachable!("Raw midi event is expected to have length 1, 2 or 3."), 145 | } 146 | } 147 | } 148 | 149 | impl RawMidiEvent { 150 | /// Create a new `RawMidiEvent` with the given raw data. 151 | /// 152 | /// Panics 153 | /// ------ 154 | /// Panics when `data` does not have length 1, 2 or 3. 155 | #[inline] 156 | pub fn new(bytes: &[u8]) -> Self { 157 | match Self::try_new(bytes) { 158 | Some(s) => s, 159 | None => { 160 | Self::panic_data_too_long(bytes); 161 | } 162 | } 163 | } 164 | 165 | fn panic_data_too_long(bytes: &[u8]) -> ! { 166 | let mut event_as_string = String::new(); 167 | write!(event_as_string, "data : &["); 168 | for (index, byte) in bytes.iter().enumerate() { 169 | write!(event_as_string, "{:X} ", byte); 170 | if index > 64 { 171 | write!(event_as_string, "... "); 172 | break; 173 | } 174 | } 175 | write!(event_as_string, "]"); 176 | panic!( 177 | "Raw midi event is expected to have length 1, 2 or 3. Actual length: {}, data: {}", 178 | bytes.len(), 179 | event_as_string 180 | ); 181 | } 182 | 183 | /// Try to create a new `RawMidiEvent` with the given raw data. 184 | /// Return None when `data` does not have length 1, 2 or 3. 185 | pub fn try_new(data: &[u8]) -> Option { 186 | match data.len() { 187 | 1 => Some(Self { 188 | data: [data[0], 0, 0], 189 | length: data.len(), 190 | }), 191 | 2 => Some(Self { 192 | data: [data[0], data[1], 0], 193 | length: data.len(), 194 | }), 195 | 3 => Some(Self { 196 | data: [data[0], data[1], data[2]], 197 | length: data.len(), 198 | }), 199 | _ => None, 200 | } 201 | } 202 | 203 | /// Get the raw data from a `RawMidiEvent`, including "padding". 204 | pub fn data(&self) -> &[u8; 3] { 205 | &self.data 206 | } 207 | 208 | /// Get the raw data from a `RawMidiEvent`. 209 | pub fn bytes(&self) -> &[u8] { 210 | &self.data[0..self.length] 211 | } 212 | } 213 | 214 | #[cfg(feature = "backend-combined-midly-0-5")] 215 | use crate::backend::combined::midly::midly_0_5::io::CursorError; 216 | 217 | #[cfg(feature = "backend-combined-midly-0-5")] 218 | #[derive(Debug, Clone)] 219 | /// The error type when converting from `midly`'s `TrackEventKind` to a `RawMidiEvent`. 220 | pub enum MidlyConversionError { 221 | /// Not a live event. 222 | NotALiveEvent, 223 | /// Cursor error (technical error). 224 | CursorError(CursorError), 225 | } 226 | 227 | #[cfg(feature = "backend-combined-midly-0-5")] 228 | impl Display for MidlyConversionError { 229 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 230 | match self { 231 | MidlyConversionError::NotALiveEvent => write!(f, "Not a live event."), 232 | MidlyConversionError::CursorError(e) => match e { 233 | CursorError::InvalidInput(msg) => { 234 | write!(f, "Technical error: the input SMF was invalid: {}", msg) 235 | } 236 | CursorError::OutOfSpace => { 237 | write!(f, "Technical error: the in-memory buffer was too small") 238 | } 239 | }, 240 | } 241 | } 242 | } 243 | 244 | #[cfg(feature = "backend-combined-midly-0-5")] 245 | impl Error for MidlyConversionError { 246 | fn source(&self) -> Option<&(dyn Error + 'static)> { 247 | None 248 | } 249 | } 250 | 251 | #[cfg(feature = "backend-combined-midly-0-5")] 252 | impl From for MidlyConversionError { 253 | fn from(e: CursorError) -> Self { 254 | MidlyConversionError::CursorError(e) 255 | } 256 | } 257 | 258 | #[cfg(feature = "backend-combined-midly-0-5")] 259 | impl<'a> TryFrom> for RawMidiEvent { 260 | type Error = MidlyConversionError; 261 | 262 | fn try_from(value: TrackEventKind<'a>) -> Result { 263 | let mut raw_data: [u8; 3] = [0, 0, 0]; 264 | let mut slice = &mut raw_data[0..3]; 265 | value 266 | .as_live_event() 267 | .ok_or(MidlyConversionError::NotALiveEvent)? 268 | .write(&mut slice)?; 269 | // The slice is updated to point to the not-yet-overwritten bytes. 270 | let number_of_bytes = 3 - slice.len(); 271 | Ok(RawMidiEvent::new(&raw_data[0..number_of_bytes])) 272 | } 273 | } 274 | 275 | #[cfg(feature = "backend-combined-midly-0-5")] 276 | #[test] 277 | fn conversion_from_midly_to_raw_midi_event_works() { 278 | let channel = 1; 279 | let program = 2; 280 | let event_kind = TrackEventKind::Midi { 281 | channel: u4::from(channel), 282 | message: MidiMessage::ProgramChange { 283 | program: u7::from(program), 284 | }, 285 | }; 286 | let raw_midi_event = RawMidiEvent::try_from(event_kind).unwrap(); 287 | assert_eq!(raw_midi_event.length, 2); 288 | assert_eq!( 289 | raw_midi_event.data, 290 | [ 291 | channel | midi_consts::channel_event::PROGRAM_CHANGE, 292 | program, 293 | 0 294 | ] 295 | ); 296 | } 297 | 298 | impl AsRef for RawMidiEvent { 299 | fn as_ref(&self) -> &RawMidiEvent { 300 | self 301 | } 302 | } 303 | 304 | impl AsMut for RawMidiEvent { 305 | fn as_mut(&mut self) -> &mut RawMidiEvent { 306 | self 307 | } 308 | } 309 | 310 | /// `Timed` adds timing to an event. 311 | /// 312 | /// # Suggestion 313 | /// If you want to handle events in a sample-accurate way, you can use an 314 | /// `EventQueue` to queue them when you receive them, and later use the 315 | /// `split` method on the queue to render the audio. 316 | #[derive(PartialEq, Eq, Debug)] 317 | pub struct Timed { 318 | /// The offset (in frames) of the event relative to the start of 319 | /// the audio buffer. 320 | /// 321 | /// E.g. when `time_in_frames` is 6, this means that 322 | /// the event happens on the sixth frame of the buffer in the call to 323 | /// the [`render_buffer`] method of the `Plugin` trait. 324 | /// 325 | /// [`render_buffer`]: ../trait.Plugin.html#tymethod.render_buffer 326 | pub time_in_frames: u32, 327 | /// The underlying event. 328 | pub event: E, 329 | } 330 | 331 | impl Timed { 332 | pub fn new(time_in_frames: u32, event: E) -> Self { 333 | Self { 334 | time_in_frames, 335 | event, 336 | } 337 | } 338 | } 339 | 340 | impl Clone for Timed 341 | where 342 | E: Clone, 343 | { 344 | fn clone(&self) -> Self { 345 | Timed { 346 | time_in_frames: self.time_in_frames, 347 | event: self.event.clone(), 348 | } 349 | } 350 | } 351 | 352 | impl Copy for Timed where E: Copy {} 353 | 354 | impl AsRef for Timed { 355 | fn as_ref(&self) -> &E { 356 | &self.event 357 | } 358 | } 359 | 360 | impl AsMut for Timed { 361 | fn as_mut(&mut self) -> &mut E { 362 | &mut self.event 363 | } 364 | } 365 | 366 | /// `Indexed` adds an index to an event of type `E`. 367 | /// The index typically corresponds to the index of the channel. 368 | #[derive(PartialEq, Eq, Debug)] 369 | pub struct Indexed { 370 | /// The index of the event. 371 | pub index: usize, 372 | /// The underlying event. 373 | pub event: E, 374 | } 375 | 376 | impl Indexed { 377 | pub fn new(index: usize, event: E) -> Self { 378 | Self { index, event } 379 | } 380 | } 381 | 382 | impl Clone for Indexed 383 | where 384 | E: Clone, 385 | { 386 | fn clone(&self) -> Self { 387 | Self { 388 | index: self.index, 389 | event: self.event.clone(), 390 | } 391 | } 392 | } 393 | 394 | impl Copy for Indexed where E: Copy {} 395 | 396 | impl AsRef for Indexed { 397 | fn as_ref(&self) -> &E { 398 | &self.event 399 | } 400 | } 401 | 402 | impl AsMut for Indexed { 403 | fn as_mut(&mut self) -> &mut E { 404 | &mut self.event 405 | } 406 | } 407 | 408 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 409 | pub struct DeltaEvent { 410 | pub microseconds_since_previous_event: u64, 411 | pub event: E, 412 | } 413 | 414 | /// Stretch integer (`u64`) time stamps by a fractional factor that may change over time. 415 | pub struct TimeStretcher { 416 | nominator: u64, 417 | denominator: NonZeroU64, 418 | input_offset: u64, 419 | output_offset: u64, 420 | drifting_correction: f64, 421 | } 422 | 423 | impl TimeStretcher { 424 | /// Create a new `TimeStretcher`. 425 | pub fn new(nominator: u64, denominator: NonZeroU64) -> Self { 426 | Self { 427 | nominator, 428 | denominator, 429 | input_offset: 0, 430 | output_offset: 0, 431 | drifting_correction: 0.0, 432 | } 433 | } 434 | 435 | /// Stretch the input time by the factor `nominator/denominator`. 436 | /// 437 | /// # Parameters 438 | /// `input_time`: the time to be stretched 439 | /// `new_speed`: if this contains `Some((nominator, denominator))`, future times will 440 | /// be stretched by the new factor `nominator/denominator`. 441 | pub fn stretch( 442 | &mut self, 443 | absolute_input_time: u64, 444 | new_speed: Option<(u64, NonZeroU64)>, 445 | ) -> u64 { 446 | let self_denominator: u64 = self.denominator.into(); // Avoid some cumbersome type annotations. 447 | 448 | // Adapt offset in order to avoid integer overflow when multiplying. 449 | let input_offset_delta: u64 = (absolute_input_time - self.input_offset) / self_denominator; 450 | self.input_offset += input_offset_delta * self_denominator; 451 | self.output_offset += input_offset_delta * self.nominator; 452 | 453 | let new_time = (absolute_input_time - self.input_offset) * self.nominator 454 | / self_denominator 455 | + self.output_offset; 456 | 457 | if let Some((mut new_nominator, mut new_denominator)) = new_speed { 458 | let gcd = new_nominator.gcd(new_denominator.into()); 459 | new_nominator = new_nominator / gcd; 460 | // Safety: because new_denominator != 0, gcd != 0. 461 | // Safety: gcd <= new_denominator, new_denominator / gcd >= 1 > 0. 462 | new_denominator = 463 | unsafe { NonZeroU64::new_unchecked(<_ as Into>::into(new_denominator) / gcd) }; 464 | if new_nominator != self.nominator || new_denominator != self.denominator { 465 | // Update the drifting correction 466 | let error = ((absolute_input_time - self.input_offset) * self.nominator) as f64 467 | / (self_denominator as f64) 468 | - ((new_time - self.output_offset) as f64); 469 | self.drifting_correction += error; 470 | let correction = self.drifting_correction as u64; 471 | self.drifting_correction -= correction as f64; 472 | 473 | // Set the new offsets, taking into account the correction. 474 | self.input_offset = absolute_input_time; 475 | self.output_offset = new_time + correction; 476 | 477 | // Set the new nominators and denominators 478 | self.nominator = new_nominator; 479 | self.denominator = new_denominator; 480 | } 481 | } 482 | new_time 483 | } 484 | } 485 | 486 | #[test] 487 | pub fn event_time_stretcher_works_when_no_drifting_correction_needed() { 488 | let mut stretcher = TimeStretcher::new(1, NonZeroU64::new(1).unwrap()); 489 | // Input events and the line below: output events 490 | // 0 . . 1 . . 2 . . . . 3 . . . . 4 491 | // 0 . 1 . 2 . . . . . . 3 . . . . . . 4 492 | let input = vec![ 493 | (0, Some((2, NonZeroU64::new(3).unwrap()))), 494 | (3, None), 495 | (6, Some((7, NonZeroU64::new(5).unwrap()))), 496 | (11, None), 497 | (16, None), 498 | ]; 499 | let mut observed_output = Vec::new(); 500 | for input in input.into_iter() { 501 | observed_output.push(stretcher.stretch(input.0, input.1)); 502 | } 503 | let expected_output = vec![0, 2, 4, 11, 18]; 504 | assert_eq!(expected_output, observed_output); 505 | } 506 | 507 | #[test] 508 | pub fn event_time_stretcher_works_when_drifting_correction_needed() { 509 | let mut stretcher = TimeStretcher::new(1, NonZeroU64::new(1).unwrap()); 510 | // Underscore: factor 1/2, dash: factor 1/3 511 | // _______ ----- _______------ 512 | // 0 . 1 2 . . 3 4 . 5 6 . . 7 513 | // Below: the output. We use two lines since there are sometimes two events at the same time. 514 | // 0 => 0 515 | // 1 => 1 516 | // 2 => 1 + 1/2, after rounding: 1. Here we change the speed, so this error gets accumulated. Error: 1/2 517 | // 3 => 2 + 1/2, after rounding: 2. 518 | // 4 => 2 + 1/2 + 1/3, after rounding: 2. Here we change speed, so this error gets accumulated as well. Error: 5/6. 519 | // 5 => 3 + 1/2 + 1/3, after rounding: 3. 520 | // 6 => 3 + 1/2 + 1/3 + 1/2. Rounding: floor(floor(floor(1 + 1/2) + 1 + 1/3) + 1 + 1/2) = 3. 521 | // We change speed, so this error gets accumulated. Error: 8/6. 522 | // 7 => 3 + 1/2 + 1/3 + 1/2 + 1. Here we apply the correction and we get 5. 523 | let input = vec![ 524 | (0, Some((1, NonZeroU64::new(2).unwrap()))), // 0 525 | (2, None), // 1 526 | (3, Some((1, NonZeroU64::new(3).unwrap()))), // 2 527 | (6, None), // 3 528 | (7, Some((1, NonZeroU64::new(2).unwrap()))), // 4 529 | (9, None), // 5 530 | (10, Some((1, NonZeroU64::new(3).unwrap()))), // 6 531 | (13, None), // 7 532 | ]; 533 | let mut observed_output = Vec::new(); 534 | for input in input.into_iter() { 535 | observed_output.push(stretcher.stretch(input.0, input.1)); 536 | } 537 | let expected_output = vec![0, 1, 1, 2, 2, 3, 3, 5]; 538 | assert_eq!(expected_output, observed_output); 539 | } 540 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Rsynth 2 | //! An API abstraction for API's for audio plugins and applications. 3 | //! Use it to write real-time audio effects, software synthesizers, ... and target different platforms 4 | //! (vst, jack, offline audio rendering, ...). 5 | //! It is currently most suitable for real-time or "streaming" audio processing. 6 | //! E.g. you cannot use it to reverse audio in time. 7 | //! 8 | //! ## Back-ends 9 | //! `rsynth` currently supports the following back-ends: 10 | //! 11 | //! * [`jack`] (behind the `backend-jack` feature) 12 | //! * [`vst`] (behind the `backend-vst` feature) 13 | //! * [`combined`] combine different back-ends for audio input, audio output, midi input and 14 | //! midi output, mostly for offline rendering and testing (behind various features) 15 | //! 16 | //! See the documentation of each back-end for more information. 17 | //! 18 | //! ## Features and how to use them 19 | //! 20 | //! `rsynth` puts common functionality of the different backends behind common traits. 21 | //! Conversely, a plugin can be used for different backends by implementing common traits. 22 | //! A mix-and-match approach is used: if a backend doesn't require a certain functionality, 23 | //! you don't need the corresponding trait. 24 | //! 25 | //! ### Starting the backend/entry point for the host 26 | //! 27 | //! * Jack: [`run()`](./backend/jack_backend/fn.run.html) 28 | //! * Offline : [`run()`](backend/combined/fn.run.html) 29 | //! * VST 2.4: [`vst_init!`] 30 | //! 31 | //! ### Meta-data 32 | //! There are a number of traits that an application or plugin needs to implement in order to define 33 | //! meta-data. Every plugin or application should implement these, but it can be tedious, so you can 34 | //! implement these traits in a more straightforward way by implementing the [`Meta`] trait. 35 | //! However, you can also implement these trait "by hand". 36 | //! 37 | //! **Meta-data for Jack** 38 | //! 39 | //! Applications need to implement 40 | //! * [`CommonPluginMeta`] (name of the plugin etc) 41 | //! * [`AudioHandlerMeta`] (number of audio ports) 42 | //! * [`CommonAudioPortMeta`] (names of the audio in and out ports) 43 | //! * [`MidiHandlerMeta`] (number of midi ports) 44 | //! * [`CommonMidiPortMeta`] (names of the audio in and out ports) 45 | //! 46 | //! **No meta-data for offline rendering** 47 | //! 48 | //! Applications do not need to implement special traits describing meta-data. 49 | //! 50 | //! **Meta-data for VST 2.4** 51 | //! 52 | //! Plugins need to implement 53 | //! * [`CommonPluginMeta`] (name of the plugin etc) 54 | //! * [`AudioHandlerMeta`] (number of audio ports) 55 | //! * [`CommonAudioPortMeta`] (names of the audio in and out ports) 56 | //! * [`VstPluginMeta`] (vst-specific meta-data) 57 | //! 58 | //! ### Rendering audio 59 | //! All backends require the plugin/application to implement the [`ContextualAudioRenderer`] trait. 60 | //! [`ContextualAudioRenderer`] has two type parameters and the type parameter depends on the 61 | //! backends to use. 62 | //! One type parameter is the data type used to represent a sample. 63 | //! The other type parameter is called the "context" and can be used to access functionality of 64 | //! the backend in the audio rendering itself. 65 | //! Common functionality of the context is defined in the [`HostInterface`] trait. 66 | //! The application or plugin can have either a generic implementation of the [`ContextualAudioRenderer`] 67 | //! or choose to use different, specialized implementations if different behaviour is needed. 68 | //! 69 | //! **Rendering audio with Jack** 70 | //! 71 | //! Applications need to implement 72 | //! * [`AudioHandler`] 73 | //! * [`ContextualAudioRenderer`]`` 74 | //! 75 | //! **Rendering audio offline** 76 | //! 77 | //! Applications need to implement 78 | //! * [`ContextualAudioRenderer`]`>>` Note: the type parameter `S`, which represents the sample data type, is free. 79 | //! 80 | //! **Rendering audio with VST 2.4** 81 | //! 82 | //! Plugins need to implement 83 | //! * [`AudioHandler`] 84 | //! * [`ContextualAudioRenderer`]`` 85 | //! * [`ContextualAudioRenderer`]`` 86 | //! 87 | //! _Note_: [`HostCallback`] is re-exported from the vst crate, but implements `rsynth`'s 88 | //! [`HostInterface`], which defines functionality shared by all backends. 89 | //! 90 | //! ### Handling (midi) events 91 | //! A plugin or application can handle events (typically midi events) by implementing the 92 | //! [`ContextualEventHandler`] trait. This trait is generic over the event type. It also has 93 | //! a second type parameter, the context, which typically corresponds to the host, so that 94 | //! plugins or applications can have access to the host while handling events. 95 | //! 96 | //! **Handling events with Jack** 97 | //! 98 | //! Applications need to implement 99 | //! * [`ContextualEventHandler`]`<`[`Indexed`]`<`[`Timed`]`<`[`RawMidiEvent`]`>>, `[`JackHost`]`>`, 100 | //! * [`ContextualEventHandler`]`<`[`Indexed`]`<`[`Timed`]`<`[`SysExEvent`]`>>, `[`JackHost`]`>` 101 | //! 102 | //! **Handling events with the "offline" backend** 103 | //! 104 | //! Applications need to implement 105 | //! * [`EventHandler`]`<`[`Timed`]`<`[`RawMidiEvent`]`>>` 106 | //! 107 | //! _Note_: [`EventHandler`] is similar to [`ContextualEventHandler`], but without the context. 108 | //! We would like to make this more uniform in a future version and also require 109 | //! [`ContextualEventHandler`] here. 110 | //! 111 | //! **Handling events with VST 2.4** 112 | //! Plugins need to implement 113 | //! 114 | //! * [`ContextualEventHandler`]`<`[`Timed`]`<`[`RawMidiEvent`]`>, `[`HostCallback`]`>` and 115 | //! * [`ContextualEventHandler`]`<`[`Timed`]`<`[`SysExEvent`]`>, `[`HostCallback`]`>`. 116 | //! 117 | //! _Note_: VST 2.4 does not support sample-accurate events; a dummy timestamp of `0` is always added. 118 | //! 119 | //! _Note_: [`HostCallback`] is re-exported from the vst crate, but implements `rsynth`'s 120 | //! [`HostInterface`], which defines functionality shared by all backends. 121 | //! 122 | //! ### Generating midi events 123 | //! The "context" parameter passed in the methods from the [`ContextualAudioRenderer`] and 124 | //! [`ContextualEventHandler`] traits allows to access features from the host/backend, such as 125 | //! generating midi events. 126 | //! 127 | //! **Generating midi events with Jack** 128 | //! 129 | //! [`JackHost`] implements the following traits: 130 | //! 131 | //! * [`EventHandler`]`<`[`Indexed`]`<`[`Timed`]`<`[`RawMidiEvent`]`>>>` 132 | //! * [`EventHandler`]`<`[`Indexed`]`<`[`Timed`]`<`[`SysExEvent`]`>>>` 133 | //! 134 | //! **Generating midi events with offline rendering** 135 | //! 136 | //! [`MidiWriterWrapper`] implements 137 | //! * [`EventHandler`]`<`[`Timed`]`<`[`RawMidiEvent`]`>>` 138 | //! 139 | //! **Generating midi events with VST 2.4 is not possible** 140 | //! 141 | //! ### Stopping the backend 142 | //! The "context" parameter passed in the methods from the [`ContextualAudioRenderer`] and 143 | //! [`ContextualEventHandler`] traits allows to access features from the host/backend, such as 144 | //! stopping the backend. 145 | //! All "backends" implement the [`HostInterface`] trait, which defines a [`stop`] method. 146 | //! The [`stop`] method only actually does something if the backend additionally implements 147 | //! the [`Stop`] trait. 148 | //! 149 | //! **Stopping Jack** 150 | //! 151 | //! Stopping Jack is possible: [`JackHost`] implements the [`Stop`] trait. 152 | //! 153 | //! **Stopping offline rendering** 154 | //! 155 | //! Stopping offline rendering is possible: [`MidiWriterWrapper`] implements the [`Stop`] trait. 156 | //! Additionally, offline rendering automatically stops when the [`fill_buffer`] method of the 157 | //! [`AudioReader`] indicates that no frames are to be expected anymore. 158 | //! 159 | //! **Stopping VST 2.4 is not possible** 160 | //! 161 | //! [`jack`]: ./backend/jack_backend/index.html 162 | //! [`vst`]: ./backend/vst_backend/index.html 163 | //! [`combined`]: ./backend/combined/index.html 164 | //! [`EventHandler`]: ./event/trait.EventHandler.html 165 | //! [`RawMidiEvent`]: ./event/struct.RawMidiEvent.html 166 | //! [`SysExEvent`]: ./event/struct.SysExEvent.html 167 | //! [`Timed`]: ./event/struct.Timed.html 168 | //! [`Timed`]: ./event/struct.Timed.html 169 | //! [`Indexed`]: ./event/struct.Indexed.html 170 | //! [`Indexed`]: ./event/struct.Indexed.html 171 | //! [`CommonPluginMeta`]: ./trait.CommonPluginMeta.html 172 | //! [`AudioHandlerMeta`]: ./trait.AudioHandlerMeta.html 173 | //! [`MidiHandlerMeta`]: ./trait.MidiHandlerMeta.html 174 | //! [`CommonAudioPortMeta`]: ./trait.CommonAudioPortMeta.html 175 | //! [`Meta`]: ./meta/trait.Meta.html 176 | //! [`AudioRenderer`]: ./trait.AudioRenderer.html 177 | //! [`ContextualAudioRenderer`]: trait.ContextualAudioRenderer.html 178 | //! [`ContextualEventHandler`]: ./event/trait.ContextualEventHandler.html 179 | //! [`EventHandler`]: ./event/trait.EventHandler.html 180 | //! [`vst_init!`]: ./macro.vst_init.html 181 | //! [`jack_backend::run()`]: ./backend/jack_backend/fn.run.html 182 | //! [`combined::run()`]: backend/combined/fn.run.html 183 | //! [`HostCallback`]: ./backend/vst_backend/vst/plugin/struct.HostCallback.html 184 | //! [`HostInterface`]: ./backend/trait.HostInterface.html 185 | //! [`JackHost`]: ./backend/jack_backend/struct.JackHost.html 186 | //! [`AudioHandler`]: ./trait.AudioHandler.html 187 | //! [`MidiWriterWrapper`]: ./backend/combined/struct.MidiWriterWrapper.html 188 | //! [`CommonMidiPortMeta`]: ./trait.CommonMidiPortMeta.html 189 | //! [`VstPluginMeta`]: ./backend/vst_backend/trait.VstPluginMeta.html 190 | //! [`MidiWriterWrapper`]: ./backend/combined/struct.MidiWriterWrapper.html 191 | //! [`stop`]: ./backend/trait.HostInterface.html#method.stop 192 | //! [`Stop`]: ./backend/trait.Stop.html 193 | //! [`fill_buffer`]: ./backend/combined/trait.AudioReader.html#tymethod.fill_buffer 194 | //! [`AudioReader`]: ./backend/combined/trait.AudioReader.html 195 | 196 | #[macro_use] 197 | extern crate log; 198 | 199 | use crate::buffer::AudioBufferInOut; 200 | use crate::meta::{AudioPort, General, Meta, MidiPort, Name, Port}; 201 | use std::fmt::{Error, Write}; 202 | 203 | #[macro_use] 204 | pub mod buffer; 205 | pub mod backend; 206 | pub mod envelope; 207 | pub mod event; 208 | pub mod meta; 209 | pub mod test_utilities; 210 | pub mod utilities; 211 | 212 | /// Re-exports from the [`vecstorage`](https://crates.io/crates/vecstorage) crate. 213 | pub mod vecstorage { 214 | pub use vecstorage::VecStorage; 215 | } 216 | 217 | #[cfg(feature = "rsor-0-1")] 218 | /// Re-exports from the [`rsor`](https://crates.io/crates/rsor) crate. 219 | pub mod rsor { 220 | pub use rsor::Slice; 221 | } 222 | 223 | /// Define the maximum number of audio inputs and the maximum number of audio outputs. 224 | /// 225 | /// Backends that require the plugin to implement this trait ensure that when calling the 226 | /// [`render_buffer`] method of the [`AudioRenderer`] trait 227 | /// * the number of inputs channels (`buffer.number_of_input_channels()`) is smaller than or equal to 228 | /// `Self::max_number_of_audio_inputs()` and 229 | /// * the number of outputs (`buffer.number_of_output_channels()`) is smaller than or equal to 230 | /// `Self::max_number_of_audio_outputs()`. 231 | /// 232 | /// # Remark 233 | /// This trait can be more conveniently implemented by implementing the [`Meta`] trait. 234 | /// 235 | /// [`Meta`]: ./meta/trait.Meta.html 236 | /// [`render_buffer`]: ./trait.AudioRenderer.html#tymethod.render_buffer 237 | /// [`AudioRenderer`]: ./trait.AudioRenderer.html 238 | pub trait AudioHandlerMeta { 239 | /// The maximum number of audio inputs supported. 240 | /// This method should return the same value every time it is called. 241 | fn max_number_of_audio_inputs(&self) -> usize; 242 | 243 | /// The maximum number of audio outputs supported. 244 | /// This method should return the same value every time it is called. 245 | fn max_number_of_audio_outputs(&self) -> usize; 246 | } 247 | 248 | /// Define how sample-rate changes are handled. 249 | pub trait AudioHandler { 250 | /// Called when the sample-rate changes. 251 | /// The backend should ensure that this function is called before 252 | /// any other method. 253 | /// 254 | /// # Parameters 255 | /// `sample_rate`: The new sample rate in frames per second (Hz). 256 | /// Common sample rates are 44100 Hz (CD quality) and 48000 Hz. 257 | // TODO: Looking at the WikiPedia list https://en.wikipedia.org/wiki/Sample_rate, it seems that 258 | // TODO: there are no fractional sample rates. Maybe change the data type into u32? 259 | fn set_sample_rate(&mut self, sample_rate: f64); 260 | } 261 | 262 | /// Define the maximum number of midi inputs and the maximum number of midi outputs. 263 | /// This trait can be more conveniently implemented by implementing the [`Meta`] trait. 264 | /// 265 | /// [`Meta`]: ./meta/trait.Meta.html 266 | pub trait MidiHandlerMeta { 267 | /// The maximum number of midi inputs supported. 268 | /// This method should return the same value for subsequent calls. 269 | fn max_number_of_midi_inputs(&self) -> usize; 270 | 271 | /// The maximum number of midi outputs supported. 272 | /// This method should return the same value for subsequent calls. 273 | fn max_number_of_midi_outputs(&self) -> usize; 274 | } 275 | 276 | // TODO: Is this trait actually used? 277 | /// Defines how audio is rendered. 278 | /// 279 | /// The type parameter `S` refers to the data type of a sample. 280 | /// It is typically `f32` or `f64`. 281 | pub trait AudioRenderer 282 | where 283 | S: Copy, 284 | { 285 | /// This method is called repeatedly for subsequent audio buffers. 286 | fn render_buffer(&mut self, buffer: &mut AudioBufferInOut); 287 | } 288 | 289 | /// Defines how audio is rendered, similar to the [`AudioRenderer`] trait. 290 | /// The extra parameter `context` can be used by the backend to provide extra information. 291 | /// 292 | /// The type parameter `S` refers to the data type of a sample. 293 | /// It is typically `f32` or `f64`. 294 | /// 295 | /// [`AudioRenderer`]: ./trait.AudioRenderer.html 296 | pub trait ContextualAudioRenderer 297 | where 298 | S: Copy, 299 | { 300 | /// This method called repeatedly for subsequent buffers. 301 | /// 302 | /// It is similar to the [`render_buffer`] from the [`AudioRenderer`] trait, 303 | /// see its documentation for more information. 304 | /// 305 | /// [`AudioRenderer`]: ./trait.AudioRenderer.html 306 | /// [`render_buffer`]: ./trait.AudioRenderer.html#tymethod.render_buffer 307 | fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut Context); 308 | } 309 | 310 | /// Provides common meta-data of the plugin or application to the host. 311 | /// This trait is common for all backends that need this info. 312 | /// This trait can be more conveniently implemented by implementing the [`Meta`] trait. 313 | /// 314 | /// [`Meta`]: ./meta/trait.Meta.html 315 | pub trait CommonPluginMeta { 316 | /// The name of the plugin or application. 317 | /// 318 | #[deprecated(since = "0.1.2", note = "Use or implement `plugin_name` instead.")] 319 | fn name(&self) -> &str { 320 | "plugin_or_application" 321 | } 322 | 323 | /// The name of the plugin or application. 324 | /// 325 | /// # Compatibility note 326 | /// The default implementation of this method will likely be removed in a future release. 327 | fn plugin_name(&self, buffer: &mut W) -> Result<(), std::fmt::Error> { 328 | buffer.write_str(self.name()) 329 | } 330 | } 331 | 332 | /// Provides some meta-data of the audio-ports used by the plugin or application to the host. 333 | /// This trait can be more conveniently implemented by implementing the [`Meta`] trait. 334 | /// 335 | /// [`Meta`]: ./meta/trait.Meta.html 336 | pub trait CommonAudioPortMeta: AudioHandlerMeta { 337 | /// The name of the audio input with the given index. 338 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_audio_inputs()`]. 339 | /// 340 | /// # Note 341 | /// When using the Jack backend, this function should not return an empty string. 342 | /// 343 | /// [`Self::max_number_of_audio_inputs()`]: trait.AudioHandlerMeta.html#tymethod.max_number_of_audio_inputs 344 | #[deprecated(since = "0.1.2", note = "Use or implement `input_name` instead.")] 345 | fn audio_input_name(&self, index: usize) -> String { 346 | let mut result = String::new(); 347 | match self.input_name(&mut result, index) { 348 | Ok(_) => result, 349 | Err(_) => format!("audio in {}", index), 350 | } 351 | } 352 | 353 | /// The name of the audio input with the given index. 354 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_audio_inputs()`]. 355 | /// 356 | /// # Note 357 | /// When using the Jack backend, the name should not be an empty string. 358 | /// 359 | /// [`Self::max_number_of_audio_inputs()`]: trait.AudioHandlerMeta.html#tymethod.max_number_of_audio_inputs 360 | fn input_name( 361 | &self, 362 | buffer: &mut W, 363 | index: usize, 364 | ) -> Result<(), std::fmt::Error> { 365 | write!(buffer, "audio in {}", index) 366 | } 367 | 368 | /// The name of the audio output with the given index. 369 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_audio_outputs()`]. 370 | /// 371 | /// # Note 372 | /// When using the Jack backend, this function should not return an empty string. 373 | /// 374 | /// [`Self::max_number_of_audio_outputs()`]: ./trait.AudioHandlerMeta.html#tymethod.max_number_of_audio_outputs 375 | #[deprecated(since = "0.1.2", note = "Use or implement `output_name` instead.")] 376 | fn audio_output_name(&self, index: usize) -> String { 377 | let mut result = String::new(); 378 | match self.output_name(&mut result, index) { 379 | Ok(_) => result, 380 | Err(_) => format!("audio out {}", index), 381 | } 382 | } 383 | 384 | /// The name of the audio output with the given index. 385 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_audio_outputs()`]. 386 | /// 387 | /// # Note 388 | /// When using the Jack backend, the name should not be an empty string. 389 | /// 390 | /// [`Self::max_number_of_audio_outputs()`]: ./trait.AudioHandlerMeta.html#tymethod.max_number_of_audio_outputs 391 | fn output_name( 392 | &self, 393 | buffer: &mut W, 394 | index: usize, 395 | ) -> Result<(), std::fmt::Error> { 396 | write!(buffer, "audio out {}", index) 397 | } 398 | } 399 | 400 | /// Provides some meta-data of the midi-ports used by the plugin or application to the host. 401 | /// This trait can be more conveniently implemented by implementing the [`Meta`] trait. 402 | /// 403 | /// [`Meta`]: ./meta/trait.Meta.html 404 | pub trait CommonMidiPortMeta: MidiHandlerMeta { 405 | /// The name of the midi input with the given index. 406 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_midi_inputs()`]. 407 | /// 408 | /// # Note 409 | /// When using the Jack backend, this function should not return an empty string. 410 | /// 411 | /// # Note 412 | /// The default implementation of this method uses `input_name`. 413 | /// 414 | /// [`Self::max_number_of_midi_inputs()`]: trait.MidiHandlerMeta.html#tymethod.max_number_of_midi_inputs 415 | #[deprecated(since = "0.1.2", note = "Use or implement `input_name` instead.")] 416 | fn midi_input_name(&self, index: usize) -> String { 417 | let mut result = String::new(); 418 | match self.input_name(&mut result, index) { 419 | Ok(_) => result, 420 | Err(_) => format!("midi in {}", index), 421 | } 422 | } 423 | 424 | /// The name of the midi input with the given index. 425 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_midi_inputs()`]. 426 | /// 427 | /// # Note 428 | /// When using the Jack backend, the name should not be an empty string. 429 | /// 430 | /// [`Self::max_number_of_midi_inputs()`]: trait.MidiHandlerMeta.html#tymethod.max_number_of_midi_inputs 431 | fn input_name( 432 | &self, 433 | buffer: &mut W, 434 | index: usize, 435 | ) -> Result<(), std::fmt::Error> { 436 | write!(buffer, "midi in {}", index) 437 | } 438 | 439 | /// The name of the midi output with the given index. 440 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_midi_outputs()`] 441 | /// 442 | /// # Note 443 | /// When using the Jack backend, this function should not return an empty string. 444 | /// 445 | /// [`Self::max_number_of_midi_outputs()`]: ./trait.MidiHandlerMeta.html#tymethod.max_number_of_midi_outputs 446 | #[deprecated(since = "0.1.2", note = "Use or implement `output_name` instead.")] 447 | fn midi_output_name(&self, index: usize) -> String { 448 | let mut result = String::new(); 449 | match self.output_name(&mut result, index) { 450 | Ok(_) => result, 451 | Err(_) => format!("midi out {}", index), 452 | } 453 | } 454 | 455 | /// The name of the midi output with the given index. 456 | /// You can assume that `index` is strictly smaller than [`Self::max_number_of_midi_outputs()`] 457 | /// 458 | /// # Note 459 | /// When using the Jack backend, the name should not be an empty string. 460 | /// 461 | /// [`Self::max_number_of_midi_outputs()`]: ./trait.MidiHandlerMeta.html#tymethod.max_number_of_midi_outputs 462 | fn output_name( 463 | &self, 464 | buffer: &mut W, 465 | index: usize, 466 | ) -> Result<(), std::fmt::Error> { 467 | write!(buffer, "midi out {}", index) 468 | } 469 | } 470 | 471 | impl CommonPluginMeta for T 472 | where 473 | T: Meta, 474 | T::MetaData: General, 475 | <::MetaData as General>::GeneralData: Name, 476 | { 477 | fn name(&self) -> &str { 478 | self.meta().general().name() 479 | } 480 | 481 | fn plugin_name(&self, buffer: &mut W) -> Result<(), Error> { 482 | self.meta().general().write_name(buffer) 483 | } 484 | } 485 | 486 | impl AudioHandlerMeta for T 487 | where 488 | T: Meta, 489 | T::MetaData: Port, 490 | { 491 | fn max_number_of_audio_inputs(&self) -> usize { 492 | self.meta().in_ports().len() 493 | } 494 | 495 | fn max_number_of_audio_outputs(&self) -> usize { 496 | self.meta().out_ports().len() 497 | } 498 | } 499 | 500 | impl CommonAudioPortMeta for T 501 | where 502 | T: Meta, 503 | T::MetaData: Port, 504 | <::MetaData as Port>::PortData: Name, 505 | { 506 | fn input_name(&self, buffer: &mut W, index: usize) -> Result<(), Error> { 507 | self.meta().in_ports()[index].write_name(buffer) 508 | } 509 | 510 | fn output_name(&self, buffer: &mut W, index: usize) -> Result<(), Error> { 511 | self.meta().out_ports()[index].write_name(buffer) 512 | } 513 | } 514 | 515 | impl MidiHandlerMeta for T 516 | where 517 | T: Meta, 518 | T::MetaData: Port, 519 | { 520 | fn max_number_of_midi_inputs(&self) -> usize { 521 | self.meta().in_ports().len() 522 | } 523 | 524 | fn max_number_of_midi_outputs(&self) -> usize { 525 | self.meta().out_ports().len() 526 | } 527 | } 528 | 529 | impl CommonMidiPortMeta for T 530 | where 531 | T: Meta, 532 | T::MetaData: Port, 533 | <::MetaData as Port>::PortData: Name, 534 | { 535 | fn input_name(&self, buffer: &mut W, index: usize) -> Result<(), Error> { 536 | self.meta().in_ports()[index].write_name(buffer) 537 | } 538 | 539 | fn output_name(&self, buffer: &mut W, index: usize) -> Result<(), Error> { 540 | self.meta().out_ports()[index].write_name(buffer) 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /src/meta/mod.rs: -------------------------------------------------------------------------------- 1 | //! Mechanisms for defining the meta-data of a plugin or application. 2 | //! 3 | //! `rsynth` uses a hierarchy of different traits that allow your audio application 4 | //! or plug-in to define various aspects of the meta-data. 5 | //! 6 | //! Implementing each of these traits one by one can be rather tedious. 7 | //! For this reason, these traits have blanket impls, so that you only need 8 | //! to implement the [`Meta`] trait and in its implementation, return the 9 | //! meta-data. 10 | //! 11 | //! Example 12 | //! ------- 13 | //! ``` 14 | //! use rsynth::meta::{Meta, MetaData, InOut}; 15 | //! struct MyPlugin { 16 | //! meta: MetaData<&'static str, &'static str, &'static str> 17 | //! /* ... */ 18 | //! } 19 | //! 20 | //! impl MyPlugin { 21 | //! pub fn new() -> Self { 22 | //! Self { 23 | //! meta: MetaData { 24 | //! general_meta: unimplemented!(), 25 | //! audio_port_meta: InOut { 26 | //! inputs: vec![unimplemented!()], 27 | //! outputs: vec![unimplemented!()], 28 | //! }, 29 | //! midi_port_meta: InOut { 30 | //! inputs: vec![unimplemented!()], 31 | //! outputs: vec![unimplemented!()], 32 | //! }, 33 | //! } 34 | //! } 35 | //! } 36 | //! } 37 | //! 38 | //! impl Meta for MyPlugin { 39 | //! type MetaData = MetaData<&'static str, &'static str, &'static str>; 40 | //! fn meta(&self) -> &Self::MetaData { 41 | //! &self.meta 42 | //! } 43 | //! } 44 | //! ``` 45 | //! 46 | //! # How it works under the hood 47 | //! 48 | //! Back-ends may require the plugin to implement a number of traits concerning meta-data. 49 | //! Suppose for instance a backend requires plugins to implement the `CommonPluginMeta` trait. 50 | //! The `CommonPluginMeta` trait defines the "name" of the plugin. 51 | //! There is a blanket impl that implements the `CommonPluginMeta` for any type that 52 | //! implements `Meta` where the associated type `Meta::MetaData` implements the `General` trait 53 | //! (which allows getting general meta-data) where the associated type `General::GeneralData` 54 | //! implements the `Name` trait. 55 | //! Now the `MetaData` struct implements `General` with associated type 56 | //! `General::GeneralData` equal to `G`. 57 | //! Also, `Name` is implemented for `String` and for `&'static str`. 58 | //! So if a plugin implements `Meta` with the associated type `Meta::MetaData` equal to the struct 59 | //! `MetaData<&'static str, _, _>`, then it automatically implements `CommonPluginMeta`. 60 | 61 | use std::fmt::Error; 62 | 63 | /// Define the meta-data for an application or plug-in. 64 | /// 65 | /// See the [module level documentation] for more details. 66 | /// 67 | /// [module level documentation]: ./index.html 68 | pub trait Meta { 69 | /// The data-type that represents the meta-data. 70 | /// 71 | /// Note 72 | /// ---- 73 | /// In most cases, the struct [`MetaData`] can be used for this associated type. 74 | /// 75 | /// [`MetaData`]: ./struct.MetaData.html 76 | type MetaData; 77 | 78 | /// Get the meta-data. 79 | fn meta(&self) -> &Self::MetaData; 80 | } 81 | 82 | /// Define meta-data of an application or plugin as a whole. 83 | pub trait General { 84 | /// The data-type of the general meta-data. 85 | type GeneralData; 86 | /// Get the general meta-data. 87 | fn general(&self) -> &Self::GeneralData; 88 | } 89 | 90 | /// Implement this trait to indicate that a type can be used to represent 91 | /// meta-data that contains a name. 92 | pub trait Name { 93 | /// Get the name. 94 | #[deprecated(since = "0.1.2", note = "Use or implement `write_name` instead.")] 95 | fn name(&self) -> &str { 96 | "" 97 | } 98 | 99 | /// Write the name to the given buffer. 100 | /// 101 | /// # Compatibility note 102 | /// The default implementation of this method will likely be removed in a future release. 103 | fn write_name(&self, buffer: &mut W) -> Result<(), std::fmt::Error> { 104 | buffer.write_str(self.name()) 105 | } 106 | } 107 | 108 | impl Name for String { 109 | fn name(&self) -> &str { 110 | self 111 | } 112 | 113 | fn write_name(&self, buffer: &mut W) -> Result<(), Error> { 114 | buffer.write_str(&self) 115 | } 116 | } 117 | 118 | impl Name for &'static str { 119 | fn name(&self) -> &str { 120 | self 121 | } 122 | 123 | fn write_name(&self, buffer: &mut W) -> Result<(), Error> { 124 | buffer.write_str(self) 125 | } 126 | } 127 | 128 | /// Define meta-data for input ports and output ports. 129 | /// 130 | /// The type parameter `T` is a dummy type parameter so that meta-data for different types of 131 | /// ports can be defined. 132 | /// Typical values for `T` are [`MidiPort`] and [`AudioPort`]. 133 | /// 134 | /// Example 135 | /// ------- 136 | /// ``` 137 | /// use rsynth::meta::{Port, MidiPort, AudioPort}; 138 | /// struct MyMetaData { 139 | /// audio_input_port_names: Vec, 140 | /// audio_output_port_names: Vec, 141 | /// midi_input_port_names: Vec, 142 | /// midi_output_port_names: Vec, 143 | /// } 144 | /// 145 | /// impl Port for MyMetaData { 146 | /// type PortData = String; 147 | /// fn in_ports(&self) -> &[Self::PortData] { 148 | /// self.audio_input_port_names.as_slice() 149 | /// } 150 | /// fn out_ports(&self) -> &[Self::PortData] { 151 | /// self.audio_output_port_names.as_slice() 152 | /// } 153 | /// } 154 | /// 155 | /// impl Port for MyMetaData { 156 | /// type PortData = String; 157 | /// fn in_ports(&self) -> &[Self::PortData] { 158 | /// self.audio_input_port_names.as_slice() 159 | /// } 160 | /// fn out_ports(&self) -> &[Self::PortData] { 161 | /// self.audio_output_port_names.as_slice() 162 | /// } 163 | /// } 164 | /// ``` 165 | /// 166 | /// Note 167 | /// ---- 168 | /// For most use cases, you can use the pre-defined [`MetaData`] struct, which already implements 169 | /// `Port` and `Port`. 170 | /// 171 | /// [`MidiPort`]: ./struct.MidiPort.html 172 | /// [`AudioPort`]: ./struct.AudioPort.html 173 | /// [`MetaData`]: ./struct.MetaData.html 174 | pub trait Port { 175 | type PortData; 176 | fn in_ports(&self) -> &[Self::PortData]; 177 | fn out_ports(&self) -> &[Self::PortData]; 178 | } 179 | 180 | /// A "marker" struct to be used as a type parameter for the [`Port`] trait, indicating 181 | /// that this implementation of [`Port`] defines meta-data for an audio port. 182 | /// 183 | /// [`Port`]: ./trait.Port.html 184 | pub struct AudioPort; 185 | 186 | /// A "marker" struct to be used as a type parameter for the [`Port`] trait, indicating 187 | /// that this implementation of [`Port`] defines meta-data for a midi port. 188 | /// 189 | /// [`Port`]: ./trait.Port.html 190 | pub struct MidiPort; 191 | 192 | /// Represents general-purpose meta-data of an audio application or plugin. 193 | /// 194 | /// See the [module level documentation] for an example. 195 | /// 196 | /// The struct `MetaData` has three type parameters: 197 | /// * `G`: the data type of the "general" meta-data. 198 | /// * `AP`: the data type of the meta-data for the audio ports 199 | /// * `MP`: the data type of the meta-data for the midi ports 200 | /// [module level documentation]: ./index.html 201 | pub struct MetaData { 202 | /// The meta-data about the application or plugin as a whole. 203 | pub general_meta: G, 204 | /// Meta-data about the audio ports. 205 | pub audio_port_meta: InOut, 206 | /// Meta-data about the midi ports. 207 | pub midi_port_meta: InOut, 208 | } 209 | 210 | /// Represents meta-data about a input and output ports. 211 | /// 212 | /// See the documentation of the [`MetaData`] struct for more information. 213 | /// 214 | /// [`MetaData`]: ./struct.MetaData.html 215 | pub struct InOut { 216 | /// Meta-data of the input ports. 217 | pub inputs: Vec, 218 | /// Meta-data of the output ports. 219 | pub outputs: Vec, 220 | } 221 | 222 | impl General for MetaData { 223 | type GeneralData = G; 224 | fn general(&self) -> &G { 225 | &self.general_meta 226 | } 227 | } 228 | 229 | impl Port for MetaData { 230 | type PortData = AP; 231 | fn in_ports(&self) -> &[AP] { 232 | self.audio_port_meta.inputs.as_ref() 233 | } 234 | 235 | fn out_ports(&self) -> &[AP] { 236 | self.audio_port_meta.outputs.as_ref() 237 | } 238 | } 239 | 240 | impl Port for MetaData { 241 | type PortData = MP; 242 | 243 | fn in_ports(&self) -> &[MP] { 244 | self.midi_port_meta.inputs.as_ref() 245 | } 246 | 247 | fn out_ports(&self) -> &[MP] { 248 | self.midi_port_meta.outputs.as_ref() 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/test_utilities/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for testing. 2 | 3 | use crate::buffer::{AudioBufferInOut, AudioChunk}; 4 | use crate::event::{ContextualEventHandler, EventHandler}; 5 | use crate::{AudioHandler, AudioHandlerMeta, ContextualAudioRenderer}; 6 | use std::fmt::Debug; 7 | 8 | pub struct DummyEventHandler; 9 | 10 | impl EventHandler for DummyEventHandler { 11 | fn handle_event(&mut self, _event: E) {} 12 | } 13 | 14 | impl ContextualEventHandler for DummyEventHandler { 15 | fn handle_event(&mut self, _event: E, _context: &mut C) {} 16 | } 17 | 18 | /// A plugin useful for writing automated tests. 19 | // TODO: Add more documentation. 20 | pub struct TestPlugin { 21 | expected_inputs: Vec>, 22 | provided_outputs: Vec>, 23 | expected_events: Vec>, 24 | provided_events: Vec>, 25 | meta: M, 26 | buffer_index: usize, 27 | event_index: usize, 28 | } 29 | 30 | impl TestPlugin { 31 | pub fn new( 32 | expected_inputs: Vec>, 33 | provided_outputs: Vec>, 34 | expected_events: Vec>, 35 | provided_events: Vec>, 36 | meta: M, 37 | ) -> Self { 38 | assert_eq!(expected_inputs.len(), provided_outputs.len(), "When constructing a test plugin, `expected_inputs` and `provided_outputs should have the same length."); 39 | assert!(expected_inputs.len() <= expected_events.len(), "When constructing a test plugin, `expected_inputs` and `provided_outputs` should be smaller in length than `expected_events`."); 40 | assert!(expected_inputs.len() + 1 >= expected_events.len(), "When constructing a test plugin, `expected_events` can have only one element more than `expected_inputs` and `provided_outputs`."); 41 | assert_eq!(expected_inputs.len(), provided_events.len(), "When constructing a test plugin, `expected_inputs`, `provided_outputs` and `provided_events` should all have the same length."); 42 | TestPlugin { 43 | expected_inputs, 44 | provided_outputs, 45 | expected_events, 46 | provided_events, 47 | meta, 48 | buffer_index: 0, 49 | event_index: 0, 50 | } 51 | } 52 | 53 | pub fn check_last(&self) { 54 | assert_eq!(self.buffer_index, self.expected_inputs.len()); 55 | } 56 | } 57 | 58 | impl AudioHandlerMeta for TestPlugin 59 | where 60 | M: AudioHandlerMeta, 61 | { 62 | fn max_number_of_audio_inputs(&self) -> usize { 63 | self.meta.max_number_of_audio_inputs() 64 | } 65 | fn max_number_of_audio_outputs(&self) -> usize { 66 | self.meta.max_number_of_audio_outputs() 67 | } 68 | } 69 | 70 | impl AudioHandler for TestPlugin 71 | where 72 | M: AudioHandler, 73 | { 74 | fn set_sample_rate(&mut self, sample_rate: f64) { 75 | self.meta.set_sample_rate(sample_rate); 76 | } 77 | } 78 | 79 | impl ContextualAudioRenderer for TestPlugin 80 | where 81 | S: PartialEq + Debug + Copy, 82 | C: EventHandler, 83 | { 84 | fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut C) { 85 | assert!( 86 | self.buffer_index < self.expected_inputs.len(), 87 | "`render_buffer` called more often than expected: expected only {} times", 88 | self.expected_inputs.len() 89 | ); 90 | 91 | assert_eq!( 92 | self.event_index, 93 | self.expected_events[self.buffer_index].len(), 94 | "`handle_event` called {} times for buffer {} (zero-based), but {} times was expected", 95 | self.event_index, 96 | self.buffer_index, 97 | self.expected_events[self.buffer_index].len() 98 | ); 99 | self.event_index = 0; 100 | 101 | let expected_input_channels = &self.expected_inputs[self.buffer_index].channels(); 102 | assert_eq!( 103 | buffer.number_of_input_channels(), 104 | expected_input_channels.len(), 105 | "`render_buffer` called with {} input channels, but {} were expected", 106 | buffer.number_of_input_channels(), 107 | expected_input_channels.len() 108 | ); 109 | for (input_channel_index, input_channel) in buffer.inputs().channels().iter().enumerate() { 110 | let expected_input_channel = &expected_input_channels[input_channel_index]; 111 | assert_eq!( 112 | input_channel.len(), 113 | expected_input_channel.len(), 114 | "mismatch in input channel #{} in buffer #{}: \ 115 | expected input channel with length {}, but got one with length {}", 116 | input_channel_index, 117 | self.buffer_index, 118 | input_channel.len(), 119 | expected_input_channel.len() 120 | ); 121 | for (sample_index, sample) in input_channel.iter().enumerate() { 122 | assert_eq!( 123 | *sample, 124 | expected_input_channel[sample_index], 125 | "mismatch in input sample with index #{} in channel #{} in buffer #{}: \ 126 | expected {:?} but got {:?}", 127 | sample_index, 128 | input_channel_index, 129 | self.buffer_index, 130 | expected_input_channel[sample_index], 131 | sample 132 | ); 133 | } 134 | } 135 | 136 | let expected_output_channels = self.provided_outputs[self.buffer_index].channels(); 137 | assert_eq!( 138 | buffer.number_of_output_channels(), 139 | expected_output_channels.len() 140 | ); 141 | // TODO: Use an iterator here. 142 | for output_channel_index in 0..expected_output_channels.len() { 143 | let expected_output_channel = &expected_output_channels[output_channel_index]; 144 | assert_eq!( 145 | buffer.number_of_frames(), 146 | expected_output_channel.len(), 147 | "mismatch in output channel #{} in buffer #{}: \ 148 | expected one with length {}, but got one with length {}", 149 | output_channel_index, 150 | self.buffer_index, 151 | expected_output_channel.len(), 152 | buffer.number_of_frames() 153 | ); 154 | buffer 155 | .outputs() 156 | .index_channel(output_channel_index) 157 | .copy_from_slice(expected_output_channel); 158 | } 159 | 160 | let events = self.provided_events.drain(..1).next().unwrap(); 161 | for event in events { 162 | context.handle_event(event); 163 | } 164 | 165 | self.buffer_index += 1; 166 | self.event_index = 0; 167 | } 168 | } 169 | 170 | impl EventHandler for TestPlugin 171 | where 172 | E: PartialEq + Debug, 173 | { 174 | fn handle_event(&mut self, event: E) { 175 | assert!( 176 | self.buffer_index < self.expected_events.len(), 177 | "`handle_event` is called after {} calls to `render_buffer`; this is unexpected", 178 | self.expected_events.len() 179 | ); 180 | let expected_events_for_this_buffer = &self.expected_events[self.buffer_index]; 181 | assert!( 182 | self.event_index < expected_events_for_this_buffer.len(), 183 | "`handle_events` is called more than {0} times after {1} calls to `render_buffer`; \ 184 | only {0} times are expected because we expect only \ 185 | {0} events for the subsequent buffer", 186 | expected_events_for_this_buffer.len(), 187 | self.buffer_index 188 | ); 189 | assert_eq!( 190 | event, 191 | expected_events_for_this_buffer[self.event_index], 192 | "mismatch for event #{} after {} calls to `render_buffer`: \ 193 | expected {:?} but got {:?}.", 194 | self.event_index, 195 | self.buffer_index, 196 | expected_events_for_this_buffer[self.event_index], 197 | event 198 | ); 199 | self.event_index += 1; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/utilities/mod.rs: -------------------------------------------------------------------------------- 1 | #![deprecated( 2 | since = "0.1.1", 3 | note = "Deprecated in favour of the dedicated `polyphony` crate." 4 | )] 5 | pub mod polyphony; 6 | -------------------------------------------------------------------------------- /src/utilities/polyphony.rs: -------------------------------------------------------------------------------- 1 | //! Utility to facilitate genarating different sounds at the same time (polyphony). 2 | //! 3 | //! Polyphony consists of different steps: 4 | //! 5 | //! 1. Classify how the event should be dispatched. 6 | //! How exactly it should be classified, is defined by the `EventDispatchClass` enum. 7 | //! The dispatching itself is done by a type that implements the `EventDispatchClassifier` trait. 8 | //! 2. Next, a voice should be assigned to the event. 9 | //! The `VoiceAssigner` trait defines this action. 10 | //! 3. Then, the event can be dispatched. 11 | //! The `EventDispatcher` trait and the `ContextualEventDispatcher` trait define 12 | //! methods for doing this. 13 | //! 14 | //! # Example of using polyphony 15 | //! 16 | //! The following example illustrates a plugin (or application) that has multiple voices that 17 | //! correspond to different tones. 18 | //! 19 | //! ``` 20 | //! use rsynth::utilities::polyphony::{Voice, EventDispatchClassifier, ToneIdentifier, 21 | //! RawMidiEventToneIdentifierDispatchClassifier, ContextualEventDispatcher}; 22 | //! use rsynth::utilities::polyphony::simple_event_dispatching::SimpleVoiceState; 23 | //! use rsynth::utilities::polyphony::simple_event_dispatching::SimpleEventDispatcher; 24 | //! use rsynth::event::{ContextualEventHandler, Indexed, Timed, RawMidiEvent}; 25 | //! use rsynth::ContextualAudioRenderer; 26 | //! use rsynth::buffer::AudioBufferInOut; 27 | //! 28 | //! struct MyVoice { 29 | //! // ... 30 | //! } 31 | //! 32 | //! impl Voice> for MyVoice { 33 | //! fn state(&self) -> SimpleVoiceState { 34 | //! // Let the event dispatcher know what state this voice is in. 35 | //! unimplemented!(); 36 | //! } 37 | //! } 38 | //! 39 | //! impl ContextualEventHandler, Context> for MyVoice { 40 | //! fn handle_event(&mut self, event: Timed, context: &mut Context) { 41 | //! // Here you typically change the state of the voice. 42 | //! unimplemented!() 43 | //! } 44 | //! } 45 | //! 46 | //! impl ContextualAudioRenderer for MyVoice { 47 | //! fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut Context) { 48 | //! // Render one voice. 49 | //! unimplemented!() 50 | //! } 51 | //! } 52 | //! 53 | //! struct MyPlugin { 54 | //! voices: Vec, 55 | //! // ... 56 | //! } 57 | //! 58 | //! impl ContextualEventHandler>, Context> for MyPlugin 59 | //! { 60 | //! fn handle_event(&mut self, event: Indexed>, context: &mut Context) { 61 | //! let mut dispatcher = SimpleEventDispatcher::new(RawMidiEventToneIdentifierDispatchClassifier); 62 | //! // Here we simply pass the context that we're given, but you can also pass a custom 63 | //! // context that uses shared data that is stored in `self`. 64 | //! dispatcher.dispatch_contextual_event(event.event, &mut self.voices, context); 65 | //! } 66 | //! } 67 | //! 68 | //! impl ContextualAudioRenderer for MyPlugin 69 | //! { 70 | //! fn render_buffer(&mut self, buffer: &mut AudioBufferInOut, context: &mut Context) { 71 | //! for voice in self.voices.iter_mut() { 72 | //! // Here we simply pass the context that we're given, but you can also pass a custom 73 | //! // context that uses shared data that is stored in `self`. 74 | //! voice.render_buffer(buffer, context); 75 | //! } 76 | //! } 77 | //! } 78 | //! 79 | //! ``` 80 | 81 | use crate::event::{ContextualEventHandler, EventHandler, RawMidiEvent}; 82 | use midi_consts::channel_event::*; 83 | 84 | pub enum EventDispatchClass { 85 | Broadcast, 86 | AssignNewVoice(Identifier), 87 | VoiceSpecific(Identifier), 88 | ReleaseVoice(Identifier), 89 | } 90 | 91 | /// Used to dispatch polyphonic event to the correct voice, based on the tone of the event. 92 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 93 | pub struct ToneIdentifier(pub u8); 94 | 95 | pub trait EventDispatchClassifier 96 | where 97 | Event: Copy, 98 | { 99 | type VoiceIdentifier: Eq + Copy; 100 | 101 | fn classify(&self, event: &Event) -> EventDispatchClass; 102 | } 103 | 104 | #[derive(Default)] 105 | pub struct RawMidiEventToneIdentifierDispatchClassifier; 106 | 107 | impl EventDispatchClassifier for RawMidiEventToneIdentifierDispatchClassifier 108 | where 109 | Event: AsRef + Copy, 110 | { 111 | type VoiceIdentifier = ToneIdentifier; 112 | 113 | fn classify(&self, event: &Event) -> EventDispatchClass { 114 | let data = event.as_ref().data(); 115 | match data[0] & EVENT_TYPE_MASK { 116 | NOTE_OFF => EventDispatchClass::ReleaseVoice(ToneIdentifier(data[1])), 117 | NOTE_ON => { 118 | if data[2] == 0 { 119 | // Velocity 0 is considered the same as note off. 120 | EventDispatchClass::ReleaseVoice(ToneIdentifier(data[1])) 121 | } else { 122 | EventDispatchClass::AssignNewVoice(ToneIdentifier(data[1])) 123 | } 124 | } 125 | POLYPHONIC_KEY_PRESSURE => EventDispatchClass::VoiceSpecific(ToneIdentifier(data[1])), 126 | _ => EventDispatchClass::Broadcast, 127 | } 128 | } 129 | } 130 | 131 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 132 | pub enum VoiceAssignment { 133 | None, 134 | All, 135 | Some(usize), 136 | } 137 | 138 | /// Implement this trait to inform the polyphonic event dispatcher what state this voice is in. 139 | pub trait Voice { 140 | fn state(&self) -> State; 141 | } 142 | 143 | pub trait VoiceAssigner: EventDispatchClassifier 144 | where 145 | Event: Copy, 146 | { 147 | type Voice; 148 | 149 | fn assign_event(&mut self, event: Event, voices: &mut [Self::Voice]) -> VoiceAssignment { 150 | match self.classify(&event) { 151 | EventDispatchClass::Broadcast => VoiceAssignment::All, 152 | EventDispatchClass::VoiceSpecific(identifier) 153 | | EventDispatchClass::ReleaseVoice(identifier) => { 154 | match self.find_active_voice(identifier, voices) { 155 | Some(index) => VoiceAssignment::Some(index), 156 | None => VoiceAssignment::None, 157 | } 158 | } 159 | EventDispatchClass::AssignNewVoice(identifier) => { 160 | VoiceAssignment::Some(self.find_idle_voice(identifier, voices)) 161 | } 162 | } 163 | } 164 | 165 | fn find_active_voice( 166 | &mut self, 167 | identifier: Self::VoiceIdentifier, 168 | voices: &mut [Self::Voice], 169 | ) -> Option; 170 | 171 | fn find_idle_voice( 172 | &mut self, 173 | identifier: Self::VoiceIdentifier, 174 | voices: &mut [Self::Voice], 175 | ) -> usize; 176 | } 177 | 178 | pub trait EventDispatcher: VoiceAssigner 179 | where 180 | Event: Copy, 181 | Self::Voice: EventHandler, 182 | { 183 | fn dispatch_event(&mut self, event: Event, voices: &mut [Self::Voice]) { 184 | match self.assign_event(event, voices) { 185 | VoiceAssignment::None => {} 186 | VoiceAssignment::Some(index) => { 187 | voices[index].handle_event(event); 188 | } 189 | VoiceAssignment::All => { 190 | for voice in voices { 191 | voice.handle_event(event); 192 | } 193 | } 194 | } 195 | } 196 | } 197 | 198 | pub trait ContextualEventDispatcher: VoiceAssigner 199 | where 200 | Event: Copy, 201 | Self::Voice: ContextualEventHandler, 202 | { 203 | /// Dispatch an event to the voice or voices that should handle it. 204 | fn dispatch_contextual_event( 205 | &mut self, 206 | event: Event, 207 | voices: &mut [Self::Voice], 208 | context: &mut Context, 209 | ) { 210 | match self.assign_event(event, voices) { 211 | VoiceAssignment::None => {} 212 | VoiceAssignment::Some(index) => { 213 | voices[index].handle_event(event, context); 214 | } 215 | VoiceAssignment::All => { 216 | for voice in voices { 217 | voice.handle_event(event, context); 218 | } 219 | } 220 | } 221 | } 222 | } 223 | 224 | /// Some basic event dispatching. 225 | pub mod simple_event_dispatching { 226 | use super::{ 227 | ContextualEventDispatcher, EventDispatchClass, EventDispatchClassifier, EventDispatcher, 228 | Voice, VoiceAssigner, 229 | }; 230 | use crate::event::{ContextualEventHandler, EventHandler}; 231 | use std::marker::PhantomData; 232 | 233 | /// A simple voice state 234 | #[derive(Clone, Copy, PartialEq, Eq)] 235 | pub enum SimpleVoiceState 236 | where 237 | VoiceIdentifier: Copy + Eq, 238 | { 239 | /// The voice is idle (in other words: doing nothing). 240 | Idle, 241 | /// The voice has received a signal to stop, but is still rendering audio (e.g. some reverb 242 | /// after the end of the audio). 243 | /// 244 | /// The `VoiceIdentifier` indicates what it is still rendering. 245 | Releasing(VoiceIdentifier), 246 | /// The voice has not yet received a signal to stop and is still rendering audio. 247 | Active(VoiceIdentifier), 248 | } 249 | 250 | /// A simple event dispatcher. 251 | /// 252 | /// The type parameter `Classifier` refers to the classifier that is used to classify events. 253 | /// In order to use this `SimpleEventDispatcher`, 254 | /// the concrete type used for `Classifier` should implement the `EventDispatchClassifier` trait. 255 | /// 256 | /// The type parameter `V` refers to the voice. 257 | pub struct SimpleEventDispatcher { 258 | classifier: Classifier, 259 | _voice_phantom: PhantomData, 260 | } 261 | 262 | impl SimpleEventDispatcher { 263 | pub fn new(classifier: Classifier) -> Self { 264 | Self { 265 | classifier, 266 | _voice_phantom: PhantomData, 267 | } 268 | } 269 | } 270 | 271 | impl Default for SimpleEventDispatcher 272 | where 273 | Classifier: Default, 274 | { 275 | fn default() -> Self { 276 | Self { 277 | classifier: Classifier::default(), 278 | _voice_phantom: PhantomData, 279 | } 280 | } 281 | } 282 | 283 | impl EventDispatchClassifier 284 | for SimpleEventDispatcher 285 | where 286 | Classifier: EventDispatchClassifier, 287 | Event: Copy, 288 | { 289 | type VoiceIdentifier = Classifier::VoiceIdentifier; 290 | 291 | fn classify(&self, event: &Event) -> EventDispatchClass { 292 | self.classifier.classify(event) 293 | } 294 | } 295 | 296 | impl VoiceAssigner for SimpleEventDispatcher 297 | where 298 | Classifier: EventDispatchClassifier, 299 | V: Voice>, 300 | Event: Copy, 301 | { 302 | type Voice = V; 303 | 304 | fn find_active_voice( 305 | &mut self, 306 | identifier: Self::VoiceIdentifier, 307 | voices: &mut [Self::Voice], 308 | ) -> Option { 309 | voices 310 | .iter() 311 | .position(|voice| voice.state() == SimpleVoiceState::Active(identifier)) 312 | // TODO: what should we do when we receive an event for a voice that 313 | // is already releasing? 314 | } 315 | 316 | fn find_idle_voice( 317 | &mut self, 318 | _identifier: Self::VoiceIdentifier, 319 | voices: &mut [Self::Voice], 320 | ) -> usize { 321 | let mut second_best = 0; 322 | for (index, voice) in voices.iter().enumerate() { 323 | match voice.state() { 324 | SimpleVoiceState::Idle => { 325 | return index; 326 | } 327 | SimpleVoiceState::Releasing(_) => { 328 | second_best = index; 329 | } 330 | SimpleVoiceState::Active(_) => {} 331 | } 332 | } 333 | second_best 334 | } 335 | } 336 | 337 | impl ContextualEventDispatcher 338 | for SimpleEventDispatcher 339 | where 340 | Classifier: EventDispatchClassifier, 341 | V: Voice> 342 | + ContextualEventHandler, 343 | Event: Copy, 344 | { 345 | } 346 | 347 | impl EventDispatcher for SimpleEventDispatcher 348 | where 349 | Classifier: EventDispatchClassifier, 350 | V: Voice> + EventHandler, 351 | Event: Copy, 352 | { 353 | } 354 | } 355 | --------------------------------------------------------------------------------