Merge yabridge-group.exe and yabridge-host.exe

They were 95% the same, so this makes a lot more sense this way. When
group host processes were introduced yabridge only did VST2 bridging,
but we already have a plugin type argument anyways so might as well
reuse that for group hosts.
This commit is contained in:
Robbert van der Helm
2022-04-16 23:26:24 +02:00
parent 4d9e865dd3
commit b3efcf4e06
14 changed files with 54 additions and 390 deletions
+3 -3
View File
@@ -36,15 +36,15 @@ void* find_plugin_library(const std::string& name) {
// `dlopen()` for distro packaged versions of yabridge
const std::vector<fs::path> search_path = get_augmented_search_path();
if (const auto& yabridge_host_path =
search_in_path(search_path, yabridge_individual_host_name)) {
search_in_path(search_path, yabridge_host_name)) {
const fs::path candidate = yabridge_host_path->parent_path() / name;
if (fs::exists(candidate)) {
return dlopen(candidate.c_str(), RTLD_LAZY | RTLD_LOCAL);
}
}
if (const auto& yabridge_host_32_path = search_in_path(
search_path, yabridge_individual_host_name_32bit)) {
if (const auto& yabridge_host_32_path =
search_in_path(search_path, yabridge_host_name_32bit)) {
const fs::path candidate =
yabridge_host_32_path->parent_path() / name;
if (fs::exists(candidate)) {
+2 -17
View File
@@ -30,26 +30,11 @@ constexpr char yabridge_vst3_plugin_name[] = "@vst3_plugin_name@";
* The name of the Wine plugin host application, e.g. `yabridge-host.exe` for
* the regular 64-bit build.
*/
constexpr char yabridge_individual_host_name[] =
"@individual_host_binary_64bit@";
/**
* The name of the group host application, e.g. `yabridge-group.exe` for the
* regular 64-bit build.
*/
constexpr char yabridge_group_host_name[] = "@group_host_binary_64bit@";
constexpr char yabridge_host_name[] = "@host_binary_64bit@";
/**
* The name of the 32-bit Wine plugin host application, e.g.
* `yabridge-host-32.exe`.` This is used as a bitbridge to be able to load
* legacy 32-bit only Windows plugins from a 64-bit Linux host.
*/
constexpr char yabridge_individual_host_name_32bit[] =
"@individual_host_binary_32bit@";
/**
* The name of the 32-bit group host application, e.g. `yabridge-group-32.exe`.`
* This is used as a bitbridge to be able to load legacy 32-bit only Windows
* plugins from a 64-bit Linux host.
*/
constexpr char yabridge_group_host_name_32bit[] = "@group_host_binary_32bit@";
constexpr char yabridge_host_name_32bit[] = "@host_binary_32bit@";
+2 -4
View File
@@ -7,10 +7,8 @@ config_header = configure_file(
{
'vst2_plugin_name': 'lib' + vst2_plugin_name + '.so',
'vst3_plugin_name': 'lib' + vst3_plugin_name + '.so',
'individual_host_binary_32bit': individual_host_name_32bit + '.exe',
'individual_host_binary_64bit': individual_host_name_64bit + '.exe',
'group_host_binary_32bit': group_host_name_32bit + '.exe',
'group_host_binary_64bit': group_host_name_64bit + '.exe',
'host_binary_32bit': host_name_32bit + '.exe',
'host_binary_64bit': host_name_64bit + '.exe',
}
)
)
+4 -6
View File
@@ -110,8 +110,7 @@ IndividualHost::IndividualHost(asio::io_context& io_context,
: HostProcess(io_context, sockets),
plugin_info_(plugin_info),
host_path_(find_vst_host(plugin_info.native_library_path_,
plugin_info.plugin_arch_,
false)),
plugin_info.plugin_arch_)),
handle_(launch_host(
host_path_,
{
@@ -172,8 +171,7 @@ GroupHost::GroupHost(asio::io_context& io_context,
: HostProcess(io_context, sockets),
plugin_info_(plugin_info),
host_path_(find_vst_host(plugin_info.native_library_path_,
plugin_info.plugin_arch_,
true)) {
plugin_info.plugin_arch_)) {
// When using plugin groups, we'll first try to connect to an existing group
// host process and ask it to host our plugin. If no such process exists,
// then we'll start a new process. In the event that multiple yabridge
@@ -205,8 +203,8 @@ GroupHost::GroupHost(asio::io_context& io_context,
// because it should run independently of this yabridge instance as
// it will likely outlive it.
Process::Handle group_host =
launch_host(host_path_, {group_socket_path.string()}, logger,
config, plugin_info);
launch_host(host_path_, {"group", group_socket_path.string()},
logger, config, plugin_info);
group_host.detach();
group_host_connect_handler_ =
+3 -6
View File
@@ -304,13 +304,10 @@ std::string create_logger_prefix(const fs::path& endpoint_base_dir) {
}
fs::path find_vst_host(const ghc::filesystem::path& this_plugin_path,
LibArchitecture plugin_arch,
bool use_plugin_groups) {
auto host_name = use_plugin_groups ? yabridge_group_host_name
: yabridge_individual_host_name;
LibArchitecture plugin_arch) {
auto host_name = yabridge_host_name;
if (plugin_arch == LibArchitecture::dll_32) {
host_name = use_plugin_groups ? yabridge_group_host_name_32bit
: yabridge_individual_host_name_32bit;
host_name = yabridge_host_name_32bit;
}
// If our `.so` file is a symlink, then search for the host in the directory
+2 -5
View File
@@ -181,7 +181,7 @@ std::string create_logger_prefix(
const ghc::filesystem::path& endpoint_base_dir);
/**
* Finds the Wine VST host (either `yabridge-host.exe` or `yabridge-host.exe`
* Finds the Wine VST host (either `yabridge-host.exe` or `yabridge-host-32.exe`
* depending on the plugin). For this we will search in two places:
*
* 1. Alongside libyabridge-{vst2,vst3}.so if the file got symlinked. This is
@@ -195,16 +195,13 @@ std::string create_logger_prefix(
* from.
* @param plugin_arch The architecture of the plugin, either 64-bit or 32-bit.
* Used to determine which host application to use, if available.
* @param use_plugin_groups Whether the plugin is using plugin groups and we
* should be looking for the group host instead of the individual plugin host.
*
* @return The a path to the VST host, if found.
* @throw std::runtime_error If the Wine VST host could not be found.
*/
ghc::filesystem::path find_vst_host(
const ghc::filesystem::path& this_plugin_path,
LibArchitecture plugin_arch,
bool use_plugin_groups);
LibArchitecture plugin_arch);
/**
* Generate the group socket endpoint name used based on the name of the group,
-98
View File
@@ -1,98 +0,0 @@
// yabridge: a Wine plugin bridge
// Copyright (C) 2020-2022 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 <https://www.gnu.org/licenses/>.
#include <iostream>
// Generated inside of the build directory
#include <config.h>
#include <version.h>
#include "../common/utils.h"
#include "bridges/group.h"
#include "bridges/vst2.h"
static const std::string host_name = "yabridge group host version " +
std::string(yabridge_git_version)
#ifdef __i386__
+ " (32-bit compatibility mode)"
#endif
;
/**
* This works very similar to the host application defined in
* `individual-host.cpp`, but instead of just loading a single plugin this will
* act as a daemon that can host multiple 'grouped' plugins. This works by
* allowing the `libyabridge-{vst2,vst3}.so` instance to connect this this
* process over a socket to ask this process to host a VST `.dll` file using a
* provided socket. After that initialization step both the regular individual
* plugin host and this group plugin host will function identically on both the
* plugin and the Wine VST host side.
*/
int YABRIDGE_EXPORT
#ifdef WINE_USE_CDECL
__cdecl
#endif
main(int argc, char* argv[]) {
// Instead of directly hosting a plugin, this process will receive a UNIX
// domain socket endpoint path that it should listen on to allow yabridge
// instances to spawn plugins in this process.
if (argc < 2) {
std::cerr << host_name << std::endl;
std::cerr << "Usage: "
#ifdef __i386__
<< yabridge_group_host_name_32bit
#else
<< yabridge_group_host_name
#endif
<< " <unix_domain_socket>" << std::endl;
return 1;
}
const std::string group_socket_endpoint_path(argv[1]);
std::cerr << "Initializing " << host_name << std::endl;
// NOTE: Some plugins use Microsoft COM, but don't initialize it first and
// just pray the host does it for them. Examples of this are
// PSPaudioware's InfiniStrip and Shattered Glass Audio Code Red Free.
OleInitialize(nullptr);
try {
GroupBridge bridge(group_socket_endpoint_path);
// Blocks the main thread until all plugins have exited
bridge.handle_incoming_connections();
} catch (const std::system_error& error) {
// If another process is already listening on the socket, we'll just
// print a message and exit quietly. This could happen if the host
// starts multiple yabridge instances that all use the same plugin group
// at the same time.
// The same error is also used if we could not create a pipe. Since that
// error is so rare, we'll just print to STDERR before that happens to
// differentiate the two cases.
std::cerr << "Another process is already listening on this group's "
"socket, connecting to the existing process:"
<< std::endl;
std::cerr << error.what() << std::endl;
return 0;
}
// Like in `individual-host.cpp`, this shouldn't be needed, but sometimes
// with Wine background threads will be kept alive while this process exits
TerminateProcess(GetCurrentProcess(), 0);
}
-155
View File
@@ -1,155 +0,0 @@
// yabridge: a Wine plugin bridge
// Copyright (C) 2020-2022 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 <https://www.gnu.org/licenses/>.
#include <iostream>
#include <thread>
// Generated inside of the build directory
#include <config.h>
#include <version.h>
#include "../common/utils.h"
#include "bridges/vst2.h"
#ifdef WITH_VST3
#include "bridges/vst3.h"
#endif
static const std::string host_name = "yabridge host version " +
std::string(yabridge_git_version)
#ifdef __i386__
+ " (32-bit compatibility mode)"
#endif
;
/**
* This is the default plugin host application. It will load the specified
* plugin plugin, and then connect back to the `libyabridge-{vst2,vst3}.so`
* instance that spawned this over the socket.
*/
int YABRIDGE_EXPORT
#ifdef WINE_USE_CDECL
__cdecl
#endif
main(int argc, char* argv[]) {
// We pass the plugin format, the name of the VST2 plugin .dll file or VST3
// bundle to load, the base directory for the Unix domain socket endpoints
// to connect to and the process ID of the process the native plugin is
// being hosted in as arguments for yabridge-host.exe
if (argc < 5) {
std::cerr << host_name << std::endl;
std::cerr << "Usage: "
#ifdef __i386__
<< yabridge_individual_host_name_32bit
#else
<< yabridge_individual_host_name
#endif
<< " <plugin_type> <plugin_location> "
"<endpoint_base_directory> <parent_pid>"
<< std::endl;
return 1;
}
const std::string plugin_type_str(argv[1]);
const PluginType plugin_type = plugin_type_from_string(plugin_type_str);
const std::string plugin_location(argv[2]);
const std::string socket_endpoint_path(argv[3]);
const pid_t parent_pid = std::stoi(argv[4]);
std::cerr << "Initializing " << host_name << std::endl;
std::cerr << "Preparing to load " << plugin_type_to_string(plugin_type)
<< " plugin at '" << plugin_location << "'" << std::endl;
// NOTE: Some plugins use Microsoft COM, but don't initialize it first and
// just pray the host does it for them. Examples of this are
// PSPaudioware's InfiniStrip and Shattered Glass Audio Code Red Free.
OleInitialize(nullptr);
// As explained in `Vst2Bridge`, the plugin has to be initialized in the
// same thread as the one that calls `io_context.run()`. This setup is
// slightly more convoluted than it has to be, but doing it this way we
// don't need to differentiate between individually hosted plugins and
// plugin groups when it comes to event handling.
MainContext main_context{};
std::unique_ptr<HostBridge> bridge;
try {
switch (plugin_type) {
case PluginType::vst2:
bridge = std::make_unique<Vst2Bridge>(
main_context, plugin_location, socket_endpoint_path,
parent_pid);
break;
case PluginType::vst3:
#ifdef WITH_VST3
bridge = std::make_unique<Vst3Bridge>(
main_context, plugin_location, socket_endpoint_path,
parent_pid);
#else
std::cerr << "This version of yabridge has not been compiled "
"with VST3 support"
<< std::endl;
return 1;
#endif
break;
case PluginType::unknown:
std::cerr << "Unknown plugin type '" << plugin_type_str << "'"
<< std::endl;
return 1;
break;
};
} catch (const std::exception& error) {
std::cerr << "Error while initializing the Wine plugin host:"
<< std::endl;
std::cerr << error.what() << std::endl;
// See below, just returning from `main()` isn't enough to terminate the
// process
TerminateProcess(GetCurrentProcess(), 0);
return 1;
}
// Let the plugin receive and handle its events on its own thread. Some
// potentially unsafe events that should always be run from the UI thread
// will be posted to `main_context`.
Win32Thread worker_thread([&]() {
pthread_setname_np(pthread_self(), "worker");
bridge->run();
// // When the sockets get closed, this application should
// // terminate gracefully
// main_context.stop();
// FIXME: So some of the background threads spawned by the plugin may
// get stuck if the host got terminated abruptly. After an entire
// day of debugging I still have no idea whether this is a bug in
// yabridge, Wine, or those plugins, but just killing off this
// process and all of its threads 'fixes' the issue.
//
// https://github.com/robbert-vdh/yabridge/issues/69
TerminateProcess(GetCurrentProcess(), 0);
});
std::cerr << "Finished initializing '" << plugin_location << "'"
<< std::endl;
// Handle Win32 messages and X11 events on a timer, just like in
// `GroupBridge::async_handle_events()``
main_context.async_handle_events(
[&]() { bridge->handle_events(); },
[&]() { return !bridge->inhibits_event_loop(); });
main_context.run();
}
+5 -52
View File
@@ -1,11 +1,7 @@
# The plugin host applications come on four flavours: hosting a single plugin
# and hosting multiple plugins within a single process, and 32-bit and 64-bit.
# We will compile the shared parts for the individual an plugin group hosts as a
# static library first to cut down on compile times.
#
# As with the libraries, we cannot call `executable()` here since we would like
# to keep all relevant files in the root of the build directory, and Meson
# doesn't have a way to customize that yet.
if is_64bit_system
host_64bit_deps = [
configuration_dep,
@@ -54,7 +50,7 @@ if with_bitbridge
endif
endif
host_common_sources = files(
host_sources = files(
'../common/communication/vst2.cpp',
'../common/serialization/vst2.cpp',
'../common/configuration.cpp',
@@ -67,14 +63,16 @@ host_common_sources = files(
'../common/utils.cpp',
'../include/llvm/small-vector.cpp',
'bridges/common.cpp',
'bridges/group.cpp',
'bridges/vst2.cpp',
'editor.cpp',
'host.cpp',
'utils.cpp',
'xdnd-proxy.cpp',
)
if with_vst3
host_common_sources += files(
host_sources += files(
'../common/logging/vst3.cpp',
'../common/serialization/vst3/component-handler/component-handler.cpp',
'../common/serialization/vst3/component-handler/component-handler-2.cpp',
@@ -139,48 +137,3 @@ if with_vst3
'bridges/vst3.cpp',
)
endif
# These will be linked against a static library made from `host_common_sources`
individual_host_sources = files(
'individual-host.cpp',
)
group_host_sources = files(
'bridges/group.cpp',
'group-host.cpp',
)
if is_64bit_system
host_common_64bit = static_library(
'host_common_64bit',
host_common_sources,
native : false,
include_directories : include_dir,
dependencies : host_64bit_deps,
cpp_args : compiler_options + wine_64bit_compiler_options,
link_args : ['-m64'],
)
host_common_64bit_dep = declare_dependency(
link_with : host_common_64bit,
include_directories : include_dir,
dependencies : host_64bit_deps,
compile_args : compiler_options + wine_64bit_compiler_options,
)
endif
if with_bitbridge
host_common_32bit = static_library(
'host_common_32bit',
host_common_sources,
native : false,
include_directories : include_dir,
dependencies : host_32bit_deps,
cpp_args : compiler_options + wine_32bit_compiler_options,
link_args : ['-m32'],
)
host_common_32bit_dep = declare_dependency(
link_with : host_common_32bit,
include_directories : include_dir,
dependencies : host_32bit_deps,
compile_args : compiler_options + wine_32bit_compiler_options,
)
endif