diff --git a/meson.build b/meson.build index 3e59fc38..8d2bba62 100644 --- a/meson.build +++ b/meson.build @@ -22,6 +22,7 @@ endif boost_dep = dependency('boost', modules : ['filesystem']) msgpack_dep = dependency('msgpack') +threads_dep = dependency('threads') include_dir = include_directories('src/include') @@ -33,7 +34,7 @@ shared_library( ], native : true, include_directories : include_dir, - dependencies : [boost_dep, msgpack_dep], + dependencies : [boost_dep, msgpack_dep, threads_dep], link_args : ['-ldl'] ) diff --git a/src/plugin/bridge.cpp b/src/plugin/bridge.cpp index 5d6b7dec..83588776 100644 --- a/src/plugin/bridge.cpp +++ b/src/plugin/bridge.cpp @@ -38,18 +38,30 @@ namespace fs = boost::filesystem; */ constexpr auto yabridge_wine_host_name = "yabridge-host.exe"; +fs::path find_vst_plugin(); fs::path find_wine_vst_host(); +fs::path generate_endpoint_name(); bp::environment set_wineprefix(); // TODO: When adding debug information, print both the path to the VST host and // the chosen wineprefix Bridge::Bridge() - : vst_stdin(), + : io_context(), + socket_endpoint(generate_endpoint_name().string()), + host_vst_dispatch(io_context), + vst_stdin(), vst_stdout(), vst_host(find_wine_vst_host(), bp::std_in = vst_stdin, bp::std_out = vst_stdout, - bp::env = set_wineprefix()) {} + bp::env = set_wineprefix()) { + boost::asio::local::stream_protocol::acceptor acceptor(io_context, + socket_endpoint); + + // It's very important that these sockets are connected to in the same order + // in the Wine VST host + acceptor.accept(host_vst_dispatch); +} /** * Handle an event sent by the VST host. Most of these opcodes will be passed @@ -140,6 +152,58 @@ fs::path find_wine_vst_host() { return vst_host_path; } +/** + * Find the VST plugin .dll file that corresponds to this copy of + * `libyabridge.so`. This should be the same as the name of this file but with a + * `.dll` file extension instead of `.so`. + * + * @return The a path to the accompanying VST plugin .dll file. + * @throw std::runtime_error If no matching .dll file could be found. + */ +fs::path find_vst_plugin() { + fs::path plugin_path = boost::dll::this_line_location(); + plugin_path.replace_extension(".dll"); + + // This function is used in the constructor's initializer list so we have to + // throw when the path could not be found + if (!fs::exists(plugin_path)) { + throw std::runtime_error( + "'" + plugin_path.string() + + "' does not exist, make sure to rename 'libyabridge.so' to match a " + "VST plugin .dll file."); + } + + return plugin_path; +} + +/** + * Generate a unique name for the Unix domain socket endpoint based on the VST + * plugin's name. + * + * @return A path to a not yet existing Unix domain socket endpoint. + * @throw std::runtime_error If no matching .dll file could be found. + */ +fs::path generate_endpoint_name() { + auto plugin_name = + find_vst_plugin().filename().replace_extension("").string(); + + fs::path candidate_endpoint; + do { + std::string random_id("TODO"); + candidate_endpoint = + fs::temp_directory_path() + .append("yabridge") + .append(plugin_name + "-" + random_id + ".sock"); + } while (fs::exists(candidate_endpoint)); + + // TODO: Should probably try creating the endpoint right here and catch any + // exceptions since this could technically result in a race condition + // when two instances of yabridge decide to use the same endpoint name + // at the same time + + return candidate_endpoint; +} + /** * Locate the wineprefix and set the `WINEPREFIX` environment variable if found. * This way it's also possible to run .dll files outside of a wineprefix using diff --git a/src/plugin/bridge.h b/src/plugin/bridge.h index 28fa54e6..cdd907c0 100644 --- a/src/plugin/bridge.h +++ b/src/plugin/bridge.h @@ -18,6 +18,8 @@ #include +#include +#include #include /** @@ -60,6 +62,15 @@ class Bridge { float get_parameter(AEffect* plugin, int32_t index); private: + boost::asio::io_context io_context; + boost::asio::local::stream_protocol::endpoint socket_endpoint; + + // The naming convention for these sockets is `__`. For + // instance the socket named `host_vst_dispatch` forwards + // `AEffect.dispatch()` calls from the native VST host to the Windows VST + // plugin (through the Wine VST host). + boost::asio::local::stream_protocol::socket host_vst_dispatch; + boost::process::opstream vst_stdin; boost::process::ipstream vst_stdout; boost::process::child vst_host;