mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +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]
|
## [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
|
||||||
|
|
||||||
- Changed the plugin detection mechanism to support yet another way of
|
- 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([&]() {
|
host_callback_handler = std::thread([&]() {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
// TODO: Think of a nicer way to structure this and the similar
|
||||||
|
// handler in `PluginBridge::handle_dispatch_midi_events`
|
||||||
receive_event(
|
receive_event(
|
||||||
vst_host_callback, std::pair<Logger&, bool>(logger, false),
|
vst_host_callback, std::nullopt, [&](Event& event) {
|
||||||
passthrough_event(&plugin, host_callback_function));
|
// 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&) {
|
} catch (const boost::system::system_error&) {
|
||||||
// This happens when the sockets got closed because the plugin
|
// 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(),
|
std::copy(response.buffers[channel].begin(),
|
||||||
response.buffers[channel].end(), outputs[channel]);
|
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) {
|
float HostBridge::get_parameter(AEffect* /*plugin*/, int index) {
|
||||||
|
|||||||
@@ -226,4 +226,20 @@ class HostBridge {
|
|||||||
* `processReplacing` calls.
|
* `processReplacing` calls.
|
||||||
*/
|
*/
|
||||||
std::vector<uint8_t> process_buffer;
|
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`.
|
// done inside of `passthrough_event`.
|
||||||
return AEffect(*plugin);
|
return AEffect(*plugin);
|
||||||
break;
|
break;
|
||||||
|
case audioMasterProcessEvents:
|
||||||
|
return DynamicVstEvents(*static_cast<const VstEvents*>(data));
|
||||||
|
break;
|
||||||
// We detect whether an opcode should return a string by checking
|
// We detect whether an opcode should return a string by checking
|
||||||
// whether there's a zeroed out buffer behind the void pointer. This
|
// whether there's a zeroed out buffer behind the void pointer. This
|
||||||
// works for any host, but not all plugins zero out their buffers.
|
// works for any host, but not all plugins zero out their buffers.
|
||||||
|
|||||||
Reference in New Issue
Block a user