mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Add basic communication with a child process
This commit is contained in:
@@ -10,6 +10,11 @@ There are a few things that should be done before making this public, including:
|
|||||||
- Document the project setup and the way communication works.
|
- Document the project setup and the way communication works.
|
||||||
- Document what this has been tested on and what does or does not work.
|
- Document what this has been tested on and what does or does not work.
|
||||||
- Document wine32 support.
|
- Document wine32 support.
|
||||||
|
- Add proper debugging support activated using an environment variable.
|
||||||
|
- Write all stdout and stderr output from the plugin to a temporary file so it
|
||||||
|
can be inspected when using a host such as Bitwig that hides this by
|
||||||
|
default.
|
||||||
|
- Catch exceptions during initialization and print them to stderr.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@@ -18,6 +23,7 @@ the following dependencies:
|
|||||||
|
|
||||||
- gcc (tested using GCC 9.2)
|
- gcc (tested using GCC 9.2)
|
||||||
- A Wine installation with `wiengcc` and the development headers.
|
- A Wine installation with `wiengcc` and the development headers.
|
||||||
|
- Boost
|
||||||
- [msgpack-c](git@github.com:msgpack/msgpack-c.git)
|
- [msgpack-c](git@github.com:msgpack/msgpack-c.git)
|
||||||
|
|
||||||
The project can then be compiled as follows:
|
The project can then be compiled as follows:
|
||||||
|
|||||||
+3
-2
@@ -20,6 +20,7 @@ endif
|
|||||||
# about the way these two components work together can be found in the readme
|
# about the way these two components work together can be found in the readme
|
||||||
# file.
|
# file.
|
||||||
|
|
||||||
|
boost_dep = dependency('boost', modules : ['filesystem'])
|
||||||
msgpack_dep = dependency('msgpack')
|
msgpack_dep = dependency('msgpack')
|
||||||
|
|
||||||
include_dir = include_directories('src/include')
|
include_dir = include_directories('src/include')
|
||||||
@@ -40,8 +41,8 @@ shared_library(
|
|||||||
linux_plugin_src + common_src,
|
linux_plugin_src + common_src,
|
||||||
native : true,
|
native : true,
|
||||||
include_directories : include_dir,
|
include_directories : include_dir,
|
||||||
dependencies : [msgpack_dep],
|
dependencies : [boost_dep, msgpack_dep],
|
||||||
link_args : []
|
link_args : ['-ldl']
|
||||||
)
|
)
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <msgpack.hpp>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An event as dispatched by the VST host. These events will get forwarded to
|
||||||
|
* the VST host process running under Wine. The fields here mirror those
|
||||||
|
* arguments sent to the `AEffect::dispatch` function.
|
||||||
|
*/
|
||||||
|
struct Event {
|
||||||
|
int32_t opcode;
|
||||||
|
int32_t parameter;
|
||||||
|
// TODO: This is an intptr_t, is this actually a poitner that should be
|
||||||
|
// dereferenced?
|
||||||
|
intptr_t value;
|
||||||
|
float option;
|
||||||
|
|
||||||
|
MSGPACK_DEFINE(opcode, parameter, value, option)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AN instance of this should be sent back as a response to an incoming event.
|
||||||
|
*/
|
||||||
|
struct EventResult {
|
||||||
|
/**
|
||||||
|
* The result that should be returned from the dispatch function.
|
||||||
|
*/
|
||||||
|
intptr_t return_value;
|
||||||
|
/**
|
||||||
|
* If present, this should get written into the void pointer passed to the
|
||||||
|
* dispatch function.
|
||||||
|
*/
|
||||||
|
std::optional<std::string> result;
|
||||||
|
|
||||||
|
// TODO: Add missing return value fields;
|
||||||
|
|
||||||
|
MSGPACK_DEFINE(return_value, result)
|
||||||
|
};
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "bridge.h"
|
#include "bridge.h"
|
||||||
|
|
||||||
|
#include <boost/dll/runtime_symbol_info.hpp>
|
||||||
|
#include <boost/process/io.hpp>
|
||||||
|
#include <boost/process/search_path.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <msgpack.hpp>
|
#include <msgpack.hpp>
|
||||||
|
|
||||||
@@ -9,6 +12,22 @@
|
|||||||
// implementation details, such as the use of intptr_t isntead of void*
|
// implementation details, such as the use of intptr_t isntead of void*
|
||||||
// here.
|
// here.
|
||||||
|
|
||||||
|
namespace bp = boost::process;
|
||||||
|
namespace fs = boost::filesystem;
|
||||||
|
|
||||||
|
constexpr auto yabridge_wine_host_name = "yabridge-host.exeTODO";
|
||||||
|
|
||||||
|
fs::path find_wine_vst_host();
|
||||||
|
|
||||||
|
Bridge::Bridge()
|
||||||
|
: vst_stdin(),
|
||||||
|
vst_stdout(),
|
||||||
|
vst_host(find_wine_vst_host(),
|
||||||
|
bp::std_in = vst_stdin,
|
||||||
|
bp::std_out = vst_stdout) {
|
||||||
|
// TODO: Wineprefix detection
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an event sent by the VST host. Most of these opcodes will be passed
|
* Handle an event sent by the VST host. Most of these opcodes will be passed
|
||||||
* through to the winelib VST host.
|
* through to the winelib VST host.
|
||||||
@@ -36,6 +55,8 @@ intptr_t Bridge::dispatch(AEffect* /*plugin*/,
|
|||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case effClose:
|
case effClose:
|
||||||
// TODO: Gracefully close the editor?
|
// TODO: Gracefully close the editor?
|
||||||
|
// XXX: Boost.Process will send SIGKILL to the process for us, is
|
||||||
|
// there a way to manually send a SIGTERM signal instead?
|
||||||
|
|
||||||
// The VST API does not have an explicit function for releasing
|
// The VST API does not have an explicit function for releasing
|
||||||
// resources, so we'll have to do it here. The actual plugin
|
// resources, so we'll have to do it here. The actual plugin
|
||||||
@@ -67,3 +88,34 @@ float Bridge::get_parameter(AEffect* /*plugin*/, int32_t /*index*/
|
|||||||
// TODO: Unimplmemented
|
// TODO: Unimplmemented
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the Wine VST hsot (named `yabridge-host.exe`). For this we will search
|
||||||
|
* in two places:
|
||||||
|
*
|
||||||
|
* 1. Alongside libyabridge.so if the file got symlinked. This is useful
|
||||||
|
* when developing, as you can simply symlink the the libyabridge.so
|
||||||
|
* file in the build directory without having to install anything to
|
||||||
|
* /usr.
|
||||||
|
* 2. In the regular search path.
|
||||||
|
*
|
||||||
|
* @return The a path to the VST host, if found.
|
||||||
|
* @throw std::runtime_error If the Wine VST host could not be found.
|
||||||
|
*/
|
||||||
|
fs::path find_wine_vst_host() {
|
||||||
|
fs::path host_path = fs::canonical(boost::dll::this_line_location());
|
||||||
|
host_path.remove_filename().append(yabridge_wine_host_name);
|
||||||
|
if (fs::exists(host_path)) {
|
||||||
|
return host_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bosot will return an empty path if the file could not be found in the
|
||||||
|
// search path
|
||||||
|
fs::path vst_host_path = bp::search_path(yabridge_wine_host_name);
|
||||||
|
if (fs::is_empty(vst_host_path)) {
|
||||||
|
throw std::runtime_error("Could not locate '" +
|
||||||
|
std::string(yabridge_wine_host_name) + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return vst_host_path;
|
||||||
|
}
|
||||||
|
|||||||
+21
-2
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
#include <vestige/aeffect.h>
|
#include <vestige/aeffect.h>
|
||||||
|
|
||||||
|
#include <boost/process/child.hpp>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handles the communication between the Linux native VST plugin and the
|
* This handles the communication between the Linux native VST plugin and the
|
||||||
* Wine VST host. The functions below should be used as callback functions in an
|
* Wine VST host. The functions below should be used as callback functions in an
|
||||||
@@ -9,12 +11,24 @@
|
|||||||
*/
|
*/
|
||||||
class Bridge {
|
class Bridge {
|
||||||
public:
|
public:
|
||||||
// The below four functions are the handlers from the VST2 API. They are
|
/**
|
||||||
|
* Initializes the Wine VST bridge. This sets up the STDIN/STDOUT streams
|
||||||
|
* for event handling and a shared memory buffer for passing audio.
|
||||||
|
*
|
||||||
|
* TODO: Figure out whether shared memory gives us better throughput and/or
|
||||||
|
* lower overhead than using a Unix domain socket would.
|
||||||
|
*
|
||||||
|
* @throw std::runtime_error Thrown when the VST host could not be found, or
|
||||||
|
* if it could not locate and load a VST .dll file.
|
||||||
|
*/
|
||||||
|
Bridge();
|
||||||
|
|
||||||
|
// The four below functions are the handlers from the VST2 API. They are
|
||||||
// called through proxy functions in `plugin.cpp`.
|
// called through proxy functions in `plugin.cpp`.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an event sent by the VST host. Most of these opcodes will be
|
* Handle an event sent by the VST host. Most of these opcodes will be
|
||||||
* passed through to the winelib VST host through a Unix domain socket.
|
* passed through to the winelib VST host.
|
||||||
*/
|
*/
|
||||||
intptr_t dispatch(AEffect* plugin,
|
intptr_t dispatch(AEffect* plugin,
|
||||||
int32_t opcode,
|
int32_t opcode,
|
||||||
@@ -28,4 +42,9 @@ class Bridge {
|
|||||||
int32_t sample_frames);
|
int32_t sample_frames);
|
||||||
void set_parameter(AEffect* plugin, int32_t index, float value);
|
void set_parameter(AEffect* plugin, int32_t index, float value);
|
||||||
float get_parameter(AEffect* plugin, int32_t index);
|
float get_parameter(AEffect* plugin, int32_t index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::process::opstream vst_stdin;
|
||||||
|
boost::process::ipstream vst_stdout;
|
||||||
|
boost::process::child vst_host;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user