Add basic communication with a child process

This commit is contained in:
Robbert van der Helm
2020-02-08 17:18:39 +01:00
parent 65996f856a
commit b757001435
5 changed files with 122 additions and 4 deletions
+6
View File
@@ -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
View File
@@ -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(
+40
View File
@@ -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)
};
+52
View File
@@ -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
View File
@@ -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;
}; };