Files
yabridge/src/plugin/plugin-bridge.h
T
Robbert van der Helm b379708b21 Encapsulate individual/group handling differences
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.
2020-05-29 18:08:44 +02:00

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;
};