// yabridge: a Wine VST bridge // Copyright (C) 2020-2021 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 "../bitsery/ext/message-reference.h" #include "../configuration.h" #include "../utils.h" #include "common.h" #include "vst3/component-handler-proxy.h" #include "vst3/connection-point-proxy.h" #include "vst3/context-menu-proxy.h" #include "vst3/context-menu-target.h" #include "vst3/host-context-proxy.h" #include "vst3/plug-frame-proxy.h" #include "vst3/plug-view-proxy.h" #include "vst3/plugin-factory-proxy.h" #include "vst3/plugin-proxy.h" // Event handling for our VST3 plugins works slightly different from how we // handle VST2 plugins. VST3 does not have a centralized event dispatching // interface like VST2 does, and it uses a bunch of separate interfaces instead. // Instead of having an a single event/result with accompanying payload values // for both host -> plugin `dispatcher()` and plugin -> host `audioMaster()` // calls, we'll send objects of type `T` that should receive a response of type // `T::Response`, where all of the possible `T`s are stored in an // `std::variant`. This way we communicate in a completely type safe way. // TODO: If this approach works, maybe we can also refactor the VST2 handling to // do this since it's a bit safer and easier to read // All messages for creating objects and calling interfaces on them are defined // as part of the interfaces and implementations in `vst3/` /** * Marker struct to indicate the other side (the plugin) should send a copy of * the configuration. */ struct WantsConfiguration { using Response = Configuration; template void serialize(S&) {} }; /** * When we send a control message from the plugin to the Wine VST host, this * encodes the information we request or the operation we want to perform. A * request of type `ControlRequest(T)` should send back a `T::Response`. */ using ControlRequest = std::variant; template void serialize(S& s, ControlRequest& payload) { // All of the objects in `ControlRequest` should have their own // serialization function. s.ext(payload, bitsery::ext::StdVariant{}); } /** * A subset of all functions a host can call on a plugin. These functions are * called from a hot loop every processing cycle, so we want a dedicated socket * for these for every plugin instance. * * We use a separate struct for this so we can keep the * `YaAudioProcessor::Process` object, which also contains the entire audio * processing data struct, alive as a thread local static object on the Wine * side, and as a regular field in `Vst3PluginProxyImpl` on the plugin side. In * our variant we then store a `MessageReference` that points to this object, * and we'll do some magic to be able to serialize and deserialize this object * without needing to create copies. See `MessageReference` and * `bitsery::ext::MessageReference` for more information. */ struct AudioProcessorRequest { AudioProcessorRequest() {} /** * Initialize the variant with an object. In `Vst3Sockets::send_message()` * the object gets implicitly converted to the this variant. */ template AudioProcessorRequest(T request) : payload(std::move(request)) {} using Payload = std::variant, YaAudioProcessor::GetTailSamples, YaComponent::GetControllerClassId, YaComponent::SetIoMode, YaComponent::GetBusCount, YaComponent::GetBusInfo, YaComponent::GetRoutingInfo, YaComponent::ActivateBus, YaComponent::SetActive, YaPrefetchableSupport::GetPrefetchableSupport>; Payload payload; template void serialize(S& s) { s.ext( payload, bitsery::ext::StdVariant{ [&](S& s, MessageReference& request_ref) { // When serializing this reference we'll read the data // directly from the referred to object. During // deserializing we'll deserialize into the persistent and // thread local `process_request` object (see // `Vst3Sockets::add_audio_processor_and_listen`) and then // reassign the reference to point to that boject. s.ext(request_ref, bitsery::ext::MessageReference(process_request)); }, [](S& s, auto& request) { s.object(request); }}); } /** * Used for deserializing the `MessageReference` * variant. When we encounter this variant, we'll actually deserialize the * object into this object, and we'll then reassign the reference to point * to this object. That way we can keep it around as a thread local object * to prevent unnecessary allocations. */ std::optional process_request; }; /** * When we do a callback from the Wine VST host to the plugin, this encodes the * information we want or the operation we want to perform. A request of type * `CallbackRequest(T)` should send back a `T::Response`. */ using CallbackRequest = std::variant; template void serialize(S& s, CallbackRequest& payload) { // All of the objects in `CallbackRequest` should have their own // serialization function. s.ext(payload, bitsery::ext::StdVariant{}); } /** * Get the actual variant for a request. We need a function for this to be able * to handle composite types, like `AudioProcessorRequest` that use * `MesasgeReference` to be able to store persistent objects in the message * variant. */ template std::variant& get_request_variant(std::variant& request) { return request; } /** * Fetch the `std::variant<>` from an audio processor request object. This will * let us use our regular, simple function call dispatch code, but we can still * store the process data in a separate field (to reduce allocations). * * @overload */ inline AudioProcessorRequest::Payload& get_request_variant( AudioProcessorRequest& request) { return request.payload; }