mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Further describe the architecture
This commit is contained in:
@@ -119,76 +119,89 @@ variables:
|
|||||||
|
|
||||||
I started this project because the alternatives were either unmaintained, not
|
I started this project because the alternatives were either unmaintained, not
|
||||||
self-contained or very difficult to work with. With this implementation I'd like
|
self-contained or very difficult to work with. With this implementation I'd like
|
||||||
to prioritize maintainability and correctness, even if it would cause slightly
|
to prioritize maintainability and correctness, with performance being a
|
||||||
more overhead than a more optimized solution would. Please let me know if you
|
secondary goal. Please let me know if you have any suggestions on how to improve
|
||||||
have any suggestions on how to improve this!
|
this!
|
||||||
|
|
||||||
## 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 or symlink of
|
(`yabridge-host.exe`/`yabridge-host.exe.so`). I'll refer to a copy of or a
|
||||||
`libyabridge.so` as _the plugin_, the native Linux VST host that's hosting the
|
symlink to `libyabridge.so` as _the plugin_, the native Linux VST host that's
|
||||||
plugin as _the native VST host_, the Wine VST host that's hosting a Windows
|
hosting the plugin as _the native VST host_, the Wine VST host that's hosting a
|
||||||
`.dll` file as _the Wine VST host_, and the Windows VST plugin that's loaded in
|
Windows `.dll` file as _the Wine VST host_, and the Windows VST plugin that's
|
||||||
the Wine VST host is simply the _Windows VST plugin_. The whole process works as
|
loaded in the Wine VST host is simply the _Windows VST plugin_. The whole
|
||||||
follows:
|
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 `libyabridge.so` should be
|
`Serum_x64.dll` file you'd like to bridge, then there should be a symlink to
|
||||||
renamed to `Serum_x64.so`.
|
`libyabridge.so` named `Serum_x64.so`.
|
||||||
2. The plugin first attempts to locate:
|
2. The plugin first attempts to locate:
|
||||||
|
|
||||||
- The location of `yabridge-host.exe`. For this it will first search for the
|
- The location of `yabridge-host.exe`. For this it will first search for the
|
||||||
file either alongside plugin. This is useful for development, as it allows
|
file either alongside `libyabridge.so`. This is useful for development, as
|
||||||
you to use a symlink to `libyabridge.so` from the build directory causing
|
it allows you to use a symlink from the build directory to cause yabridge
|
||||||
yabridge to use the corresponding `yabridge-host.exe` from the same build
|
to use the `yabridge-host.exe` from that same build directory. If this file
|
||||||
directory. If this file can't be found, it will fall back to searching
|
can't be found, then it will fall back to searching through the search path.
|
||||||
through the search path.
|
- The wine prefix plugin is located in.
|
||||||
- The wine prefix plugin is located in
|
|
||||||
- The corresponding Windows VST plugin `.dll` file.
|
- The corresponding Windows VST plugin `.dll` file.
|
||||||
|
|
||||||
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. I chose to use Unix domain
|
Wine VST host somewhere in a temporary directory and starts listening on it.
|
||||||
sockets rather than shared memory to avoid having to do manual
|
I chose to use Unix domain sockets rather than shared memory because this way
|
||||||
synchronization and because they have very low overhead. This also makes it
|
you get low latency communication with without any busy waits or manual
|
||||||
possible to send arbitrarily large data without having to split it into
|
synchronisation for free. The added benefit is that it also makes it possible
|
||||||
chunks first, which is useful for transmitting audio and preset data.
|
to send arbitrarily large data without having to split it up into chunks
|
||||||
|
first, which is useful for transmitting audio 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.
|
||||||
5. Communication gets set up using multiple sockets over the same end point.
|
5. Communication gets set up using multiple sockets over the same end point.
|
||||||
This allows us to use blocking read operations while handling a certain event
|
This allows us to use blocking read operations from multiple threads to
|
||||||
type to avoid receiving messages out of order. The following types of events
|
handle multiple different events without the risk of receiving packets in the
|
||||||
get assigned a socket:
|
wrong order. The following types of events get assigned a socket:
|
||||||
|
|
||||||
- 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.
|
||||||
- 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.
|
||||||
|
|
||||||
|
Both the `dispatch()` and `audioMasterCallback()` functions are handled in
|
||||||
|
the same way, with some minor variations on how payload data gets
|
||||||
|
serialized depending on the opcode of the event being sent.
|
||||||
|
|
||||||
- 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 plugin
|
`setParameter()` functions. Both functions get forwarded to the Windows VST
|
||||||
through the Wine VST host using a single socket.
|
plugin through the Wine VST host using a single socket because they're very
|
||||||
|
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 `process()` and
|
||||||
`processReplacing()` functions. Both functions get forwarded to the Windows
|
`processReplacing()` functions. Both functions get forwarded to the Windows
|
||||||
VST plugin through the Wine VST host using a single socket.
|
VST plugin through the Wine VST host using a single socket. The `process()`
|
||||||
|
function has been deprecated, so a VST host will never call it if
|
||||||
|
`processReplacing()` is supported by the plugin.
|
||||||
|
- Updates of the Windows VST plugin's `AEffect` object. This object tells the
|
||||||
|
host about the plugin's capabilities. A copy of this is sent over a socket
|
||||||
|
from the Wine VST hsot to the plugin after it loads the Windows VST plugin
|
||||||
|
so it can return a pointer to it to the native VST host. Whenever this
|
||||||
|
struct updates, the Windows VST plugin will call the `audioMasterIOChanged`
|
||||||
|
host callback and we'll repeat this process.
|
||||||
|
|
||||||
The first step when passing through any of these function calls over a socket
|
The operations described above are all handled by first serializing the
|
||||||
is to serialize the function's parameters as binary data. Both request and
|
function parameters and any payload into an object before they can be sent
|
||||||
the corresponding response objects for all of these function calls can be
|
over a socket. The objects used for encoding both the requests and and the
|
||||||
found in `src/common/communication.h`, along with functions to read and write
|
responses for theses events can be found in `src/common/communication.h`
|
||||||
these objects over streams and sockets. The actual binary serialization is
|
along with functions that read and write these objects over streams and
|
||||||
handled using [bitsery](https://github.com/fraillt/bitsery).
|
sockets. The actual binary serialization is handled using
|
||||||
|
[bitsery](https://github.com/fraillt/bitsery).
|
||||||
|
|
||||||
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.
|
||||||
7. After the Windows VST plugin has started loading we will forward all values
|
7. After the Windows VST plugin has started loading we will forward all values
|
||||||
from the plugin's `AEffect` struct to the Linux native VST plugin. After this
|
from the plugin's `AEffect` struct to the Linux native VST plugin using the
|
||||||
point the plugin will stop blocking and has finished loading.
|
socket described above. After this point the plugin will stop blocking and
|
||||||
|
has finished loading.
|
||||||
TODO: Do plugins update their `AEffect` struct update itself after
|
|
||||||
initialization? For instance to change the number of parameters. Is there any
|
|
||||||
way to catch this other than checking for updates ourselves?
|
|
||||||
|
|||||||
Reference in New Issue
Block a user