Implement audioMasterProcessEvents, closing #5

This allows plugins to output MIDI events.
This commit is contained in:
Robbert van der Helm
2020-05-06 01:04:20 +02:00
parent e71fd433f9
commit af060054d3
4 changed files with 62 additions and 2 deletions
+6
View File
@@ -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
+37 -2
View File
@@ -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) {
+16
View File
@@ -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;
};
+3
View File
@@ -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.