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 what this has been tested on and what does or does not work.
|
||||
- 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
|
||||
|
||||
@@ -18,6 +23,7 @@ the following dependencies:
|
||||
|
||||
- gcc (tested using GCC 9.2)
|
||||
- A Wine installation with `wiengcc` and the development headers.
|
||||
- Boost
|
||||
- [msgpack-c](git@github.com:msgpack/msgpack-c.git)
|
||||
|
||||
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
|
||||
# file.
|
||||
|
||||
boost_dep = dependency('boost', modules : ['filesystem'])
|
||||
msgpack_dep = dependency('msgpack')
|
||||
|
||||
include_dir = include_directories('src/include')
|
||||
@@ -40,8 +41,8 @@ shared_library(
|
||||
linux_plugin_src + common_src,
|
||||
native : true,
|
||||
include_directories : include_dir,
|
||||
dependencies : [msgpack_dep],
|
||||
link_args : []
|
||||
dependencies : [boost_dep, msgpack_dep],
|
||||
link_args : ['-ldl']
|
||||
)
|
||||
|
||||
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 <boost/dll/runtime_symbol_info.hpp>
|
||||
#include <boost/process/io.hpp>
|
||||
#include <boost/process/search_path.hpp>
|
||||
#include <iostream>
|
||||
#include <msgpack.hpp>
|
||||
|
||||
@@ -9,6 +12,22 @@
|
||||
// implementation details, such as the use of intptr_t isntead of void*
|
||||
// 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
|
||||
* through to the winelib VST host.
|
||||
@@ -36,6 +55,8 @@ intptr_t Bridge::dispatch(AEffect* /*plugin*/,
|
||||
switch (opcode) {
|
||||
case effClose:
|
||||
// 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
|
||||
// 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
|
||||
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 <boost/process/child.hpp>
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -9,12 +11,24 @@
|
||||
*/
|
||||
class Bridge {
|
||||
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`.
|
||||
|
||||
/**
|
||||
* 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,
|
||||
int32_t opcode,
|
||||
@@ -28,4 +42,9 @@ class Bridge {
|
||||
int32_t sample_frames);
|
||||
void set_parameter(AEffect* plugin, int32_t index, float value);
|
||||
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