mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Implement audioMasterProcessEvents, closing #5
This allows plugins to output MIDI events.
This commit is contained in:
@@ -8,6 +8,12 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for plugins that send MIDI events back to the host. This plugins
|
||||
such as Cthulhu and Scaler to output notes and CC for another plugin to work
|
||||
with.
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed the plugin detection mechanism to support yet another way of
|
||||
|
||||
@@ -253,9 +253,30 @@ HostBridge::HostBridge(audioMasterCallback host_callback)
|
||||
host_callback_handler = std::thread([&]() {
|
||||
try {
|
||||
while (true) {
|
||||
// TODO: Think of a nicer way to structure this and the similar
|
||||
// handler in `PluginBridge::handle_dispatch_midi_events`
|
||||
receive_event(
|
||||
vst_host_callback, std::pair<Logger&, bool>(logger, false),
|
||||
passthrough_event(&plugin, host_callback_function));
|
||||
vst_host_callback, std::nullopt, [&](Event& event) {
|
||||
// MIDI events sent from the plugin back to the host are
|
||||
// a special case here. They have to sent during the
|
||||
// `processReplacing()` function or else the host will
|
||||
// ignore them. Because of this we'll temporarily save
|
||||
// any MIDI events we receive here, and then we'll
|
||||
// actually send them to the host at the end of the
|
||||
// `process_replacing()` function.
|
||||
if (event.opcode == audioMasterProcessEvents) {
|
||||
std::lock_guard lock(incoming_midi_events_mutex);
|
||||
|
||||
incoming_midi_events.push_back(
|
||||
std::get<DynamicVstEvents>(event.payload));
|
||||
EventResult response{1, nullptr};
|
||||
|
||||
return response;
|
||||
} else {
|
||||
return passthrough_event(
|
||||
&plugin, host_callback_function)(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (const boost::system::system_error&) {
|
||||
// This happens when the sockets got closed because the plugin
|
||||
@@ -531,6 +552,20 @@ void HostBridge::process_replacing(AEffect* /*plugin*/,
|
||||
std::copy(response.buffers[channel].begin(),
|
||||
response.buffers[channel].end(), outputs[channel]);
|
||||
}
|
||||
|
||||
// Plugins are allowed to send MIDI events during processing using a host
|
||||
// callback. These have to be processed during the actual
|
||||
// `processReplacing()` function or else the hsot will ignore them. To
|
||||
// prevent these events from getting delayed by a sample we'll process them
|
||||
// after the plugin is done processing audio rather than during the time
|
||||
// we're still waiting on the plugin.
|
||||
std::lock_guard lock(incoming_midi_events_mutex);
|
||||
for (DynamicVstEvents& events : incoming_midi_events) {
|
||||
host_callback_function(&plugin, audioMasterProcessEvents, 0, 0,
|
||||
&events.as_c_events(), 0.0);
|
||||
}
|
||||
|
||||
incoming_midi_events.clear();
|
||||
}
|
||||
|
||||
float HostBridge::get_parameter(AEffect* /*plugin*/, int index) {
|
||||
|
||||
@@ -226,4 +226,20 @@ class HostBridge {
|
||||
* `processReplacing` calls.
|
||||
*/
|
||||
std::vector<uint8_t> process_buffer;
|
||||
|
||||
/**
|
||||
* Sending MIDI events sent to the host by the plugin using
|
||||
* `audioMasterProcessEvents` function has to be done during the processing
|
||||
* function. If they are sent during any other time or from another thread,
|
||||
* then the host will just discard them. Because we're receiving our host
|
||||
* callbacks on a separate thread, we have to temporarily store any events
|
||||
* we receive so we can send them to the host at the end of
|
||||
* `process_replacing()`.
|
||||
*/
|
||||
std::vector<DynamicVstEvents> incoming_midi_events;
|
||||
/**
|
||||
* Mutex for locking the above event queue, since recieving and processing
|
||||
* now happens in two different threads.
|
||||
*/
|
||||
std::mutex incoming_midi_events_mutex;
|
||||
};
|
||||
|
||||
@@ -361,6 +361,9 @@ class HostCallbackDataConverter : DefaultDataConverter {
|
||||
// done inside of `passthrough_event`.
|
||||
return AEffect(*plugin);
|
||||
break;
|
||||
case audioMasterProcessEvents:
|
||||
return DynamicVstEvents(*static_cast<const VstEvents*>(data));
|
||||
break;
|
||||
// We detect whether an opcode should return a string by checking
|
||||
// whether there's a zeroed out buffer behind the void pointer. This
|
||||
// works for any host, but not all plugins zero out their buffers.
|
||||
|
||||
Reference in New Issue
Block a user