This crate provides conversion into the VST types, which is to allow a VST host to use it. This is provided by the
MidiVSTConverter
.
Wraps midir
to provide MIDI input handling. The host may be started with MidiHost
.
When MIDI messages are received, they're pushed onto a lock-free atomic_queue::Queue
. The messages are picked-up in
the audio-thread by MidiAudioThreadHandler
.
It provides easy stand-alone MIDI integration with:
audio-processor-traits
-MidiEventHandler
traitrust-vst
-PluginInstance
Currently, MIDI messages over 3 bytes are dropped by this host. In addition, the queue is bounded & a size must be
provided. Default
implementations will use a MIDI queue capacity of 100.
This is a stand-alone MIDI host with helpers for doing real-time handling of MIDI messages in
the audio-thread and to integrate with VST plugins.
This is part of https://github.com/yamadapc/augmented-audio.
In order to integrate with the VST C API, this crate does manual memory allocation and handling. This is due to VST event types being unrepresentable as safe Rust constructs (and due to real-time safety being required as well).
This crate provides the host
side, which is the MIDI host. This host allocates when it receives messages from midir
.
The events are forwarded onto a lock-free queue (atomic_queue
).
On the audio_thread
and vst
modules, past construction methods that should be called on the audio-thread will not
(de)-allocate. This is tested using the assert_no_alloc
crate.
In addition, basedrop
/ audio_garbage_collector
are used to prevent de-allocation from happening on the
audio-thread.
The crate has unit-tests (though it should be considered experimental as all of this repository).
Test coverage is at 80%.
The MidiHost
exposes an actix API, to be used with the actix actor system. This is to ease communicating with the midi
handler from multiple threads.
To use this crate:
- [
basedrop::Collector
] You'll need to set-upbasedrop
oraudio-garbage-collector
- [
host::MidiHost
] should be created- This will connect to inputs & push messages to a queue
- [
audio_thread::MidiAudioThreadHandler
] On your audio thread you should pop from the queue- This is enough to add MIDI to a standalone [
audio_processor_traits::MidiEventHandler
]
- This is enough to add MIDI to a standalone [
- [
vst::MidiVSTConverter
] If you're implementing a host, you'll have to convert messages onto the VST API
fn example() {
use audio_processor_standalone_midi::audio_thread::MidiAudioThreadHandler;
use audio_processor_standalone_midi::host::MidiHost;
use audio_processor_standalone_midi::vst::MidiVSTConverter;
use basedrop::Collector;
// GC ======================================================================================
// See `audio-garbage-collector` for an easier to use wrapper on top of this.
//
// `Collector` will let us use reference counted values on the audio-thread, which will be
// pushed to a queue for de-allocation. You must set-up a background thread that forces it
// to actually collect garbage from the queue.
let gc = Collector::new();
let handle = gc.handle();
// Host ====================================================================================
// A host may be created with `new` or `default_with_handle`.
let mut host = MidiHost::default_with_handle(&handle);
// It'll connect to all MIDI input ports when started
// host.start().expect("Failed to connect");
// The host will push messages onto a lock-free queue. This is a reference counted value.
let midi_messages_queue = host.messages().clone();
// Audio-thread ============================================================================
// ...
// Within your audio-thread
// ...
// You'll want to share a `MidiAudioThreadHandler` between process calls as it pre-allocates
// buffers.
let mut midi_audio_thread_handler = MidiAudioThreadHandler::default();
// On each tick you'll call collect
midi_audio_thread_handler.collect_midi_messages(&midi_messages_queue);
// You'll get the MIDI message buffer
let midi_messages = midi_audio_thread_handler.buffer();
// ^ This is a `&Vec<MidiMessageEntry>`. If you're using `audio-processor-traits`, you can
// pass this into any `MidiEventHandler` implementor.
// VST interop =============================================================================
// If you want to interface with a VST plugin, you'll need to convert messages into the
// C-api.
// ...
// You'll want to share this between process calls as it pre-allocates buffers.
let mut midi_vst_converter = MidiVSTConverter::default();
midi_vst_converter.accept(&midi_messages);
// This can be passed into VST plugins.
let events: &vst::api::Events = midi_vst_converter.events();
}
License: MIT