mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 12:10:09 +02:00
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:
@@ -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)) {
|
||||
|
||||
@@ -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@";
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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_ =
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user