Move event handling logic to a dedicated class

Now all pieces are in place to allow handling events over multiple
socket connections.
This commit is contained in:
Robbert van der Helm
2020-10-25 23:08:11 +01:00
parent 54ed69c408
commit 74c3cab046
8 changed files with 581 additions and 544 deletions
-2
View File
@@ -263,7 +263,5 @@ void GroupHost::terminate() {
// There's no need to manually terminate group host processes as they will
// shut down automatically after all plugins have exited. Manually closing
// the dispatch socket will cause the associated plugin to exit.
sockets.host_vst_dispatch.shutdown(
boost::asio::local::stream_protocol::socket::shutdown_both);
sockets.host_vst_dispatch.close();
}
+32 -44
View File
@@ -121,41 +121,31 @@ PluginBridge::PluginBridge(audioMasterCallback host_callback)
// instead of asynchronous IO since communication has to be handled in
// lockstep anyway
host_callback_handler = std::jthread([&]() {
while (true) {
try {
// TODO: Think of a nicer way to structure this and the similar
// handler in `Vst2Bridge::handle_dispatch_midi_events`
receive_event(
sockets.vst_host_callback,
std::pair<Logger&, bool>(logger, false), [&](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);
// TODO: Think of a nicer way to structure this and the similar
// handler in `Vst2Bridge::handle_dispatch_midi_events`
sockets.vst_host_callback.receive(
std::pair<Logger&, bool>(logger, false), [&](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{.return_value = 1,
.payload = nullptr,
.value_payload = std::nullopt};
incoming_midi_events.push_back(
std::get<DynamicVstEvents>(event.payload));
EventResult response{.return_value = 1,
.payload = nullptr,
.value_payload = std::nullopt};
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
// is being shut down
break;
}
}
return response;
} else {
return passthrough_event(&plugin,
host_callback_function)(event);
}
});
});
// Read the plugin's information from the Wine process. This can only be
@@ -435,10 +425,9 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/,
intptr_t return_value = 0;
try {
// TODO: Add some kind of timeout?
return_value = send_event(
sockets.host_vst_dispatch, dispatch_mutex, converter,
std::pair<Logger&, bool>(logger, true), opcode, index,
value, data, option);
return_value = sockets.host_vst_dispatch.send(
converter, std::pair<Logger&, bool>(logger, true), opcode,
index, value, data, option);
} catch (const boost::system::system_error& a) {
// Thrown when the socket gets closed because the VST plugin
// loaded into the Wine process crashed during shutdown
@@ -461,10 +450,9 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/,
// thread and socket to pass MIDI events. Otherwise plugins will
// stop receiving MIDI data when they have an open dropdowns or
// message box.
return send_event(sockets.host_vst_dispatch_midi_events,
dispatch_midi_events_mutex, converter,
std::pair<Logger&, bool>(logger, true), opcode,
index, value, data, option);
return sockets.host_vst_dispatch_midi_events.send(
converter, std::pair<Logger&, bool>(logger, true), opcode,
index, value, data, option);
break;
case effCanDo: {
const std::string query(static_cast<const char*>(data));
@@ -521,9 +509,9 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/,
// and loading plugin state it's much better to have bitsery or our
// receiving function temporarily allocate a large enough buffer rather than
// to have a bunch of allocated memory sitting around doing nothing.
return send_event(sockets.host_vst_dispatch, dispatch_mutex, converter,
std::pair<Logger&, bool>(logger, true), opcode, index,
value, data, option);
return sockets.host_vst_dispatch.send(
converter, std::pair<Logger&, bool>(logger, true), opcode, index, value,
data, option);
}
template <typename T>
+5 -11
View File
@@ -23,9 +23,9 @@
#include <mutex>
#include <thread>
#include "../common/communication.h"
#include "../common/configuration.h"
#include "../common/logging.h"
#include "../common/communication.h"
#include "host-process.h"
/**
@@ -132,16 +132,10 @@ class PluginBridge {
std::jthread host_callback_handler;
/**
* A binary semaphore to prevent race conditions from the dispatch function
* being called by two threads at once. See `send_event()` for more
* information.
*/
std::mutex dispatch_mutex;
std::mutex dispatch_midi_events_mutex;
/**
* A similar semaphore as the `dispatch_*` semaphores in the rare case that
* `getParameter()` and `setParameter()` are being called at the same time
* since they use the same socket.
* A mutex to prevent multiple simultaneous calls to `getParameter()` and
* `setParameter()`. This likely won't happen, but better safe than sorry.
* For `dispatch()` and `audioMaster()` there's some more complex logic for
* this in `EventHandler`.
*/
std::mutex parameters_mutex;