audio_processor_standalone_midi/
lib.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23//! [![crates.io](https://img.shields.io/crates/v/audio-processor-standalone-midi.svg)](https://crates.io/crates/audio-processor-standalone-midi)
24//! [![docs.rs](https://docs.rs/audio-processor-standalone-midi/badge.svg)](https://docs.rs/audio-processor-standalone-midi/)
25//! - - -
26//!
27//! This crate provides conversion into the VST types, which is to allow a VST host to use it. This is provided by the
28//! `MidiVSTConverter`.
29//!
30//! Wraps `midir` to provide MIDI input handling. The host may be started with `MidiHost`.
31//!
32//! When MIDI messages are received, they're pushed onto a lock-free `atomic_queue::Queue`. The messages are picked-up in
33//! the audio-thread by `MidiAudioThreadHandler`.
34//!
35//! It provides easy stand-alone MIDI integration with:
36//!
37//! * `audio-processor-traits` - `MidiEventHandler` trait
38//! * `rust-vst` - `PluginInstance`
39//!
40//! Currently, MIDI messages over 3 bytes are dropped by this host. In addition, the queue is bounded & a size must be
41//! provided. `Default` implementations will use a MIDI queue capacity of 100.
42//! This is a stand-alone MIDI host with helpers for doing real-time handling of MIDI messages in
43//! the audio-thread and to integrate with VST plugins.
44//!
45//! This is part of https://github.com/yamadapc/augmented-audio.
46//!
47//! # Memory Safety
48//! In order to integrate with the VST C API, this crate does manual memory allocation and handling. This is due to VST
49//! event types being unrepresentable as safe Rust constructs (and due to real-time safety being required as well).
50//!
51//! # Real-time Safety
52//! This crate provides the `host` side, which is the MIDI host. This host allocates when it receives messages from `midir`.
53//!
54//! The events are forwarded onto a lock-free queue (`atomic_queue`).
55//!
56//! On the `audio_thread` and `vst` modules, past construction methods that should be called on the audio-thread will not
57//! (de)-allocate. This is tested using the `assert_no_alloc` crate.
58//!
59//! In addition, `basedrop` / `audio_garbage_collector` are used to prevent de-allocation from happening on the
60//! audio-thread.
61//!
62//! # Test coverage
63//! The crate has unit-tests (though it should be considered experimental as all of this repository).
64//!
65//! Test coverage is at 80%.
66//!
67//! # Actix & Actors
68//! The `MidiHost` exposes an actix API, to be used with the actix actor system. This is to ease communicating with the midi
69//! handler from multiple threads.
70//!
71//! # Usage
72//! To use this crate:
73//!
74//! * [`basedrop::Collector`] You'll need to set-up `basedrop` or `audio-garbage-collector`
75//! * [`host::MidiHost`] should be created
76//!   - This will connect to inputs & push messages to a queue
77//! * [`audio_thread::MidiAudioThreadHandler`] On your audio thread you should pop from the queue
78//!   - This is enough to add MIDI to a standalone [`audio_processor_traits::MidiEventHandler`]
79//! * [`vst::MidiVSTConverter`] If you're implementing a host, you'll have to convert messages onto
80//!   the VST API
81//!
82//! ```
83//! fn example() {
84//!     use audio_processor_standalone_midi::audio_thread::MidiAudioThreadHandler;
85//!     use audio_processor_standalone_midi::host::MidiHost;
86//!     use audio_processor_standalone_midi::vst::MidiVSTConverter;
87//!     use basedrop::Collector;
88//!
89//!     // GC ======================================================================================
90//!     // See `audio-garbage-collector` for an easier to use wrapper on top of this.
91//!     //
92//!     // `Collector` will let us use reference counted values on the audio-thread, which will be
93//!     // pushed to a queue for de-allocation. You must set-up a background thread that forces it
94//!     // to actually collect garbage from the queue.
95//!     let gc = Collector::new();
96//!     let handle = gc.handle();
97//!
98//!     // Host ====================================================================================
99//!     // A host may be created with `new` or `default_with_handle`.
100//!     let mut host = MidiHost::default_with_handle(&handle);
101//!     // It'll connect to all MIDI input ports when started
102//!     // host.start().expect("Failed to connect");
103//!     // The host will push messages onto a lock-free queue. This is a reference counted value.
104//!     let midi_messages_queue = host.messages().clone();
105//!
106//!     // Audio-thread ============================================================================
107//!     // ...
108//!     // Within your audio-thread
109//!     // ...
110//!     // You'll want to share a `MidiAudioThreadHandler` between process calls as it pre-allocates
111//!     // buffers.
112//!     let mut midi_audio_thread_handler = MidiAudioThreadHandler::default();
113//!
114//!     // On each tick you'll call collect
115//!     midi_audio_thread_handler.collect_midi_messages(&midi_messages_queue);
116//!     // You'll get the MIDI message buffer
117//!     let midi_messages = midi_audio_thread_handler.buffer();
118//!     // ^ This is a `&Vec<MidiMessageEntry>`. If you're using `audio-processor-traits`, you can
119//!     //   pass this into any `MidiEventHandler` implementor.
120//!
121//!     // VST interop =============================================================================
122//!     // If you want to interface with a VST plugin, you'll need to convert messages into the
123//!     // C-api.
124//!     // ...
125//!     // You'll want to share this between process calls as it pre-allocates buffers.
126//!     let mut midi_vst_converter = MidiVSTConverter::default();
127//!     midi_vst_converter.accept(&midi_messages);
128//!     // This can be passed into VST plugins.
129//!     let events: &vst::api::Events = midi_vst_converter.events();
130//! }
131//! ```
132
133/// Audio-thread handling of messages
134pub mod audio_thread;
135/// Defaults
136pub mod constants;
137/// Hosting of MIDI
138pub mod host;
139
140#[cfg(feature = "vst")]
141/// VST API conversion
142pub mod vst;
143
144#[cfg(all(test, debug_assertions))]
145pub(crate) mod test_util;