22 个版本 (11 个稳定版)
1.12.0 | 2024 年 8 月 13 日 |
---|---|
1.11.0 | 2024 年 1 月 17 日 |
1.10.0 | 2023 年 12 月 7 日 |
1.9.0 | 2023 年 5 月 22 日 |
0.1.0 | 2021 年 7 月 21 日 |
#845 in 音频
每月 162 次下载
用于 audio-processor-standalon…
330KB
2.5K SLoC
audio-processor-standalone-midi
此 crate 提供转换为 VST 类型,以便 VST 主机可以使用它。这是由 MidiVSTConverter
提供的。
封装 midir
以提供 MIDI 输入处理。可以使用 MidiHost
启动主机。
当接收到 MIDI 消息时,它们会被推送到一个无锁的 atomic_queue::Queue
。消息由 MidiAudioThreadHandler
在音频线程中拾取。
它提供了与 audio-processor-traits
- MidiEventHandler
特性
rust-vst
-PluginInstance
- 集成方便的独立 MIDI 集成
目前,此主机丢弃超过 3 个字节的 MIDI 消息。此外,队列有界限,必须提供大小。默认实现将使用 MIDI 队列容量为 100。这是一个独立的 MIDI 主机,具有在音频线程中实时处理 MIDI 消息并集成 VST 插件的辅助工具。
这是 https://github.com/yamadapc/augmented-audio 的一部分。
内存安全性
为了与 VST C API 集成,此 crate 进行手动内存分配和处理。这是由于 VST 事件类型无法表示为安全的 Rust 构造(并且由于需要实时安全性)。
实时安全性
此 crate 提供了 host
方面,即 MIDI 主机。当从 midir
接收消息时,此主机进行分配。
事件被转发到无锁队列 (atomic_queue
)。
在 audio_thread
和 vst
模块中,过去应该在音频线程上调用(反)分配的方法将不会分配。这是使用 assert_no_alloc
库进行测试的。
此外,使用 basedrop
/ audio_garbage_collector
来防止在音频线程上发生(反)分配。
测试覆盖率
该库具有单元测试(尽管应该将其视为实验性的,因为整个存储库都是如此)。
测试覆盖率为 80%。
Actix & Actors
MidiHost
暴露了一个 actix API,用于与 actix actor 系统一起使用。这是为了方便从多个线程与 midi 处理器通信。
用法
要使用此库
basedrop::Collector
您需要设置basedrop
或audio-garbage-collector
host::MidiHost
应创建- 这将连接到输入并将消息推送到队列
audio_thread::MidiAudioThreadHandler
在您的音频线程上,您应该从队列中弹出- 这足以将 MIDI 添加到独立的
audio_processor_traits::MidiEventHandler
- 这足以将 MIDI 添加到独立的
vst::MidiVSTConverter
如果您正在实现主机,您必须将消息转换到 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();
}
许可证:MIT
依赖关系
~1–30MB
~452K SLoC