mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Merge branch 'feature/bitbridge'
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
# yabridge
|
||||
|
||||
Yet Another way to use Windows VST2 plugins in Linux VST hosts. This project
|
||||
aims to be as transparent as possible to achieve the best possible plugin
|
||||
compatibility while also being easy to debug and maintain.
|
||||
Yet Another way to use Windows VST2 plugins in Linux VST hosts, including an
|
||||
optional bitbridge to run 32-bit Windows plugins in 64-bit Linux hosts. This
|
||||
project aims to be as transparent as possible to achieve the best possible
|
||||
plugin compatibility while also being easy to debug and maintain.
|
||||
|
||||
## TODOs
|
||||
|
||||
@@ -13,7 +14,8 @@ There are a few things that should be done before releasing this, including:
|
||||
- Add missing details if any to the architecture section.
|
||||
- Rewrite parts of this readme.
|
||||
- Briefly verify that this also works fine in Reaper and Ardour.
|
||||
- Document wine32 support.
|
||||
- Finish 32-bit bitbridge.
|
||||
- Upstream the updated `lib32-boost-libs` PKGBUILD.
|
||||
|
||||
## Tested with
|
||||
|
||||
@@ -114,19 +116,39 @@ the following dependencies:
|
||||
- Boost
|
||||
- xcb
|
||||
|
||||
The following dependencies are included as a Meson wrap:
|
||||
The following dependencies are included in the repository as a Meson wrap:
|
||||
|
||||
- bitsery
|
||||
|
||||
The project can then be compiled as follows:
|
||||
|
||||
```shell
|
||||
meson setup --buildtype=release --cross-file cross-wine64.conf build
|
||||
meson setup --buildtype=release --cross-file cross-wine.conf build
|
||||
ninja -C build
|
||||
```
|
||||
|
||||
When developing or debugging yabridge you can change the build type to either
|
||||
`debug` enable debug symbols and disable optimizations.
|
||||
### 32-bit bitbridge
|
||||
|
||||
It's also possible to compile a 32-bit host application for yabridge that's
|
||||
compatible with 32 bit plugins such as old SynthEdit plugins. This will allow
|
||||
yabridge to act as a bitbirdge, allowing you to run old 32-bit only Windows VST2
|
||||
plugins in a modern 64-bit Linux VST host. For this you'll need to have
|
||||
installed the 32 bit versions of the Boost and XCB libraries. This can be
|
||||
enabled as follows:
|
||||
|
||||
```shell
|
||||
# On an existing build
|
||||
meson configure build -Duse-bitbridge=true
|
||||
# Configure a new build from scratch
|
||||
meson setup --buildtype=release --cross-file cross-wine.conf -Duse-bitbridge=true build
|
||||
|
||||
ninja -C build
|
||||
```
|
||||
|
||||
This will produce two files called `yabridge-host-32.exe` and
|
||||
`yabridge-host-32.exe.so`. Yabridge will detect whether the plugin you're trying
|
||||
to load is 32-bit or 64-bit, and will run either `yabridge-host.exe` or
|
||||
`yabridge-host-32.exe` accordingly.
|
||||
|
||||
## Debugging
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
[binaries]
|
||||
c = 'winegcc'
|
||||
cpp = 'wineg++'
|
||||
ar = 'ar'
|
||||
strip = 'strip'
|
||||
|
||||
[properties]
|
||||
# These would be the arguments for 64-bit compilation. We'll set the `-m64` flag
|
||||
# (or `-m32`) in our meson.build to allow the 32-bit bitbridge to be built in
|
||||
# the same build directory as the plugin and the regular 64-bit version of the
|
||||
# host application to ensure that they are always in sync.
|
||||
# https://github.com/mesonbuild/meson/issues/5125
|
||||
|
||||
# c_args = ['-m64']
|
||||
# cpp_args = ['-m64']
|
||||
# cpp_link_args = ['-m64', '-mwindows']
|
||||
|
||||
cpp_link_args = ['-mwindows']
|
||||
needs_exe_wrapper = true
|
||||
@@ -1,17 +0,0 @@
|
||||
[binaries]
|
||||
c = 'winegcc'
|
||||
cpp = 'wineg++'
|
||||
ar = 'ar'
|
||||
strip = 'strip'
|
||||
|
||||
[properties]
|
||||
c_args=['-m64']
|
||||
cpp_args=['-m64']
|
||||
cpp_link_args=['-m64', '-mwindows']
|
||||
needs_exe_wrapper = true
|
||||
|
||||
[host_machine]
|
||||
system = 'linux'
|
||||
cpu_family = 'x86_64'
|
||||
cpu = 'x86_64'
|
||||
endian = 'little'
|
||||
+65
-21
@@ -12,26 +12,36 @@ winelib_check = '''#ifndef __WINE__
|
||||
#error 1
|
||||
#endif'''
|
||||
if not meson.get_compiler('cpp').compiles(winelib_check)
|
||||
error('You need to set up a cross compiler, check the README for more information.')
|
||||
error('You need to set up a cross compiler, check the README for compilation instructions.')
|
||||
endif
|
||||
|
||||
# Depending on the `use-bitbridge` flag we'll enable building a second 32-bit
|
||||
# host application that can act as a bit bridge for using 32-bit Windows plugins
|
||||
# in 64-bit Linux VST hsots. The plugin will determine which host application to
|
||||
# use based on the `.dll` file it's trying to load.
|
||||
# This setup is necessary until Meson provides a way to have multiple
|
||||
# cross-builds for a single build directory:
|
||||
# https://github.com/mesonbuild/meson/issues/5125
|
||||
host_name_64bit = 'yabridge-host'
|
||||
host_name_32bit = 'yabridge-host-32'
|
||||
|
||||
# This provides an easy way to start the Wine VST host using winedbg since it
|
||||
# can be quite a pain to set up
|
||||
compiler_options = []
|
||||
if get_option('use-winedbg')
|
||||
compiler_options += '-DUSE_WINEDBG'
|
||||
endif
|
||||
|
||||
# The application consists of a VST plugin (yabridge) that calls a winelib
|
||||
# program (yabridge-host) that can host Windows VST plugins. More information
|
||||
# about the way these two components work together can be found in the readme
|
||||
# file.
|
||||
if get_option('use-bitbridge')
|
||||
compiler_options += '-DUSE_BITBRIDGE'
|
||||
endif
|
||||
|
||||
# Generate header files for configuration variables such as the current git tag
|
||||
# and the last commit hash
|
||||
# and the name of the host binary
|
||||
subdir('src/common/config')
|
||||
|
||||
boost_dep = dependency('boost', modules : ['filesystem'])
|
||||
# Statically link against Boost.Filesystem, otherwise it becomes impossible to
|
||||
# distribute a prebuilt version of yabridge
|
||||
boost_dep = dependency('boost', modules : ['filesystem'], static : true)
|
||||
bitsery_dep = subproject('bitsery').get_variable('bitsery_dep')
|
||||
threads_dep = dependency('threads')
|
||||
# The built in threads dependency does not know how to handle winegcc
|
||||
@@ -40,6 +50,11 @@ xcb_dep = dependency('xcb')
|
||||
|
||||
include_dir = include_directories('src/include')
|
||||
|
||||
# The application consists of a VST plugin (yabridge) that calls a winelib
|
||||
# program (yabridge-host) that can host Windows VST plugins. More information
|
||||
# about the way these two components work together can be found in the readme
|
||||
# file.
|
||||
|
||||
shared_library(
|
||||
'yabridge',
|
||||
[
|
||||
@@ -56,21 +71,50 @@ shared_library(
|
||||
link_args : ['-ldl']
|
||||
)
|
||||
|
||||
host_sources = [
|
||||
'src/common/logging.cpp',
|
||||
'src/common/serialization.cpp',
|
||||
'src/wine-host/editor.cpp',
|
||||
'src/wine-host/editor.cpp',
|
||||
'src/wine-host/plugin-bridge.cpp',
|
||||
'src/wine-host/vst-host.cpp',
|
||||
'src/wine-host/utils.cpp',
|
||||
version_header,
|
||||
]
|
||||
|
||||
executable(
|
||||
'yabridge-host',
|
||||
[
|
||||
'src/common/logging.cpp',
|
||||
'src/common/serialization.cpp',
|
||||
'src/wine-host/editor.cpp',
|
||||
'src/wine-host/editor.cpp',
|
||||
'src/wine-host/plugin-bridge.cpp',
|
||||
'src/wine-host/vst-host.cpp',
|
||||
'src/wine-host/utils.cpp',
|
||||
version_header,
|
||||
],
|
||||
host_name_64bit,
|
||||
host_sources,
|
||||
native : false,
|
||||
include_directories : include_dir,
|
||||
dependencies : [boost_dep, bitsery_dep, wine_threads_dep, xcb_dep],
|
||||
cpp_args : compiler_options,
|
||||
link_args : []
|
||||
cpp_args : compiler_options + ['-m64'],
|
||||
link_args : ['-m64']
|
||||
)
|
||||
|
||||
if get_option('use-bitbridge')
|
||||
message('Bitbridge enabled, configuring a 32-bit host application')
|
||||
|
||||
# I honestly have no idea what the correct way to have `find_dependency()` use
|
||||
# `/usr/lib32` instead of `/usr/lib` is. If anyone does know, please tell me!
|
||||
winegcc = meson.get_compiler('cpp', native : false)
|
||||
boost_dep = [winegcc.find_library('boost_filesystem')]
|
||||
xcb_dep = [winegcc.find_library('xcb')]
|
||||
|
||||
executable(
|
||||
host_name_32bit,
|
||||
host_sources,
|
||||
native : false,
|
||||
include_directories : include_dir,
|
||||
dependencies : [boost_dep, bitsery_dep, wine_threads_dep, xcb_dep],
|
||||
# FIXME: 32-bit winegcc defines `__stdcall` differently than the 64-bit
|
||||
# version, and one of the changes is the inclusion of
|
||||
# `__atribute__((__force_align_arg_pointer__))`. For whetever reason
|
||||
# this causes GCC to complain when using function pointers with the
|
||||
# `__stdcall` calling convention in template arguments, although it
|
||||
# otherwise works just fine. We don't ignore any warnings in the
|
||||
# regular host application so this should not cause any issues!
|
||||
cpp_args : compiler_options + ['-m32', '-Wno-ignored-attributes'],
|
||||
link_args : ['-m32']
|
||||
)
|
||||
endif
|
||||
|
||||
+9
-1
@@ -1,6 +1,14 @@
|
||||
option(
|
||||
'use-bitbridge',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : '''Build a 32-bit host application for hosting 32-bit plugins.
|
||||
See the readme for full instructions on how to use this.'''
|
||||
)
|
||||
|
||||
option(
|
||||
'use-winedbg',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description: 'Whether to run the Wien VST host with GDB attached. Might not always be reliable.'
|
||||
description : 'Whether to run the Wien VST host with GDB attached. Might not always be reliable.'
|
||||
)
|
||||
|
||||
@@ -33,7 +33,8 @@ template <typename B>
|
||||
using InputAdapter = bitsery::InputBufferAdapter<B>;
|
||||
|
||||
/**
|
||||
* Serialize an object using bitsery and write it to a socket.
|
||||
* Serialize an object using bitsery and write it to a socket. This will write
|
||||
* both the size of the serialized object and the object itself over the socket.
|
||||
*
|
||||
* @param socket The Boost.Asio socket to write to.
|
||||
* @param object The object to write to the stream.
|
||||
@@ -53,8 +54,13 @@ inline void write_object(
|
||||
|
||||
// Tell the other side how large the object is so it can prepare a buffer
|
||||
// large enough before sending the data
|
||||
// NOTE: We're writing these sizes as a 64 bit integers, **not** as pointer
|
||||
// sized integers. This is to provide compatibility with the 32-bit
|
||||
// bit bridge. This won't make any function difference aside from the
|
||||
// 32-bit host application having to convert between 64 and 32 bit
|
||||
// integers.
|
||||
boost::asio::write(socket,
|
||||
boost::asio::buffer(std::array<size_t, 1>{size}));
|
||||
boost::asio::buffer(std::array<uint64_t, 1>{size}));
|
||||
const size_t bytes_written =
|
||||
boost::asio::write(socket, boost::asio::buffer(buffer, size));
|
||||
assert(bytes_written == size);
|
||||
@@ -79,7 +85,8 @@ template <typename T, typename Socket>
|
||||
inline T& read_object(Socket& socket,
|
||||
T& object,
|
||||
std::vector<uint8_t> buffer = std::vector<uint8_t>(64)) {
|
||||
std::array<size_t, 1> message_length;
|
||||
// See the note above on the use of `uint64_t` instead of `size_t`
|
||||
std::array<uint64_t, 1> message_length;
|
||||
boost::asio::read(socket, boost::asio::buffer(message_length));
|
||||
|
||||
// Make sure the buffer is large enough
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* The name of the wine host name, e.g. `yabridge-host.exe` for the regular 64
|
||||
* bit build.
|
||||
*/
|
||||
constexpr char yabridge_wine_host_name[] = "@host_binary_64bit@";
|
||||
|
||||
/**
|
||||
* The name of the 32-bit wine host name, 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_wine_host_name_32bit[] = "@host_binary_32bit@";
|
||||
@@ -1,3 +1,16 @@
|
||||
# Contains constants determined while configuring the build. As an alternative
|
||||
# to preprocessor macros.
|
||||
config_header = configure_file(
|
||||
input : 'config.h.in',
|
||||
output : 'config.h',
|
||||
configuration : configuration_data(
|
||||
{
|
||||
'host_binary_32bit': host_name_32bit + '.exe',
|
||||
'host_binary_64bit': host_name_64bit + '.exe',
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# Generate a file containing the last annotated git tag and possibly also the
|
||||
# amount of commits since then and the hash of the last commit
|
||||
version_header = vcs_tag(
|
||||
|
||||
+5
-2
@@ -209,13 +209,16 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket,
|
||||
[&](const std::vector<uint8_t>& buffer) -> void* {
|
||||
return const_cast<uint8_t*>(buffer.data());
|
||||
},
|
||||
[&](size_t& window_handle) -> void* {
|
||||
[&](native_size_t& window_handle) -> void* {
|
||||
// This is the X11 window handle that the editor should
|
||||
// reparent itself to. We have a special wrapper around the
|
||||
// dispatch function that intercepts `effEditOpen` events
|
||||
// and creates a Win32 window and then finally embeds the
|
||||
// X11 window Wine created into this wnidow handle.
|
||||
return reinterpret_cast<void*>(window_handle);
|
||||
// Make sure to convert the window ID first to `size_t` in
|
||||
// case this is the 32-bit host.
|
||||
return reinterpret_cast<void*>(
|
||||
static_cast<size_t>(window_handle));
|
||||
},
|
||||
[&](const AEffect&) -> void* { return nullptr; },
|
||||
[&](DynamicVstEvents& events) -> void* {
|
||||
|
||||
@@ -176,7 +176,9 @@ void Logger::log_event(bool is_dispatch,
|
||||
[&](const std::vector<uint8_t>& buffer) {
|
||||
message << "<" << buffer.size() << " byte chunk>";
|
||||
},
|
||||
[&](const intptr_t&) { message << "<nullptr>"; },
|
||||
[&](const native_size_t& window_id) {
|
||||
message << "<window " << window_id << ">";
|
||||
},
|
||||
[&](const AEffect&) { message << "<nullptr>"; },
|
||||
[&](const DynamicVstEvents& events) {
|
||||
message << "<" << events.events.size() << " midi_events>";
|
||||
|
||||
@@ -57,6 +57,23 @@ constexpr size_t max_midi_events = max_buffer_size / sizeof(size_t);
|
||||
*/
|
||||
constexpr size_t binary_buffer_size = 50 << 20;
|
||||
|
||||
// The plugin should always be compiled to a 64-bit version, but the host
|
||||
// application can also be 32-bit to allow using 32-bit legacy Windows VST in a
|
||||
// modern Linux VST host. Because of this we have to make sure to always use
|
||||
// 64-bit integers in places where we would otherwise use `size_t` and
|
||||
// `intptr_t`. Otherwise the binary serialization would break. The 64 <-> 32 bit
|
||||
// conversion for the 32-bit host application won't cause any issues for us
|
||||
// since we can't directly pass pointers between the plugin and the host anyway.
|
||||
|
||||
#ifndef __WINE__
|
||||
// Sanity check for the plugin, both the 64 and 32 bit hosts should follow these
|
||||
// conventions
|
||||
static_assert(std::is_same_v<size_t, uint64_t>);
|
||||
static_assert(std::is_same_v<intptr_t, int64_t>);
|
||||
#endif
|
||||
using native_size_t = uint64_t;
|
||||
using native_intptr_t = int64_t;
|
||||
|
||||
// The cannonical overloading template for `std::visitor`, not sure why this
|
||||
// isn't part of the standard library
|
||||
template <class... Ts>
|
||||
@@ -248,7 +265,7 @@ struct WantsString {};
|
||||
using EventPayload = std::variant<std::nullptr_t,
|
||||
std::string,
|
||||
std::vector<uint8_t>,
|
||||
size_t,
|
||||
native_size_t,
|
||||
AEffect,
|
||||
DynamicVstEvents,
|
||||
WantsChunkBuffer,
|
||||
@@ -270,7 +287,9 @@ void serialize(S& s, EventPayload& payload) {
|
||||
[](S& s, std::vector<uint8_t>& buffer) {
|
||||
s.container1b(buffer, binary_buffer_size);
|
||||
},
|
||||
[](S& s, size_t& window_handle) { s.value8b(window_handle); },
|
||||
[](S& s, native_size_t& window_handle) {
|
||||
s.value8b(window_handle);
|
||||
},
|
||||
[](S& s, AEffect& effect) { s.object(effect); },
|
||||
[](S& s, DynamicVstEvents& events) {
|
||||
s.container(
|
||||
@@ -292,10 +311,7 @@ void serialize(S& s, EventPayload& payload) {
|
||||
struct Event {
|
||||
int opcode;
|
||||
int index;
|
||||
// TODO: This is an intptr_t, if we want to support 32 bit Wine plugins all
|
||||
// of these these intptr_t types should be replace by `uint64_t` to
|
||||
// remain compatible with the Linux VST plugin.
|
||||
intptr_t value;
|
||||
native_intptr_t value;
|
||||
float option;
|
||||
/**
|
||||
* The event dispatch function has a void pointer parameter that's often
|
||||
@@ -371,7 +387,7 @@ struct EventResult {
|
||||
/**
|
||||
* The result that should be returned from the dispatch function.
|
||||
*/
|
||||
intptr_t return_value;
|
||||
native_intptr_t return_value;
|
||||
/**
|
||||
* Events typically either just return their return value or write a string
|
||||
* into the void pointer, but sometimes an event response should forward
|
||||
|
||||
+93
-16
@@ -30,6 +30,7 @@
|
||||
#endif
|
||||
|
||||
// Generated inside of build directory
|
||||
#include <src/common/config/config.h>
|
||||
#include <src/common/config/version.h>
|
||||
|
||||
#include "../common/communication.h"
|
||||
@@ -40,11 +41,6 @@ namespace bp = boost::process;
|
||||
// boost::filesystem
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
/**
|
||||
* The name of the wine VST host binary.
|
||||
*/
|
||||
constexpr auto yabridge_wine_host_name = "yabridge-host.exe";
|
||||
|
||||
/**
|
||||
* Used for generating random identifiers.
|
||||
*/
|
||||
@@ -53,7 +49,8 @@ constexpr char alphanumeric_characters[] =
|
||||
|
||||
std::string create_logger_prefix(const fs::path& socket_path);
|
||||
fs::path find_vst_plugin();
|
||||
fs::path find_wine_vst_host();
|
||||
PluginArchitecture find_plugin_architecture(fs::path);
|
||||
fs::path find_wine_vst_host(PluginArchitecture plugin_arch);
|
||||
std::optional<fs::path> find_wineprefix();
|
||||
fs::path generate_endpoint_name();
|
||||
bp::environment set_wineprefix();
|
||||
@@ -74,8 +71,9 @@ HostBridge& get_bridge_instance(const AEffect& plugin) {
|
||||
}
|
||||
|
||||
HostBridge::HostBridge(audioMasterCallback host_callback)
|
||||
: vst_host_path(find_wine_vst_host()),
|
||||
vst_plugin_path(find_vst_plugin()),
|
||||
: vst_plugin_path(find_vst_plugin()),
|
||||
vst_plugin_arch(find_plugin_architecture(vst_plugin_path)),
|
||||
vst_host_path(find_wine_vst_host(vst_plugin_arch)),
|
||||
// All the fields should be zero initialized because
|
||||
// `Vst2PluginInstance::vstAudioMasterCallback` from Bitwig's plugin
|
||||
// bridge will crash otherwise
|
||||
@@ -130,6 +128,21 @@ HostBridge::HostBridge(audioMasterCallback host_callback)
|
||||
logger.log("wineprefix: '" +
|
||||
find_wineprefix().value_or("<default>").string() + "'");
|
||||
|
||||
// Include a list of enabled compile-tiem features, mostly to make debug
|
||||
// logs more useful
|
||||
logger.log("");
|
||||
logger.log("Enabled features:");
|
||||
#ifdef USE_BITBRIDGE
|
||||
logger.log("- bitbridge support");
|
||||
#endif
|
||||
#ifdef USE_WINEDBG
|
||||
logger.log("- winedbg");
|
||||
#endif
|
||||
#if !(defined(USE_BITBRIDGE) || defined(USE_WINEDBG))
|
||||
logger.log(" <none>");
|
||||
#endif
|
||||
logger.log("");
|
||||
|
||||
// It's very important that these sockets are connected to in the same
|
||||
// order in the Wine VST host
|
||||
socket_acceptor.accept(host_vst_dispatch);
|
||||
@@ -480,8 +493,8 @@ std::string create_logger_prefix(const fs::path& socket_path) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Wine VST hsot (named `yabridge-host.exe`). For this we will search
|
||||
* in two places:
|
||||
* Finds the Wine VST hsot (either `yabridge-host.exe` or `yabridge-host.exe`
|
||||
* depending on the plugin). 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
|
||||
@@ -489,23 +502,31 @@ std::string create_logger_prefix(const fs::path& socket_path) {
|
||||
* /usr.
|
||||
* 2. In the regular search path.
|
||||
*
|
||||
* @param plugin_arch The architecture of the plugin, either 64-bit or 32-bit.
|
||||
* Used to determine which host application to use, if available.
|
||||
*
|
||||
* @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 find_wine_vst_host(PluginArchitecture plugin_arch) {
|
||||
auto host_name = yabridge_wine_host_name;
|
||||
if (plugin_arch == PluginArchitecture::vst_32) {
|
||||
host_name = yabridge_wine_host_name_32bit;
|
||||
}
|
||||
|
||||
fs::path host_path =
|
||||
fs::canonical(boost::dll::this_line_location()).remove_filename() /
|
||||
yabridge_wine_host_name;
|
||||
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
|
||||
const fs::path vst_host_path = bp::search_path(yabridge_wine_host_name);
|
||||
const fs::path vst_host_path = bp::search_path(host_name);
|
||||
if (vst_host_path == "") {
|
||||
throw std::runtime_error("Could not locate '" +
|
||||
std::string(yabridge_wine_host_name) + "'");
|
||||
throw std::runtime_error("Could not locate '" + std::string(host_name) +
|
||||
"'");
|
||||
}
|
||||
|
||||
return vst_host_path;
|
||||
@@ -555,10 +576,66 @@ fs::path find_vst_plugin() {
|
||||
"VST plugin .dll file.");
|
||||
}
|
||||
|
||||
// Also resolve symlinks here, mostly for development purposes
|
||||
// Also resolve symlinks here
|
||||
return fs::canonical(plugin_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the architecture of a VST plugin (or rather, a .dll file) based on
|
||||
* it's header values.
|
||||
*
|
||||
* See https://docs.microsoft.com/en-us/windows/win32/debug/pe-format for more
|
||||
* information on the PE32 format.
|
||||
*
|
||||
* @param plugin_path The path to the .dll file we're going to check.
|
||||
*
|
||||
* @return The detected architecture.
|
||||
* @throw std::runtime_error If the file is not a .dll file.
|
||||
*/
|
||||
PluginArchitecture find_plugin_architecture(fs::path plugin_path) {
|
||||
std::ifstream file(plugin_path, std::ifstream::binary | std::ifstream::in);
|
||||
|
||||
// The linker will place the offset where the PE signature is placed at the
|
||||
// end of the MS-DOS stub, at this offset
|
||||
uint32_t pe_signature_offset;
|
||||
file.seekg(0x3c);
|
||||
file.read(reinterpret_cast<char*>(&pe_signature_offset),
|
||||
sizeof(pe_signature_offset));
|
||||
|
||||
// The PE32 signature will be followed by a magic number.
|
||||
// file >> pe_signature_offset;
|
||||
uint32_t pe_signature;
|
||||
uint16_t machine_type;
|
||||
file.seekg(pe_signature_offset);
|
||||
file.read(reinterpret_cast<char*>(&pe_signature), sizeof(pe_signature));
|
||||
file.read(reinterpret_cast<char*>(&machine_type), sizeof(machine_type));
|
||||
|
||||
constexpr char expected_pe_signature[4] = {'P', 'E', '\0', '\0'};
|
||||
if (pe_signature !=
|
||||
*reinterpret_cast<const uint32_t*>(expected_pe_signature)) {
|
||||
throw std::runtime_error("'" + plugin_path.string() +
|
||||
"' is not a valid .dll file");
|
||||
}
|
||||
|
||||
// These constants are specified in
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
|
||||
switch (machine_type) {
|
||||
case 0x014c: // IMAGE_FILE_MACHINE_I386
|
||||
return PluginArchitecture::vst_32;
|
||||
break;
|
||||
case 0x8664: // IMAGE_FILE_MACHINE_AMD64
|
||||
case 0x0000: // IMAGE_FILE_MACHINE_UNKNOWN
|
||||
return PluginArchitecture::vst_64;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error(
|
||||
"'" + plugin_path.string() +
|
||||
"' does not have a supported architecture: " +
|
||||
std::to_string(machine_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique name for the Unix domain socket endpoint based on the VST
|
||||
* plugin's name. This will also generate the parent directory if it does not
|
||||
|
||||
@@ -45,6 +45,12 @@ class patched_async_pipe : public boost::process::async_pipe {
|
||||
typedef typename handle_type::executor_type executor_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* A tag to differentiate between 32 and 64-bit plugins, used to determine which
|
||||
* host application to use.
|
||||
*/
|
||||
enum class PluginArchitecture { vst_32, vst_64 };
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -90,14 +96,20 @@ class HostBridge {
|
||||
float get_parameter(AEffect* plugin, int index);
|
||||
void set_parameter(AEffect* plugin, int index, float value);
|
||||
|
||||
/**
|
||||
* The path to `yabridge-host.exe`.
|
||||
*/
|
||||
const boost::filesystem::path vst_host_path;
|
||||
/**
|
||||
* The path to the .dll being loaded in the Wine VST host.
|
||||
*/
|
||||
const boost::filesystem::path vst_plugin_path;
|
||||
/**
|
||||
* Whether the plugin is 64-bit or 32-bit.
|
||||
*/
|
||||
const PluginArchitecture vst_plugin_arch;
|
||||
/**
|
||||
* The path to the host application (i.e. a path to either
|
||||
* `yabridge-host.exe` or `yabridge-host-32.exe`). The host application will
|
||||
* be chosen depending on the architecture of the VST plugin .dll file.
|
||||
*/
|
||||
const boost::filesystem::path vst_host_path;
|
||||
|
||||
/**
|
||||
* This AEffect struct will be populated using the data passed by the Wine
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <iostream>
|
||||
|
||||
// Generated inside of build directory
|
||||
#include <src/common/config/config.h>
|
||||
#include <src/common/config/version.h>
|
||||
|
||||
#include "plugin-bridge.h"
|
||||
@@ -26,9 +27,14 @@ int main(int argc, char* argv[]) {
|
||||
// socket to connect to in plugin/bridge.cpp as the first two arguments of
|
||||
// this process.
|
||||
if (argc < 3) {
|
||||
std::cerr
|
||||
<< "Usage: yabridge-host.exe <vst_plugin_dll> <unix_domain_socket>"
|
||||
<< std::endl;
|
||||
std::cerr << "Usage: "
|
||||
#ifdef __i386__
|
||||
<< yabridge_wine_host_name_32bit
|
||||
#else
|
||||
<< yabridge_wine_host_name
|
||||
#endif
|
||||
<< " <vst_plugin_dll> <unix_domain_socket>" << std::endl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -36,6 +42,9 @@ int main(int argc, char* argv[]) {
|
||||
const std::string socket_endpoint_path(argv[2]);
|
||||
|
||||
std::cerr << "Initializing yabridge host version " << yabridge_git_version
|
||||
#ifdef __i386__
|
||||
<< " (32-bit compatibility mode)"
|
||||
#endif
|
||||
<< std::endl;
|
||||
try {
|
||||
PluginBridge bridge(plugin_dll_path, socket_endpoint_path);
|
||||
|
||||
Reference in New Issue
Block a user