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