mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-08 04:20:13 +02:00
b379708b21
This cleans up the PluginBridge significantly by getting rid of all fields and handling that was only needed for connecting to plugin groups. This was also the last thing I wanted to refactor before releasing the plugin groups feature with yabridge 1.2.
225 lines
7.8 KiB
C++
225 lines
7.8 KiB
C++
// 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
|
|
|
|
#include <vestige/aeffectx.h>
|
|
|
|
#include <boost/asio/io_context.hpp>
|
|
#include <boost/asio/local/stream_protocol.hpp>
|
|
#include <mutex>
|
|
#include <thread>
|
|
|
|
#include "../common/logging.h"
|
|
#include "configuration.h"
|
|
#include "host-process.h"
|
|
|
|
/**
|
|
* 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
|
|
* `AEffect` object.
|
|
*/
|
|
class PluginBridge {
|
|
public:
|
|
/**
|
|
* Initializes the Wine VST bridge. This sets up the sockets for event
|
|
* handling.
|
|
*
|
|
* @param host_callback The callback function passed to the VST plugin by
|
|
* the host.
|
|
*
|
|
* @throw std::runtime_error Thrown when the VST host could not be found, or
|
|
* if it could not locate and load a VST .dll file.
|
|
*/
|
|
PluginBridge(audioMasterCallback host_callback);
|
|
|
|
// The four below functions are the handlers from the VST2 API. They are
|
|
// called through proxy functions in `plugin.cpp`.
|
|
|
|
/**
|
|
* Handle an event sent by the VST host. Most of these opcodes will be
|
|
* passed through to the winelib VST host.
|
|
*/
|
|
intptr_t dispatch(AEffect* plugin,
|
|
int opcode,
|
|
int index,
|
|
intptr_t value,
|
|
void* data,
|
|
float option);
|
|
/**
|
|
* Ask the VST plugin to process audio for us. If the plugin somehow does
|
|
* not support `processReplacing()` and only supports the old `process()`
|
|
* function, then this will be handled implicitely in
|
|
* `Vst2Bridge::handle_process_replacing()`.
|
|
*/
|
|
void process_replacing(AEffect* plugin,
|
|
float** inputs,
|
|
float** outputs,
|
|
int sample_frames);
|
|
float get_parameter(AEffect* plugin, int index);
|
|
void set_parameter(AEffect* plugin, int index, float value);
|
|
|
|
/**
|
|
* The configuration for this instance of yabridge. Set based on the values
|
|
* from a `yabridge.toml`, if it exists.
|
|
*
|
|
* @see Configuration::load_for
|
|
*/
|
|
Configuration config;
|
|
|
|
/**
|
|
* The path to the .dll being loaded in the Wine VST host.
|
|
*/
|
|
const boost::filesystem::path vst_plugin_path;
|
|
|
|
/**
|
|
* This AEffect struct will be populated using the data passed by the Wine
|
|
* VST host during initialization and then passed as a pointer to the Linux
|
|
* native VST host from the Linux VST plugin's entry point.
|
|
*/
|
|
AEffect plugin;
|
|
|
|
private:
|
|
/**
|
|
* Format and log all relevant debug information during initialization.
|
|
*/
|
|
void log_init_message();
|
|
|
|
boost::asio::io_context io_context;
|
|
boost::asio::local::stream_protocol::endpoint socket_endpoint;
|
|
boost::asio::local::stream_protocol::acceptor socket_acceptor;
|
|
|
|
// The naming convention for these sockets is `<from>_<to>_<event>`. 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).
|
|
|
|
/**
|
|
* The socket that forwards all `dispatcher()` calls from the VST host to
|
|
* the plugin. This is also used once at startup to populate the values of
|
|
* the `AEffect` object.
|
|
*/
|
|
boost::asio::local::stream_protocol::socket host_vst_dispatch;
|
|
/**
|
|
* Used specifically for the `effProcessEvents` opcode. This is needed
|
|
* because the Win32 API is designed to block during certain GUI
|
|
* interactions such as resizing a window or opening a dropdown. Without
|
|
* this MIDI input would just stop working at times.
|
|
*/
|
|
boost::asio::local::stream_protocol::socket host_vst_dispatch_midi_events;
|
|
boost::asio::local::stream_protocol::socket vst_host_callback;
|
|
/**
|
|
* Used for both `getParameter` and `setParameter` since they mostly
|
|
* overlap.
|
|
*/
|
|
boost::asio::local::stream_protocol::socket host_vst_parameters;
|
|
boost::asio::local::stream_protocol::socket host_vst_process_replacing;
|
|
|
|
/**
|
|
* Whether we're done accepting sockets. The plugin may hang indefinitely if
|
|
* the Wine process fails to start, since then nothing will connect to our
|
|
* sockets. While we're waiting for our sockets we'll periodically poll the
|
|
* Wine process to see if it's still running, and terminate the socket
|
|
* accepting if it is not.
|
|
*/
|
|
std::atomic_bool finished_accepting_sockets;
|
|
|
|
/**
|
|
* The thread that handles host callbacks.
|
|
*/
|
|
std::thread host_callback_handler;
|
|
|
|
/**
|
|
* A binary semaphore to prevent race conditions from the dispatch function
|
|
* being called by two threads at once. See `send_event()` for more
|
|
* information.
|
|
*/
|
|
std::mutex dispatch_mutex;
|
|
std::mutex dispatch_midi_events_mutex;
|
|
/**
|
|
* A similar semaphore as the `dispatch_*` semaphores in the rare case that
|
|
* `getParameter()` and `setParameter()` are being called at the same time
|
|
* since they use the same socket.
|
|
*/
|
|
std::mutex parameters_mutex;
|
|
|
|
/**
|
|
* The callback function passed by the host to the VST plugin instance.
|
|
*/
|
|
audioMasterCallback host_callback_function;
|
|
|
|
/**
|
|
* The logging facility used for this instance of yabridge. See
|
|
* `Logger::create_from_env()` for how this is configured.
|
|
*
|
|
* @see Logger::create_from_env
|
|
*/
|
|
Logger logger;
|
|
|
|
/**
|
|
* The version of Wine currently in use. Used in the debug output on plugin
|
|
* startup.
|
|
*/
|
|
const std::string wine_version;
|
|
|
|
/**
|
|
* The Wine process hosting the Windows VST plugin.
|
|
*
|
|
* @see launch_vst_host
|
|
*/
|
|
std::unique_ptr<HostProcess> vst_host;
|
|
/**
|
|
* Runs the Boost.Asio `io_context` thread for logging the Wine process
|
|
* STDOUT and STDERR messages.
|
|
*/
|
|
std::thread wine_io_handler;
|
|
|
|
/**
|
|
* A scratch buffer for sending and receiving data during `process` and
|
|
* `processReplacing` calls.
|
|
*/
|
|
std::vector<uint8_t> process_buffer;
|
|
|
|
/**
|
|
* The VST host can query a plugin for arbitrary binary data such as
|
|
* presets. It will expect the plugin to write back a pointer that points to
|
|
* that data. This vector is where we store the chunk data for the last
|
|
* `effGetChunk` event.
|
|
*/
|
|
std::vector<uint8_t> chunk_data;
|
|
/**
|
|
* The VST host will expect to be returned a pointer to a struct that stores
|
|
* the dimensions of the editor window.
|
|
*/
|
|
VstRect editor_rectangle;
|
|
|
|
/**
|
|
* Sending MIDI events sent to the host by the plugin using
|
|
* `audioMasterProcessEvents` function has to be done during the processing
|
|
* function. If they are sent during any other time or from another thread,
|
|
* then the host will just discard them. Because we're receiving our host
|
|
* callbacks on a separate thread, we have to temporarily store any events
|
|
* we receive so we can send them to the host at the end of
|
|
* `process_replacing()`.
|
|
*/
|
|
std::vector<DynamicVstEvents> incoming_midi_events;
|
|
/**
|
|
* Mutex for locking the above event queue, since recieving and processing
|
|
* now happens in two different threads.
|
|
*/
|
|
std::mutex incoming_midi_events_mutex;
|
|
};
|