// yabridge: a Wine VST 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 . #pragma once #include #include #include #include "../../common/communication/vst2.h" #include "../../common/logging/vst2.h" #include "common.h" /** * This handles the communication between the Linux native VST2 plugin and the * Wine VST host. The functions below should be used as callback functions in an * `AEffect` object. * * The naming scheme of all of these 'bridge' classes is `{,Plugin}Bridge` * for greppability reasons. The `Plugin` infix is added on the native plugin * side. */ class Vst2PluginBridge : 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. */ Vst2PluginBridge(audioMasterCallback host_callback); /** * Terminate the Wine plugin host process and drop all work when the module * gets unloaded. */ ~Vst2PluginBridge() noexcept override; // 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); /** * This is the old, accumulative version of `processReplacing()`. As far as * I'm aware no host from the last 20 years will use this (since it's not * very practical), but we have to support this anyways. Because this is not * used, we'll just reuse your `process_replacing()` implementation (which * actually falls back to `process()` if the plugin somehow does not support * the former). */ void process(AEffect* plugin, float** inputs, float** outputs, int sample_frames); /** * 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); /** * The same as `Vst2PluginBridge::process_replacing`, but for double * precision audio. Support for this on both the plugin and host side is * pretty rare, but REAPER supports it. This reuses the same infrastructure * as `process_replacing` is using since the host will only call one or the * other. */ void process_double_replacing(AEffect* plugin, double** inputs, double** outputs, int sample_frames); float get_parameter(AEffect* plugin, int index); void set_parameter(AEffect* plugin, int index, float value); /** * Process audio and handle plugin-generated MIDI events afterwards. * * @tparam T The sample type. Should be either `float` for single preceision * audio processing called through `processReplacing`, or `double` for * double precision audio through `processDoubleReplacing`. * @tparam replacing Whether or not `outputs` should be replaced by the new * processed audio. This is the normal behaviour for `processReplacing()`. * If this is set to `false` then the results are added to the existing * values in `outputs`. No host will use this last behaviour anymore, but * it's part of the VST2.4 spec so we have to support it. * * @see Vst2PluginBridge::process_replacing * @see Vst2PluginBridge::process_double_replacing */ template void do_process(T** inputs, T** outputs, int sample_frames); /** * 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: /** * The thread that handles host callbacks. */ std::jthread host_callback_handler; /** * A mutex to prevent multiple simultaneous calls to `getParameter()` and * `setParameter()`. This likely won't happen, but better safe than sorry. * For `dispatch()` and `audioMaster()` there's some more complex logic for * this in `Vst2EventHandler`. */ 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. Wraps around * `PluginBridge::generic_logger`. */ Vst2Logger logger; /** * A shared memory object that contains both the input and output audio * buffers. This is first configured on the Wine plugin host side during * `effMainsChanged` and then replicated on the plugin side. This way we * reduce the amount of copying during audio processing to only two copies. * We'll write the input audio to this buffer and send the process request * to the Wine plugin host. There the Windows VST2 plugin will then read * from the buffer and write its results to the same buffer. We can then * write those results back to the host. * * This will be a nullopt until `effMainsChanged` has been called. */ std::optional process_buffers; /** * We'll periodically synchronize the Wine host's audio thread priority with * that of the host. Since the overhead from doing so does add up, we'll * only do this every once in a while. */ time_t last_audio_thread_priority_synchronization = 0; /** * 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 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 host on the audio thread at the end of * `process_replacing()`. */ boost::container::small_vector 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; /** * REAPER requires us to call `audioMasterSizeWidnow()` from the same thread * that's calling `effEditIdle()`. If we call this from any other thread, * then the FX window won't be resized. To accommodate for this, we'll store * the width and the height passed to the last call to * `audioMasterSizeWindow`. If this contains a value, we'll then call * `audioMasterSizeWindow()` with the new size during `effEditIdle()`. */ std::optional> incoming_resize; std::mutex incoming_resize_mutex; };