diff --git a/src/common/communication/vst3.h b/src/common/communication/vst3.h new file mode 100644 index 00000000..bab15217 --- /dev/null +++ b/src/common/communication/vst3.h @@ -0,0 +1,65 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +#include "common.h" + +/** + * Manages all the sockets used for communicating between the plugin and the + * Wine host when hosting a VST3 plugin. + * + * On the plugin side this class should be initialized with `listen` set to + * `true` before launching the Wine VST host. This will start listening on the + * sockets, and the call to `connect()` will then accept any incoming + * connections. + * + * @tparam Thread The thread implementation to use. On the Linux side this + * should be `std::jthread` and on the Wine side this should be `Win32Thread`. + */ +template +class Vst3Sockets : public Sockets { + public: + /** + * Sets up the sockets using the specified base directory. The sockets won't + * be active until `connect()` gets called. + * + * @param io_context The IO context the sockets should be bound to. Relevant + * when doing asynchronous operations. + * @param endpoint_base_dir The base directory that will be used for the + * Unix domain sockets. + * @param listen If `true`, start listening on the sockets. Incoming + * connections will be accepted when `connect()` gets called. This should + * be set to `true` on the plugin side, and `false` on the Wine host side. + * + * @see Vst3Sockets::connect + */ + Vst3Sockets(boost::asio::io_context& io_context, + const boost::filesystem::path& endpoint_base_dir, + bool listen) + : Sockets(endpoint_base_dir) {} + + ~Vst3Sockets() { close(); } + + void connect() override {} + + void close() override { + // Manually close all sockets so we break out of any blocking operations + // that may still be active + } +}; diff --git a/src/wine-host/bridges/group.cpp b/src/wine-host/bridges/group.cpp index 350e400b..d38cbf49 100644 --- a/src/wine-host/bridges/group.cpp +++ b/src/wine-host/bridges/group.cpp @@ -208,7 +208,9 @@ void GroupBridge::accept_requests() { break; case PluginType::vst3: #ifdef WITH_VST3 - throw std::runtime_error("TODO: Not yet implemented"); + bridge = std::make_unique( + main_context, request.plugin_path, + request.endpoint_base_dir); #else throw std::runtime_error( "This version of yabridge has not been compiled " diff --git a/src/wine-host/bridges/vst2.h b/src/wine-host/bridges/vst2.h index 403977b5..5f80c029 100644 --- a/src/wine-host/bridges/vst2.h +++ b/src/wine-host/bridges/vst2.h @@ -28,7 +28,6 @@ #include #include -#include #include #include "../../common/communication/vst2.h" @@ -43,14 +42,14 @@ class Vst2Bridge : public HostBridge { public: /** - * Initializes the Windows VST plugin and set up communication with the - * native Linux VST plugin. + * Initializes the Windows VST2 plugin and set up communication with the + * native Linux VST2 plugin. * * @param main_context The main IO context for this application. Most events * will be dispatched to this context, and the event handling loop should * also be run from this context. - * @param plugin_dll_path A (Unix style) path to the VST plugin .dll file to - * load. + * @param plugin_dll_path A (Unix style) path to the VST2 plugin .dll file + * to load. * @param endpoint_base_dir The base directory used for the socket * endpoints. See `Sockets` for more information. * diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 11535ba9..700a46d0 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -18,24 +18,38 @@ #include "../boost-fix.h" -// TODO: Do something with this, I just wanted to get the build working #include -void justdewit(const std::string& path) { +Vst3Bridge::Vst3Bridge(MainContext& main_context, + std::string plugin_dll_path, + std::string endpoint_base_dir) + : HostBridge(plugin_dll_path), + main_context(main_context), + sockets(main_context.context, endpoint_base_dir, false) { std::string error; - std::shared_ptr plugin = - VST3::Hosting::Win32Module::create(path, error); + module = VST3::Hosting::Win32Module::create(plugin_dll_path, error); - if (plugin) { + // TODO: Do something more useful with this + if (module) { // TODO: They use some thin wrappers around the interfaces, we can // probably reuse these instead of having to make our own - VST3::Hosting::FactoryInfo info = plugin->getFactory().info(); - std::cout << "Plugin name: " << plugin->getName() << std::endl; + VST3::Hosting::FactoryInfo info = module->getFactory().info(); + std::cout << "Plugin name: " << module->getName() << std::endl; std::cout << "Vendor: " << info.vendor() << std::endl; std::cout << "URL: " << info.url() << std::endl; std::cout << "Send spam to: " << info.email() << std::endl; } else { - std::cerr << "Ohnoes!" << std::endl; - std::cerr << error << std::endl; + throw std::runtime_error("Could not load the VST3 module for '" + + plugin_dll_path + "': " + error); } + + sockets.connect(); + + // TODO: We should send a copy of the configuration from the plugin at this + // point config = sockets.host_vst_control.receive_single(); +} + +void Vst3Bridge::run() { + // TODO: Do something + std::cerr << "TODO: Not yet implemented" << std::endl; } diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index e6206b82..45ca30a6 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -18,4 +18,65 @@ #include -void justdewit(const std::string& path); +#include + +#include "../../common/communication/vst3.h" +#include "../../common/configuration.h" +#include "common.h" + +/** + * This hosts a Windows VST3 plugin, forwards messages sent by the Linux VST + * plugin and provides host callback function for the plugin to talk back. + */ +class Vst3Bridge : public HostBridge { + public: + /** + * Initializes the Windows VST3 plugin and set up communication with the + * native Linux VST plugin. + * + * @param main_context The main IO context for this application. Most events + * will be dispatched to this context, and the event handling loop should + * also be run from this context. + * @param plugin_dll_path A (Unix style) path to the VST plugin .dll file to + * load. + * @param endpoint_base_dir The base directory used for the socket + * endpoints. See `Sockets` for more information. + * + * @note The object has to be constructed from the same thread that calls + * `main_context.run()`. + * + * @throw std::runtime_error Thrown when the VST plugin could not be loaded, + * or if communication could not be set up. + */ + Vst3Bridge(MainContext& main_context, + std::string plugin_dll_path, + std::string endpoint_base_dir); + + void run() override; + + private: + /** + * The IO context used for event handling so that all events and window + * message handling can be performed from a single thread, even when hosting + * multiple plugins. + */ + MainContext& main_context; + + /** + * The configuration for this instance of yabridge based on the `.so` file + * that got loaded by the host. This configuration gets loaded on the plugin + * side, and then sent over to the Wine host as part of the startup process. + */ + Configuration config; + + std::shared_ptr module; + + /** + * All sockets used for communicating with this specific plugin. + * + * NOTE: This is defined **after** the threads on purpose. This way the + * sockets will be closed first, and we can then safely wait for the + * threads to exit. + */ + Vst3Sockets sockets; +}; diff --git a/src/wine-host/individual-host.cpp b/src/wine-host/individual-host.cpp index 1201ecf0..aeff5f5a 100644 --- a/src/wine-host/individual-host.cpp +++ b/src/wine-host/individual-host.cpp @@ -85,10 +85,8 @@ main(int argc, char* argv[]) { break; case PluginType::vst3: #ifdef WITH_VST3 - justdewit(plugin_location); - - std::cerr << "TODO: Not yet implemented" << std::endl; - return 1; + bridge = std::make_unique( + main_context, plugin_location, socket_endpoint_path); #else std::cerr << "This version of yabridge has not been compiled " "with VST3 support"