mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-15 07:53:55 +02:00
Update the architecture section
This commit is contained in:
@@ -11,7 +11,6 @@ easy to debug and maintain.
|
|||||||
Everything is implemented and ready for release after a few documentation
|
Everything is implemented and ready for release after a few documentation
|
||||||
updates:
|
updates:
|
||||||
|
|
||||||
- Add missing details if any to the architecture section.
|
|
||||||
- Add a screenshot, because why not?
|
- Add a screenshot, because why not?
|
||||||
|
|
||||||
## Tested with
|
## Tested with
|
||||||
@@ -253,99 +252,122 @@ meson configure build --buildtype=debug -Duse-winedbg=true
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
The project consists of two components, a Linux native VST plugin
|
The project consists of two components: a Linux native VST plugin
|
||||||
(`libyabridge.so`) and a VST host that runs under Wine
|
(`libyabridge.so`) and a VST host that runs under Wine
|
||||||
(`yabridge-host.exe`/`yabridge-host.exe.so`). I'll refer to a copy of or a
|
(`yabridge-host.exe`/`yabridge-host.exe.so`, and
|
||||||
symlink to `libyabridge.so` as _the plugin_, the native Linux VST host that's
|
`yabridge-host-32.exe`/`yabridge-host-32.exe.so` if the bitbirdge is enabled).
|
||||||
hosting the plugin as _the native VST host_, the Wine VST host that's hosting a
|
I'll refer to the copy of or lthe symlink to `libyabridge.so` as _the plugin_,
|
||||||
Windows `.dll` file as _the Wine VST host_, and the Windows VST plugin that's
|
the native Linux VST host that's hosting the plugin as _the native VST host_,
|
||||||
loaded in the Wine VST host is simply the _Windows VST plugin_. The whole
|
the Wine VST host application that's hosting a Windows `.dll` file as _the Wine
|
||||||
process works as follows:
|
VST host_, and the Windows VST plugin that's being loaded in the Wine VST host
|
||||||
|
as the _Windows VST plugin_. The whole process works as follows:
|
||||||
|
|
||||||
1. Some copy of or a symlink to `libyabridge.so` gets loaded as a VST plugin in
|
1. Some copy of or a symlink to `libyabridge.so` gets loaded as a VST plugin in
|
||||||
a Linux VST host. This file should have been renamed to match a Windows VST
|
a Linux VST host. This file should have been renamed to match a Windows VST
|
||||||
plugin `.dll` file in the same directory. For instance, if there's a
|
plugin `.dll` file in the same directory. For instance, if there's a
|
||||||
`Serum_x64.dll` file you'd like to bridge, then there should be a symlink to
|
`Serum_x64.dll` file you'd like to bridge, then there should be a symlink to
|
||||||
`libyabridge.so` named `Serum_x64.so`.
|
`libyabridge.so` named `Serum_x64.so`.
|
||||||
2. The plugin first attempts to locate:
|
2. The plugin first attempts to locate and determine:
|
||||||
|
|
||||||
- The location of `yabridge-host.exe`. For this it will first search for the
|
- The Windows VST plugin `.dll` file that should be loaded.
|
||||||
file either alongside `libyabridge.so`. This is useful for development, as
|
|
||||||
it allows you to use a symlink from the build directory to cause yabridge
|
- The architecture of that VST plugin file. This is done by inspecting the
|
||||||
to use the `yabridge-host.exe` from that same build directory. If this file
|
headers if the `.dll` file.
|
||||||
can't be found, then it will fall back to searching through the search path.
|
|
||||||
- The wine prefix plugin is located in.
|
- The location of the Wine VST host. This will depend on the architecture
|
||||||
- The corresponding Windows VST plugin `.dll` file.
|
detected for the plugin. If the plugin was copmiled for the `x86_64`
|
||||||
|
architecture or the 'Any CPU' target, then we will look for
|
||||||
|
`yabridge-host.exe`. If the plugin was copmiled for the `x86` architecture,
|
||||||
|
when we'll search for `yabridge-host-32.exe`.
|
||||||
|
|
||||||
|
We will first search for this file alongside the actual location of
|
||||||
|
`libyabridge.so`. This is useful for development, as it allows you to use a
|
||||||
|
symlink to `libyabridge.so` directly from the build directory causing
|
||||||
|
yabridge to automatically pick up the right version of the Wine VST host.
|
||||||
|
If this file cannot be found, then it will fall back to searching through
|
||||||
|
the search path.
|
||||||
|
|
||||||
|
- The Wine prefix the plugin is located in. If the `WINEPREFIX` environment
|
||||||
|
variable is specified, then that will be used instead.
|
||||||
|
|
||||||
3. The plugin then sets up a Unix domain socket endpoint to communicate with the
|
3. The plugin then sets up a Unix domain socket endpoint to communicate with the
|
||||||
Wine VST host somewhere in a temporary directory and starts listening on it.
|
Wine VST host somewhere in a temporary directory and starts listening on it.
|
||||||
I chose to use Unix domain sockets rather than shared memory because this way
|
I chose to communicate over Unix domain sockets rather than using shared
|
||||||
you get low latency communication with without any busy waits or manual
|
memory directly because this way you get low latency communication with
|
||||||
synchronisation for free. The added benefit is that it also makes it possible
|
without any busy waits or manual synchronisation for free. The added benefit
|
||||||
to send arbitrarily large data without having to split it up into chunks
|
is that it also makes it possible to send arbitrarily large chunks of data
|
||||||
first, which is useful for transmitting audio and preset data which may have
|
without having to split it up first. This is useful for transmitting audio
|
||||||
any arbitrary size.
|
and preset data which may have any arbitrary size.
|
||||||
4. The plugin launches the Wine VST host in the detected wine prefix, passing
|
4. The plugin launches the Wine VST host in the detected wine prefix, passing
|
||||||
the name of the `.dll` file it should be loading and the path to the Unix
|
the name of the `.dll` file it should be loading and the path to the Unix
|
||||||
domain socket that was just created.
|
domain socket that was just created as its arguments.
|
||||||
5. Communication gets set up using multiple sockets over the same end point.
|
5. Communication gets set up using multiple sockets over the end point created
|
||||||
This allows us to use blocking read operations from multiple threads to
|
previously. This allows us to easily handle multiple data streams from
|
||||||
handle multiple different events without the risk of receiving packets in the
|
different threads using blocking read operations for synchronization. Doing
|
||||||
wrong order. The following types of events get assigned a socket:
|
this greatly simplifies the way communication works without compromising on
|
||||||
|
latency. The following types of events each get their own socket:
|
||||||
|
|
||||||
- Calls from the native VST host to the plugin's `dispatch()` function. These
|
- Calls from the native VST host to the plugin's `dispatcher()` function.
|
||||||
get forwarded to the Windows VST plugin through the Wine VST host.
|
These 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
|
- Calls from the native VST host to the plugin's `dispatcher()` function with
|
||||||
`opcode=effProcessEvents`. These get forwarded to the Windows VST plugin
|
the `effProcessEvents` opcode. These also get forwarded to the Windows VST
|
||||||
through the Wine VST host. This has to be handled separately from all other
|
plugin through the Wine VST host. This has to be handled separately from
|
||||||
events because of limitations of the Win32 API. Otherwise the plugin would
|
all other events because of limitations of the Win32 API. Without doing
|
||||||
not receive any MIDI events while the GUI is being resized or a dropdown
|
this the plugin would not be able to receive any MIDI events while the GUI
|
||||||
menu or message box is open.
|
is being resized or a dropdown menu or message box is shown.
|
||||||
- Host callback calls from the Windows VST plugin loaded into the Wine VST
|
- Host callback calls from the Windows VST plugin through the
|
||||||
host through the `audioMasterCallback` function. These get forwarded to the
|
`audioMasterCallback` function. These get forwarded to the native VST host
|
||||||
native VST host through the plugin.
|
through the plugin.
|
||||||
|
|
||||||
Both the `dispatch()` and `audioMasterCallback()` functions are handled in
|
Both the `dispatcher()` and `audioMasterCallback()` functions are handled
|
||||||
the same way, with some minor variations on how payload data gets
|
in the same way, with some minor variations on how payload data gets
|
||||||
serialized depending on the opcode of the event being sent.
|
serialized depending on the opcode of the event being sent. See the section
|
||||||
|
below this for more details on this procedure.
|
||||||
|
|
||||||
- Calls from the native VST host to the plugin's `getParameter()` and
|
- Calls from the native VST host to the plugin's `getParameter()` and
|
||||||
`setParameter()` functions. Both functions get forwarded to the Windows VST
|
`setParameter()` functions. Both functions get forwarded to the Windows VST
|
||||||
plugin through the Wine VST host using a single socket because they're very
|
plugin through the Wine VST host using a single socket because they're very
|
||||||
similar and don't need any complicated behaviour.
|
similar and don't need any complicated behaviour.
|
||||||
- Calls from the native VST host to the plugin's `process()` and
|
- Calls from the native VST host to the plugin's `processReplacing()`
|
||||||
`processReplacing()` functions. Both functions get forwarded to the Windows
|
function. This function gets forwarded to the Windows VST plugin through
|
||||||
VST plugin through the Wine VST host using a single socket. The `process()`
|
the Wine VST. In the rare event that the plugin does not support
|
||||||
function has been deprecated, so a VST host will never call it if
|
`processReplacing()` and only supports The deprecated commutative
|
||||||
`processReplacing()` is supported by the plugin.
|
`process()` function, then the Wine VST host will emulate the behavior of
|
||||||
- Updates of the Windows VST plugin's `AEffect` object. This object tells the
|
`processReplacing()` instead.
|
||||||
host about the plugin's capabilities. A copy of this is sent over a socket
|
- The Windows VST plugin's `AEffect` object. A copy of this is sent over a
|
||||||
from the Wine VST hsot to the plugin after it loads the Windows VST plugin
|
socket from the Wine VST hsot to the plugin after the Windows VST plugin
|
||||||
so it can return a pointer to it to the native VST host. Whenever this
|
has finished initializing. Whenever this struct gets updated by the Windows
|
||||||
struct updates, the Windows VST plugin will call the `audioMasterIOChanged`
|
VST plugin, the Windows VST plugin will call the `audioMasterIOChanged()`
|
||||||
host callback and we'll repeat this process.
|
host callback and we'll repeat the process.
|
||||||
|
|
||||||
The operations described above are all handled by first serializing the
|
The operations described above involving the host -> plugin `dispatcher()`and
|
||||||
function parameters and any payload into an object before they can be sent
|
plugin -> host `audioMaster()` functions are all handled by first serializing
|
||||||
over a socket. The objects used for encoding both the requests and and the
|
the function parameters and any payload data into a binary format so they can
|
||||||
responses for theses events can be found in `src/common/communication.h`
|
be sent over a socket. The objects used for encoding both the requests and
|
||||||
along with functions that read and write these objects over streams and
|
the responses for theses events can be found in `src/common/serialization.h`,
|
||||||
sockets. The actual binary serialization is handled using
|
and the functions that actually read and write these objects over the sockets
|
||||||
[bitsery](https://github.com/fraillt/bitsery).
|
are located in `src/common/communication.h`. The actual binary serialization
|
||||||
|
is handled using [bitsery](https://github.com/fraillt/bitsery).
|
||||||
|
|
||||||
Sending and receiving host -> plugin and plugin -> host events happen in the
|
Actually sending and receiving the events happens in the `send_event()` and
|
||||||
`send_event()` and `receive_event()` functions. Reading data and writing the
|
`receive_event()` functions. When calling either `dispatch()` or
|
||||||
results back for host-to-plugin `dispatcher()` calls and for plugin-to-host
|
`audioMaster()`, the caller will oftentimes either pass along some kind of
|
||||||
`audioMaster()` callbacks happen in the `DispatchDataConverter` and
|
data structure through the void pointer function argument, or they expect the
|
||||||
`HostCallbackDataConverter` classes respectively, with a bit of extra glue
|
function's return value to be a pointer to some kind of struct provided by
|
||||||
for GUI related operations in `PluginBridge::dispatch_wrapper`. On the
|
the plugin or host. The behaviour for reading from and writing into these
|
||||||
receiving end, the `passthrough_event()` function calls the callback
|
void pointers and returning pointers to objects when needed is encapsulated
|
||||||
functions and handles the marshalling between our data types and the VST
|
in the `DispatchDataConverter` and `HostCallbackDataCovnerter` classes for
|
||||||
API's different pointer types. This behaviour is separated from
|
the `dispatcher()` and `audioMaster()` functions respectively. For operations
|
||||||
`receive_event()` so we can some special handling for MIDI events, since a
|
involving the plugin editor there is also some extra glue in
|
||||||
select few plugins only store pointers to the received events rather than
|
`PluginBridge::dispatch_wrapper`. On the receiving end of the function calls,
|
||||||
copies of the objects. This requires the received event data to live at least
|
the `passthrough_event()` function calls the callback functions and handles
|
||||||
until the next audio buffer gets processed.
|
the marshalling between our data types created by the `*DataConverter`
|
||||||
|
classes and the VST API's different pointer types. This behaviour is
|
||||||
|
separated from `receive_event()` so we can handle MIDI events separately.
|
||||||
|
This is needed because a select few plugins only store pointers to the
|
||||||
|
received events rather than copies of the objects. Because of this, the
|
||||||
|
received event data must live at least until the next audio buffer gets
|
||||||
|
processed so it needs to be stored temporarily.
|
||||||
|
|
||||||
6. The Wine VST host loads the Windows VST plugin and starts forwarding messages
|
6. The Wine VST host loads the Windows VST plugin and starts forwarding messages
|
||||||
over the sockets described above.
|
over the sockets described above.
|
||||||
|
|||||||
Reference in New Issue
Block a user