mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-09 20:29:10 +02:00
Allow midi events to be handled during interaction
This commit is contained in:
@@ -136,19 +136,6 @@ window managers will require some slight modifications in
|
|||||||
meson configure build --buildtype=debug -Duse-winedbg=true
|
meson configure build --buildtype=debug -Duse-winedbg=true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Known issues
|
|
||||||
|
|
||||||
- Plugins can't receive MIDI events while they have an open dropdown menu. This
|
|
||||||
is a limitation of the Win32 API which requires all GUI interaction to be done
|
|
||||||
from a single thread. Dropdowns and similar GUI elements are implemented in
|
|
||||||
such a way that they will block the thread until the user selects an item.
|
|
||||||
Most plugins will make the assumption that the GUI thread is the same thread
|
|
||||||
on which the plugin was created and also that this is also the same thread
|
|
||||||
from which `dispatch()` calls are being sent. Because of these limitations we
|
|
||||||
can't just move all GUI interaction to a different thread. A decent solution
|
|
||||||
for this would be to just create another pair of sockets and threads to
|
|
||||||
specifically handle the `effProcessEvents` opcode.
|
|
||||||
|
|
||||||
## Rationale
|
## Rationale
|
||||||
|
|
||||||
I started this project because the alternatives were either unmaintained, not
|
I started this project because the alternatives were either unmaintained, not
|
||||||
@@ -201,6 +188,12 @@ process works as follows:
|
|||||||
|
|
||||||
- Calls from the native VST host to the plugin's `dispatch()` function. These
|
- Calls from the native VST host to the plugin's `dispatch()` function. These
|
||||||
get forwarded to the Windows VST plugin through the Wine VST host.
|
get forwarded to the Windows VST plugin through the Wine VST host.
|
||||||
|
- Calls from the native VST host to the plugin's `dispatch()` function with
|
||||||
|
`opcode=effProcessEvents`. These get forwarded to the Windows VST plugin
|
||||||
|
through the Wine VST host. This has to be handled separately from all other
|
||||||
|
events because of limitations of the Win32 API. Otherwise the plugin would
|
||||||
|
not receive any midi events while the GUI is being resized or a dropdown
|
||||||
|
menu or message box is open.
|
||||||
- Host callback calls from the Windows VST plugin loaded into the Wine VST
|
- Host callback calls from the Windows VST plugin loaded into the Wine VST
|
||||||
host through the `audioMasterCallback` function. These get forwarded to the
|
host through the `audioMasterCallback` function. These get forwarded to the
|
||||||
native VST host through the plugin.
|
native VST host through the plugin.
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ HostBridge::HostBridge(audioMasterCallback host_callback)
|
|||||||
socket_endpoint(generate_endpoint_name().string()),
|
socket_endpoint(generate_endpoint_name().string()),
|
||||||
socket_acceptor(io_context, socket_endpoint),
|
socket_acceptor(io_context, socket_endpoint),
|
||||||
host_vst_dispatch(io_context),
|
host_vst_dispatch(io_context),
|
||||||
|
host_vst_dispatch_midi_events(io_context),
|
||||||
vst_host_callback(io_context),
|
vst_host_callback(io_context),
|
||||||
host_vst_parameters(io_context),
|
host_vst_parameters(io_context),
|
||||||
host_vst_process_replacing(io_context),
|
host_vst_process_replacing(io_context),
|
||||||
@@ -127,6 +128,7 @@ HostBridge::HostBridge(audioMasterCallback host_callback)
|
|||||||
// It's very important that these sockets are connected to in the same
|
// It's very important that these sockets are connected to in the same
|
||||||
// order in the Wine VST host
|
// order in the Wine VST host
|
||||||
socket_acceptor.accept(host_vst_dispatch);
|
socket_acceptor.accept(host_vst_dispatch);
|
||||||
|
socket_acceptor.accept(host_vst_dispatch_midi_events);
|
||||||
socket_acceptor.accept(vst_host_callback);
|
socket_acceptor.accept(vst_host_callback);
|
||||||
socket_acceptor.accept(host_vst_parameters);
|
socket_acceptor.accept(host_vst_parameters);
|
||||||
socket_acceptor.accept(host_vst_process_replacing);
|
socket_acceptor.accept(host_vst_process_replacing);
|
||||||
@@ -281,10 +283,7 @@ intptr_t HostBridge::dispatch(AEffect* /*plugin*/,
|
|||||||
float option) {
|
float option) {
|
||||||
DispatchDataConverter converter(chunk_data, editor_rectangle);
|
DispatchDataConverter converter(chunk_data, editor_rectangle);
|
||||||
|
|
||||||
// Some events need some extra handling
|
|
||||||
// TODO: Handle GUI closing?
|
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
break;
|
|
||||||
case effClose: {
|
case effClose: {
|
||||||
// TODO: Gracefully close the editor?
|
// TODO: Gracefully close the editor?
|
||||||
// TODO: Check whether the sockets and the endpoint are closed
|
// TODO: Check whether the sockets and the endpoint are closed
|
||||||
@@ -333,6 +332,16 @@ intptr_t HostBridge::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 send_event(host_vst_dispatch_midi_events,
|
||||||
|
dispatch_midi_events_semaphore, converter,
|
||||||
|
std::pair<Logger&, bool>(logger, true), opcode,
|
||||||
|
index, value, data, option);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Maybe reuse buffers here when dealing with chunk data
|
// TODO: Maybe reuse buffers here when dealing with chunk data
|
||||||
|
|||||||
@@ -139,6 +139,13 @@ class HostBridge {
|
|||||||
// `AEffect.dispatch()` calls from the native VST host to the Windows VST
|
// `AEffect.dispatch()` calls from the native VST host to the Windows VST
|
||||||
// plugin (through the Wine VST host).
|
// plugin (through the Wine VST host).
|
||||||
boost::asio::local::stream_protocol::socket host_vst_dispatch;
|
boost::asio::local::stream_protocol::socket 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.
|
||||||
|
*/
|
||||||
|
boost::asio::local::stream_protocol::socket host_vst_dispatch_midi_events;
|
||||||
boost::asio::local::stream_protocol::socket vst_host_callback;
|
boost::asio::local::stream_protocol::socket vst_host_callback;
|
||||||
/**
|
/**
|
||||||
* Used for both `getParameter` and `setParameter` since they mostly
|
* Used for both `getParameter` and `setParameter` since they mostly
|
||||||
@@ -168,6 +175,7 @@ class HostBridge {
|
|||||||
* information.
|
* information.
|
||||||
*/
|
*/
|
||||||
std::mutex dispatch_semaphore;
|
std::mutex dispatch_semaphore;
|
||||||
|
std::mutex dispatch_midi_events_semaphore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The callback function passed by the host to the VST plugin instance.
|
* The callback function passed by the host to the VST plugin instance.
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
|||||||
io_context(),
|
io_context(),
|
||||||
socket_endpoint(socket_endpoint_path),
|
socket_endpoint(socket_endpoint_path),
|
||||||
host_vst_dispatch(io_context),
|
host_vst_dispatch(io_context),
|
||||||
|
host_vst_dispatch_midi_events(io_context),
|
||||||
vst_host_callback(io_context),
|
vst_host_callback(io_context),
|
||||||
host_vst_parameters(io_context),
|
host_vst_parameters(io_context),
|
||||||
host_vst_process_replacing(io_context),
|
host_vst_process_replacing(io_context),
|
||||||
@@ -94,6 +95,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
|||||||
// It's very important that these sockets are accepted to in the same order
|
// It's very important that these sockets are accepted to in the same order
|
||||||
// in the Linus plugin
|
// in the Linus plugin
|
||||||
host_vst_dispatch.connect(socket_endpoint);
|
host_vst_dispatch.connect(socket_endpoint);
|
||||||
|
host_vst_dispatch_midi_events.connect(socket_endpoint);
|
||||||
vst_host_callback.connect(socket_endpoint);
|
vst_host_callback.connect(socket_endpoint);
|
||||||
host_vst_parameters.connect(socket_endpoint);
|
host_vst_parameters.connect(socket_endpoint);
|
||||||
host_vst_process_replacing.connect(socket_endpoint);
|
host_vst_process_replacing.connect(socket_endpoint);
|
||||||
@@ -118,6 +120,16 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
|||||||
current_bridge_isntance = nullptr;
|
current_bridge_isntance = nullptr;
|
||||||
plugin->ptr1 = this;
|
plugin->ptr1 = this;
|
||||||
|
|
||||||
|
// This works functionally identically to the `handle_dispatch()` function
|
||||||
|
// below, but this socket will only handle midi events. This is needed
|
||||||
|
// because of Win32 API limitations.
|
||||||
|
dispatch_midi_events_handler = std::thread([&]() {
|
||||||
|
while (true) {
|
||||||
|
passthrough_event(host_vst_dispatch_midi_events, std::nullopt,
|
||||||
|
plugin, plugin->dispatcher);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
parameters_handler = std::thread([&]() {
|
parameters_handler = std::thread([&]() {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Both `getParameter` and `setParameter` functions are passed
|
// Both `getParameter` and `setParameter` functions are passed
|
||||||
@@ -191,6 +203,7 @@ void PluginBridge::handle_dispatch() {
|
|||||||
} catch (const boost::system::system_error&) {
|
} catch (const boost::system::system_error&) {
|
||||||
// This happens when the sockets got closed because the plugin is being
|
// This happens when the sockets got closed because the plugin is being
|
||||||
// shut down. In that case we can just let the whole host terminate.
|
// shut down. In that case we can just let the whole host terminate.
|
||||||
|
dispatch_midi_events_handler.detach();
|
||||||
parameters_handler.detach();
|
parameters_handler.detach();
|
||||||
process_replacing_handler.detach();
|
process_replacing_handler.detach();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,13 @@ class PluginBridge {
|
|||||||
// `AEffect.dispatch()` calls from the native VST host to the Windows VST
|
// `AEffect.dispatch()` calls from the native VST host to the Windows VST
|
||||||
// plugin (through the Wine VST host).
|
// plugin (through the Wine VST host).
|
||||||
boost::asio::local::stream_protocol::socket host_vst_dispatch;
|
boost::asio::local::stream_protocol::socket 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.
|
||||||
|
*/
|
||||||
|
boost::asio::local::stream_protocol::socket host_vst_dispatch_midi_events;
|
||||||
boost::asio::local::stream_protocol::socket vst_host_callback;
|
boost::asio::local::stream_protocol::socket vst_host_callback;
|
||||||
/**
|
/**
|
||||||
* Used for both `getParameter` and `setParameter` since they mostly
|
* Used for both `getParameter` and `setParameter` since they mostly
|
||||||
@@ -119,6 +126,12 @@ class PluginBridge {
|
|||||||
*/
|
*/
|
||||||
boost::asio::local::stream_protocol::socket vst_host_aeffect;
|
boost::asio::local::stream_protocol::socket vst_host_aeffect;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The thread that specifically handles `effProcessEvents` opcodes so the
|
||||||
|
* plugin can still receive midi during GUI interaction to work around Win32
|
||||||
|
* API limitations.
|
||||||
|
*/
|
||||||
|
std::thread 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