mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-20 02:13:56 +02:00
Allow all threads to return when sockets close
This was not a problem with individually hosted plugins because the entire process got terminated at once, but here we all threads to shut down gracefully when a plugin's sockets get closed. I wish this wouldn't need all these try-catches, but we're not writing Haskell here. The only issue remaining is that for some reason only the first instance's editor works, at least for Serum. This might be because of the message loop.
This commit is contained in:
@@ -186,21 +186,23 @@ void Vst2Bridge::handle_dispatch() {
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void Vst2Bridge::handle_dispatch_midi_events() {
|
||||
void Vst2Bridge::handle_dispatch_midi_events() {
|
||||
try {
|
||||
while (true) {
|
||||
receive_event(
|
||||
host_vst_dispatch_midi_events, std::nullopt, [&](Event& event) {
|
||||
if (BOOST_LIKELY(event.opcode == effProcessEvents)) {
|
||||
// For 99% of the plugins we can just call
|
||||
// `effProcessReplacing()` and be done with it, but a select
|
||||
// few plugins (I could only find Kontakt that does this)
|
||||
// don't actually make copies of the events they receive and
|
||||
// only store pointers, meaning that they have to live at
|
||||
// least until the next audio buffer gets processed. We're
|
||||
// not using `passhtourhg_events()` here directly because we
|
||||
// need to store a copy of the `DynamicVstEvents` struct
|
||||
// before passing the generated `VstEvents` object to the
|
||||
// plugin.
|
||||
// `effProcessReplacing()` and be done with it, but a
|
||||
// select few plugins (I could only find Kontakt that
|
||||
// does this) don't actually make copies of the events
|
||||
// they receive and only store pointers, meaning that
|
||||
// they have to live at least until the next audio
|
||||
// buffer gets processed. We're not using
|
||||
// `passhtourhg_events()` here directly because we need
|
||||
// to store a copy of the `DynamicVstEvents` struct
|
||||
// before passing the generated `VstEvents` object to
|
||||
// the plugin.
|
||||
std::lock_guard lock(next_buffer_midi_events_mutex);
|
||||
|
||||
next_audio_buffer_midi_events.push_back(
|
||||
@@ -208,13 +210,14 @@ void Vst2Bridge::handle_dispatch() {
|
||||
DynamicVstEvents& events =
|
||||
next_audio_buffer_midi_events.back();
|
||||
|
||||
// Exact same handling as in `passthrough_event`, apart from
|
||||
// making a copy of the events first
|
||||
// Exact same handling as in `passthrough_event`, apart
|
||||
// from making a copy of the events first
|
||||
const intptr_t return_value = plugin->dispatcher(
|
||||
plugin, event.opcode, event.index, event.value,
|
||||
&events.as_c_events(), event.option);
|
||||
|
||||
EventResult response{return_value, nullptr, std::nullopt};
|
||||
EventResult response{return_value, nullptr,
|
||||
std::nullopt};
|
||||
|
||||
return response;
|
||||
} else {
|
||||
@@ -224,17 +227,23 @@ void Vst2Bridge::handle_dispatch() {
|
||||
"event on MIDI processing thread"
|
||||
<< std::endl;
|
||||
|
||||
// Maybe this should just be a hard error instead, since it
|
||||
// should never happen
|
||||
// Maybe this should just be a hard error instead, since
|
||||
// it should never happen
|
||||
return passthrough_event(
|
||||
plugin, std::bind(&Vst2Bridge::dispatch_wrapper, this,
|
||||
_1, _2, _3, _4, _5, _6))(event);
|
||||
plugin,
|
||||
std::bind(&Vst2Bridge::dispatch_wrapper, this, _1,
|
||||
_2, _3, _4, _5, _6))(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (const boost::system::system_error&) {
|
||||
// The plugin has cut off communications, so we can shut down this host
|
||||
// application
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void Vst2Bridge::handle_parameters() {
|
||||
void Vst2Bridge::handle_parameters() {
|
||||
try {
|
||||
while (true) {
|
||||
// Both `getParameter` and `setParameter` functions are passed
|
||||
// through on this socket since they have a lot of overlap. The
|
||||
@@ -243,7 +252,8 @@ void Vst2Bridge::handle_dispatch() {
|
||||
auto request = read_object<Parameter>(host_vst_parameters);
|
||||
if (request.value.has_value()) {
|
||||
// `setParameter`
|
||||
plugin->setParameter(plugin, request.index, request.value.value());
|
||||
plugin->setParameter(plugin, request.index,
|
||||
request.value.value());
|
||||
|
||||
ParameterResult response{std::nullopt};
|
||||
write_object(host_vst_parameters, response);
|
||||
@@ -255,11 +265,16 @@ void Vst2Bridge::handle_dispatch() {
|
||||
write_object(host_vst_parameters, response);
|
||||
}
|
||||
}
|
||||
} catch (const boost::system::system_error&) {
|
||||
// The plugin has cut off communications, so we can shut down this host
|
||||
// application
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void Vst2Bridge::handle_process_replacing() {
|
||||
void Vst2Bridge::handle_process_replacing() {
|
||||
std::vector<std::vector<float>> output_buffers(plugin->numOutputs);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
auto request = read_object<AudioBuffers>(host_vst_process_replacing,
|
||||
process_buffer);
|
||||
@@ -281,22 +296,23 @@ void Vst2Bridge::handle_dispatch() {
|
||||
outputs.push_back(buffer.data());
|
||||
}
|
||||
|
||||
// Let the plugin process the MIDI events that were received since the
|
||||
// last buffer, and then clean up those events. This approach should not
|
||||
// be needed but Kontakt only stores pointers to rather than copies of
|
||||
// the events.
|
||||
// Let the plugin process the MIDI events that were received since
|
||||
// the last buffer, and then clean up those events. This approach
|
||||
// should not be needed but Kontakt only stores pointers to rather
|
||||
// than copies of the events.
|
||||
{
|
||||
std::lock_guard lock(next_buffer_midi_events_mutex);
|
||||
|
||||
// Any plugin made in the last fifteen years or so should support
|
||||
// `processReplacing`. In the off chance it does not we can just
|
||||
// emulate this behavior ourselves.
|
||||
// Any plugin made in the last fifteen years or so should
|
||||
// support `processReplacing`. In the off chance it does not we
|
||||
// can just emulate this behavior ourselves.
|
||||
if (plugin->processReplacing != nullptr) {
|
||||
plugin->processReplacing(plugin, inputs.data(), outputs.data(),
|
||||
plugin->processReplacing(plugin, inputs.data(),
|
||||
outputs.data(),
|
||||
request.sample_frames);
|
||||
} else {
|
||||
// If we zero out this buffer then the behavior is the same as
|
||||
// `processReplacing``
|
||||
// If we zero out this buffer then the behavior is the same
|
||||
// as `processReplacing``
|
||||
for (std::vector<float>& buffer : output_buffers) {
|
||||
std::fill(buffer.begin(), buffer.end(), 0.0);
|
||||
}
|
||||
@@ -311,6 +327,10 @@ void Vst2Bridge::handle_dispatch() {
|
||||
AudioBuffers response{output_buffers, request.sample_frames};
|
||||
write_object(host_vst_process_replacing, response, process_buffer);
|
||||
}
|
||||
} catch (const boost::system::system_error&) {
|
||||
// The plugin has cut off communications, so we can shut down this host
|
||||
// application
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t Vst2Bridge::dispatch_wrapper(AEffect* plugin,
|
||||
@@ -477,12 +497,15 @@ intptr_t VST_CALL_CONV host_callback_proxy(AEffect* effect,
|
||||
|
||||
uint32_t WINAPI handle_dispatch_midi_events_proxy(void* instance) {
|
||||
static_cast<Vst2Bridge*>(instance)->handle_dispatch_midi_events();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t WINAPI handle_parameters_proxy(void* instance) {
|
||||
static_cast<Vst2Bridge*>(instance)->handle_parameters();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t WINAPI handle_process_replacing_proxy(void* instance) {
|
||||
static_cast<Vst2Bridge*>(instance)->handle_process_replacing();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ class Vst2Bridge {
|
||||
// below. They're defined here because we can't use lambdas with WinAPI's
|
||||
// `CreateThread` which is needed to support the proper call conventions the
|
||||
// VST plugins expect.
|
||||
[[noreturn]] void handle_dispatch_midi_events();
|
||||
[[noreturn]] void handle_parameters();
|
||||
[[noreturn]] void handle_process_replacing();
|
||||
void handle_dispatch_midi_events();
|
||||
void handle_parameters();
|
||||
void handle_process_replacing();
|
||||
|
||||
/**
|
||||
* Forward the host callback made by the plugin to the host and return the
|
||||
|
||||
Reference in New Issue
Block a user