mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-15 07:53:55 +02:00
Drop the separate effProcessEvents socket
Now that event handling is fully concurrent and thus no longer gets blocked by the Win32 message loop.
This commit is contained in:
@@ -788,10 +788,6 @@ class Sockets {
|
|||||||
host_vst_dispatch(io_context,
|
host_vst_dispatch(io_context,
|
||||||
(base_dir / "host_vst_dispatch.sock").string(),
|
(base_dir / "host_vst_dispatch.sock").string(),
|
||||||
listen),
|
listen),
|
||||||
host_vst_dispatch_midi_events(
|
|
||||||
io_context,
|
|
||||||
(base_dir / "host_vst_dispatch_midi_events.sock").string(),
|
|
||||||
listen),
|
|
||||||
vst_host_callback(io_context,
|
vst_host_callback(io_context,
|
||||||
(base_dir / "vst_host_callback.sock").string(),
|
(base_dir / "vst_host_callback.sock").string(),
|
||||||
listen),
|
listen),
|
||||||
@@ -814,7 +810,6 @@ class Sockets {
|
|||||||
// Manually close all sockets so we break out of any blocking operations
|
// Manually close all sockets so we break out of any blocking operations
|
||||||
// that may still be active
|
// that may still be active
|
||||||
host_vst_dispatch.close();
|
host_vst_dispatch.close();
|
||||||
host_vst_dispatch_midi_events.close();
|
|
||||||
vst_host_callback.close();
|
vst_host_callback.close();
|
||||||
host_vst_parameters.close();
|
host_vst_parameters.close();
|
||||||
host_vst_process_replacing.close();
|
host_vst_process_replacing.close();
|
||||||
@@ -838,7 +833,6 @@ class Sockets {
|
|||||||
*/
|
*/
|
||||||
void connect() {
|
void connect() {
|
||||||
host_vst_dispatch.connect();
|
host_vst_dispatch.connect();
|
||||||
host_vst_dispatch_midi_events.connect();
|
|
||||||
vst_host_callback.connect();
|
vst_host_callback.connect();
|
||||||
host_vst_parameters.connect();
|
host_vst_parameters.connect();
|
||||||
host_vst_process_replacing.connect();
|
host_vst_process_replacing.connect();
|
||||||
@@ -861,13 +855,6 @@ class Sockets {
|
|||||||
* the plugin.
|
* the plugin.
|
||||||
*/
|
*/
|
||||||
EventHandler<Thread> host_vst_dispatch;
|
EventHandler<Thread> host_vst_dispatch;
|
||||||
/**
|
|
||||||
* Used specifically for the `effProcessEvents` opcode. This is needed
|
|
||||||
* because the Win32 API is designed to block during certain GUI
|
|
||||||
* interactions such as resizing a window or opening a dropdown. Without
|
|
||||||
* this MIDI input would just stop working at times.
|
|
||||||
*/
|
|
||||||
EventHandler<Thread> host_vst_dispatch_midi_events;
|
|
||||||
/**
|
/**
|
||||||
* The socket that forwards all `audioMaster()` calls from the Windows VST
|
* The socket that forwards all `audioMaster()` calls from the Windows VST
|
||||||
* plugin to the host.
|
* plugin to the host.
|
||||||
|
|||||||
@@ -446,15 +446,6 @@ intptr_t PluginBridge::dispatch(AEffect* /*plugin*/,
|
|||||||
|
|
||||||
return return_value;
|
return return_value;
|
||||||
}; break;
|
}; break;
|
||||||
case effProcessEvents:
|
|
||||||
// Because of limitations of the Win32 API we have to use a seperate
|
|
||||||
// thread and socket to pass MIDI events. Otherwise plugins will
|
|
||||||
// stop receiving MIDI data when they have an open dropdowns or
|
|
||||||
// message box.
|
|
||||||
return sockets.host_vst_dispatch_midi_events.send_event(
|
|
||||||
converter, std::pair<Logger&, bool>(logger, true), opcode,
|
|
||||||
index, value, data, option);
|
|
||||||
break;
|
|
||||||
case effCanDo: {
|
case effCanDo: {
|
||||||
const std::string query(static_cast<const char*>(data));
|
const std::string query(static_cast<const char*>(data));
|
||||||
|
|
||||||
|
|||||||
@@ -128,59 +128,6 @@ Vst2Bridge::Vst2Bridge(MainContext& main_context,
|
|||||||
// configuration as a response
|
// configuration as a response
|
||||||
config = sockets.host_vst_control.receive_single<Configuration>();
|
config = sockets.host_vst_control.receive_single<Configuration>();
|
||||||
|
|
||||||
// This works functionally identically to the `handle_dispatch()` function,
|
|
||||||
// but this socket will only handle MIDI events and it will handle them
|
|
||||||
// eagerly. This is needed because of Win32 API limitations.
|
|
||||||
dispatch_midi_events_handler = Win32Thread([&]() {
|
|
||||||
sockets.host_vst_dispatch_midi_events.receive_events(
|
|
||||||
std::nullopt, [&](Event& event, bool /*on_main_thread*/) {
|
|
||||||
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 `passthrough_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(
|
|
||||||
std::get<DynamicVstEvents>(event.payload));
|
|
||||||
DynamicVstEvents& events =
|
|
||||||
next_audio_buffer_midi_events.back();
|
|
||||||
|
|
||||||
// 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 = return_value,
|
|
||||||
.payload = nullptr,
|
|
||||||
.value_payload = std::nullopt};
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} else {
|
|
||||||
using namespace std::placeholders;
|
|
||||||
|
|
||||||
std::cerr << "[Warning] Received non-MIDI "
|
|
||||||
"event on MIDI processing thread"
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
parameters_handler = Win32Thread([&]() {
|
parameters_handler = Win32Thread([&]() {
|
||||||
sockets.host_vst_parameters.receive_multi<Parameter>(
|
sockets.host_vst_parameters.receive_multi<Parameter>(
|
||||||
[&](Parameter request, std::vector<uint8_t>& buffer) {
|
[&](Parameter request, std::vector<uint8_t>& buffer) {
|
||||||
@@ -322,31 +269,62 @@ bool Vst2Bridge::should_skip_message_loop() const {
|
|||||||
void Vst2Bridge::handle_dispatch() {
|
void Vst2Bridge::handle_dispatch() {
|
||||||
sockets.host_vst_dispatch.receive_events(
|
sockets.host_vst_dispatch.receive_events(
|
||||||
std::nullopt, [&](Event& event, bool /*on_main_thread*/) {
|
std::nullopt, [&](Event& event, bool /*on_main_thread*/) {
|
||||||
return passthrough_event(
|
if (event.opcode == effProcessEvents) {
|
||||||
plugin,
|
// For 99% of the plugins we can just call
|
||||||
[&](AEffect* plugin, int opcode, int index, intptr_t value,
|
// `effProcessReplacing()` and be done with it, but a select few
|
||||||
void* data, float option) -> intptr_t {
|
// plugins (I could only find Kontakt that does this) don't
|
||||||
// Certain functions will most definitely involve the GUI or
|
// actually make copies of the events they receive and only
|
||||||
// the Win32 message loop. These functions have to be
|
// store pointers to those events, meaning that they have to
|
||||||
// performed on the thread that is running the IO context,
|
// live at least until the next audio buffer gets processed.
|
||||||
// since this is also where the plugins were instantiated
|
// We're not using `passthrough_events()` here directly because
|
||||||
// and where the Win32 message loop is handled.
|
// we need to store a copy of the `DynamicVstEvents` struct
|
||||||
if (unsafe_opcodes.contains(opcode)) {
|
// before passing the generated `VstEvents` object to the
|
||||||
std::promise<intptr_t> dispatch_result;
|
// plugin.
|
||||||
boost::asio::dispatch(main_context.context, [&]() {
|
std::lock_guard lock(next_buffer_midi_events_mutex);
|
||||||
const intptr_t result = dispatch_wrapper(
|
|
||||||
plugin, opcode, index, value, data, option);
|
|
||||||
|
|
||||||
dispatch_result.set_value(result);
|
next_audio_buffer_midi_events.push_back(
|
||||||
});
|
std::get<DynamicVstEvents>(event.payload));
|
||||||
|
DynamicVstEvents& events = next_audio_buffer_midi_events.back();
|
||||||
|
|
||||||
return dispatch_result.get_future().get();
|
// Exact same handling as in `passthrough_event()`, apart
|
||||||
} else {
|
// from making a copy of the events first
|
||||||
return dispatch_wrapper(plugin, opcode, index, value,
|
const intptr_t return_value = plugin->dispatcher(
|
||||||
data, option);
|
plugin, event.opcode, event.index, event.value,
|
||||||
}
|
&events.as_c_events(), event.option);
|
||||||
},
|
|
||||||
event);
|
EventResult response{.return_value = return_value,
|
||||||
|
.payload = nullptr,
|
||||||
|
.value_payload = std::nullopt};
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
return passthrough_event(
|
||||||
|
plugin,
|
||||||
|
[&](AEffect* plugin, int opcode, int index, intptr_t value,
|
||||||
|
void* data, float option) -> intptr_t {
|
||||||
|
// Certain functions will most definitely involve the
|
||||||
|
// GUI or the Win32 message loop. These functions have
|
||||||
|
// to be performed on the thread that is running the IO
|
||||||
|
// context, since this is also where the plugins were
|
||||||
|
// instantiated and where the Win32 message loop is
|
||||||
|
// handled.
|
||||||
|
if (unsafe_opcodes.contains(opcode)) {
|
||||||
|
std::promise<intptr_t> dispatch_result;
|
||||||
|
boost::asio::dispatch(main_context.context, [&]() {
|
||||||
|
const intptr_t result = dispatch_wrapper(
|
||||||
|
plugin, opcode, index, value, data, option);
|
||||||
|
|
||||||
|
dispatch_result.set_value(result);
|
||||||
|
});
|
||||||
|
|
||||||
|
return dispatch_result.get_future().get();
|
||||||
|
} else {
|
||||||
|
return dispatch_wrapper(plugin, opcode, index,
|
||||||
|
value, data, option);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
event);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -186,12 +186,6 @@ class Vst2Bridge {
|
|||||||
*/
|
*/
|
||||||
AEffect* plugin;
|
AEffect* plugin;
|
||||||
|
|
||||||
/**
|
|
||||||
* The thread that specifically handles `effProcessEvents` opcodes so the
|
|
||||||
* plugin can still receive MIDI during GUI interaction to work around Win32
|
|
||||||
* API limitations.
|
|
||||||
*/
|
|
||||||
Win32Thread dispatch_midi_events_handler;
|
|
||||||
/**
|
/**
|
||||||
* The thread that responds to `getParameter` and `setParameter` requests.
|
* The thread that responds to `getParameter` and `setParameter` requests.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user