diff --git a/meson.build b/meson.build index 90ce16a3..ef56acd9 100644 --- a/meson.build +++ b/meson.build @@ -77,6 +77,7 @@ vst3_plugin_sources = [ 'src/common/communication/common.cpp', 'src/common/logging/common.cpp', 'src/common/logging/vst3.cpp', + 'src/common/serialization/vst3/audio-processor.cpp', 'src/common/serialization/vst3/base.cpp', 'src/common/serialization/vst3/component.cpp', 'src/common/serialization/vst3/event-list.cpp', @@ -117,6 +118,7 @@ host_sources = [ if with_vst3 host_sources += [ 'src/common/logging/vst3.cpp', + 'src/common/serialization/vst3/audio-processor.cpp', 'src/common/serialization/vst3/base.cpp', 'src/common/serialization/vst3/component.cpp', 'src/common/serialization/vst3/event-list.cpp', diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index 26bd558d..18229a1f 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -36,6 +36,82 @@ void Vst3Logger::log_unknown_interface( } } +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::SetBusArrangements& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::setBusArrangements(inputs = [SpeakerArrangement; " + << request.inputs.size() << "], numIns = " << request.num_ins + << ", outputs = [SpeakerArrangement; " << request.outputs.size() + << "], numOuts = " << request.num_outs << ")"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetBusArrangement& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::getBusArrangement(dir = " << request.dir + << ", index = " << request.index << ", &arr)"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::CanProcessSampleSize& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::canProcessSampleSize(symbolicSampleSize = " + << request.symbolic_sample_size << ")"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetLatencySamples& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::getLatencySamples()"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::SetupProcessing& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::setupProcessing(setup = )"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::SetProcessing& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::setProcessing(state = " + << (request.state ? "true" : "false") << ")"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::Process& request) { + // TODO: Only log this on log level 2 + log_request_base(is_host_vst, [&](auto& message) { + // TODO: Log about the process data + message << "::process(TODO)"; + }); +} + +void Vst3Logger::log_request(bool is_host_vst, + const YaComponent::GetTailSamples& request) { + log_request_base(is_host_vst, [&](auto& message) { + message << "::getTailSamples()"; + }); +} + void Vst3Logger::log_request(bool is_host_vst, const YaComponent::Construct&) { log_request_base(is_host_vst, [&](auto& message) { // TODO: Log the CID on verbosity level 2, and then also report all CIDs @@ -130,82 +206,6 @@ void Vst3Logger::log_request(bool is_host_vst, }); } -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::SetBusArrangements& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::setBusArrangements(inputs = [SpeakerArrangement; " - << request.inputs.size() << "], numIns = " << request.num_ins - << ", outputs = [SpeakerArrangement; " << request.outputs.size() - << "], numOuts = " << request.num_outs << ")"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetBusArrangement& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::getBusArrangement(dir = " << request.dir - << ", index = " << request.index << ", &arr)"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::CanProcessSampleSize& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::canProcessSampleSize(symbolicSampleSize = " - << request.symbolic_sample_size << ")"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetLatencySamples& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::getLatencySamples()"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::SetupProcessing& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::setupProcessing(setup = )"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::SetProcessing& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::setProcessing(state = " - << (request.state ? "true" : "false") << ")"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::Process& request) { - // TODO: Only log this on log level 2 - log_request_base(is_host_vst, [&](auto& message) { - // TODO: Log about the process data - message << "::process(TODO)"; - }); -} - -void Vst3Logger::log_request(bool is_host_vst, - const YaComponent::GetTailSamples& request) { - log_request_base(is_host_vst, [&](auto& message) { - message << "::getTailSamples()"; - }); -} - void Vst3Logger::log_request(bool is_host_vst, const YaPluginBase::Initialize& request) { log_request_base(is_host_vst, [&](auto& message) { @@ -246,6 +246,28 @@ void Vst3Logger::log_request(bool is_host_vst, const WantsConfiguration&) { }); } +void Vst3Logger::log_response( + bool is_host_vst, + const YaAudioProcessor::GetBusArrangementResponse& response) { + log_response_base(is_host_vst, [&](auto& message) { + message << response.result.string(); + if (response.result == Steinberg::kResultOk) { + message << ", "; + } + }); +} + +void Vst3Logger::log_response( + bool is_host_vst, + const YaAudioProcessor::ProcessResponse& response) { + // TODO: Only log this on verbosity level 2 + log_response_base(is_host_vst, [&](auto& message) { + message << response.result.string(); + + // TODO: Log response + }); +} + void Vst3Logger::log_response(bool is_host_vst, const Ack&) { log_response_base(is_host_vst, [&](auto& message) { message << "ACK"; }); } @@ -302,27 +324,6 @@ void Vst3Logger::log_response(bool is_host_vst, }); } -void Vst3Logger::log_response( - bool is_host_vst, - const YaComponent::GetBusArrangementResponse& response) { - log_response_base(is_host_vst, [&](auto& message) { - message << response.result.string(); - if (response.result == Steinberg::kResultOk) { - message << ", "; - } - }); -} - -void Vst3Logger::log_response(bool is_host_vst, - const YaComponent::ProcessResponse& response) { - // TODO: Only log this on verbosity level 2 - log_response_base(is_host_vst, [&](auto& message) { - message << response.result.string(); - - // TODO: Log response - }); -} - void Vst3Logger::log_response(bool is_host_vst, const YaPluginFactory::ConstructArgs& args) { log_response_base(is_host_vst, [&](auto& message) { diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h index 67912a6d..53e92a65 100644 --- a/src/common/logging/vst3.h +++ b/src/common/logging/vst3.h @@ -56,6 +56,19 @@ class Vst3Logger { // flag here indicates whether the request was initiated on the host side // (what we'll call a control message). + void log_request(bool is_host_vst, + const YaAudioProcessor::SetBusArrangements&); + void log_request(bool is_host_vst, + const YaAudioProcessor::GetBusArrangement&); + void log_request(bool is_host_vst, + const YaAudioProcessor::CanProcessSampleSize&); + void log_request(bool is_host_vst, + const YaAudioProcessor::GetLatencySamples&); + void log_request(bool is_host_vst, + const YaAudioProcessor::SetupProcessing&); + void log_request(bool is_host_vst, const YaAudioProcessor::SetProcessing&); + void log_request(bool is_host_vst, const YaAudioProcessor::Process&); + void log_request(bool is_host_vst, const YaAudioProcessor::GetTailSamples&); void log_request(bool is_host_vst, const YaComponent::Construct&); void log_request(bool is_host_vst, const YaComponent::Destruct&); void log_request(bool is_host_vst, const YaComponent::SetIoMode&); @@ -66,15 +79,6 @@ class Vst3Logger { void log_request(bool is_host_vst, const YaComponent::SetActive&); void log_request(bool is_host_vst, const YaComponent::SetState&); void log_request(bool is_host_vst, const YaComponent::GetState&); - void log_request(bool is_host_vst, const YaComponent::SetBusArrangements&); - void log_request(bool is_host_vst, const YaComponent::GetBusArrangement&); - void log_request(bool is_host_vst, - const YaComponent::CanProcessSampleSize&); - void log_request(bool is_host_vst, const YaComponent::GetLatencySamples&); - void log_request(bool is_host_vst, const YaComponent::SetupProcessing&); - void log_request(bool is_host_vst, const YaComponent::SetProcessing&); - void log_request(bool is_host_vst, const YaComponent::Process&); - void log_request(bool is_host_vst, const YaComponent::GetTailSamples&); void log_request(bool is_host_vst, const YaPluginBase::Initialize&); void log_request(bool is_host_vst, const YaPluginBase::Terminate&); void log_request(bool is_host_vst, const YaPluginFactory::Construct&); @@ -82,6 +86,10 @@ class Vst3Logger { void log_request(bool is_host_vst, const WantsConfiguration&); void log_response(bool is_host_vst, const Ack&); + void log_response(bool is_host_vst, + const YaAudioProcessor::GetBusArrangementResponse&); + void log_response(bool is_host_vst, + const YaAudioProcessor::ProcessResponse&); void log_response( bool is_host_vst, const std::variant&); @@ -89,9 +97,6 @@ class Vst3Logger { void log_response(bool is_host_vst, const YaComponent::GetRoutingInfoResponse&); void log_response(bool is_host_vst, const YaComponent::GetStateResponse&); - void log_response(bool is_host_vst, - const YaComponent::GetBusArrangementResponse&); - void log_response(bool is_host_vst, const YaComponent::ProcessResponse&); void log_response(bool is_host_vst, const YaPluginFactory::ConstructArgs&); void log_response(bool is_host_vst, const Configuration&); diff --git a/src/common/serialization/vst3/README.md b/src/common/serialization/vst3/README.md index da95009c..fcc6531d 100644 --- a/src/common/serialization/vst3/README.md +++ b/src/common/serialization/vst3/README.md @@ -9,9 +9,10 @@ VST3 interfaces are implemented as follows: | Yabridge class | Included in | Interfaces | | ------------------- | ------------- | ------------------------------------------------------ | -| `YaComponent` | | `IComponent`, `IAudioProcessor` | -| `YaHostApplication` | | `iHostAPplication` | +| `YaComponent` | | `IComponent` | +| `YaAudioProcessor` | `YaComponent` | `IAudioProcessor` | | `YaPluginBase` | `YaComponent` | `IPluginBase` | +| `YaHostApplication` | | `iHostAPplication` | | `YaPluginFactory` | | `IPluginFactory`, `IPluginFactory2`, `IPluginFactory3` | The following interfaces are implemented purely fur serialization purposes: diff --git a/src/common/serialization/vst3/audio-processor.cpp b/src/common/serialization/vst3/audio-processor.cpp new file mode 100644 index 00000000..3bcb3c9d --- /dev/null +++ b/src/common/serialization/vst3/audio-processor.cpp @@ -0,0 +1,27 @@ +// 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 . + +#include "audio-processor.h" + +YaAudioProcessor::ConstructArgs::ConstructArgs() {} + +YaAudioProcessor::ConstructArgs::ConstructArgs( + Steinberg::IPtr component) + : supported( + Steinberg::FUnknownPtr(component)) {} + +YaAudioProcessor::YaAudioProcessor(const ConstructArgs&& args) + : arguments(std::move(args)) {} diff --git a/src/common/serialization/vst3/audio-processor.h b/src/common/serialization/vst3/audio-processor.h new file mode 100644 index 00000000..140c014a --- /dev/null +++ b/src/common/serialization/vst3/audio-processor.h @@ -0,0 +1,314 @@ +// 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 . + +#pragma once + +#include +#include + +#include "../common.h" +#include "base.h" +#include "host-application.h" +#include "process-data.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" + +/** + * Wraps around `IAudioProcessor` for serialization purposes. This is + * instantiated as part of `YaComponent`. + */ +class YaAudioProcessor : public Steinberg::Vst::IAudioProcessor { + public: + /** + * These are the arguments for creating a `YaAudioProcessor`. + */ + struct ConstructArgs { + ConstructArgs(); + + /** + * Check whether an existing implementation implements `IPluginBase` and + * read arguments from it. + */ + ConstructArgs(Steinberg::IPtr object); + + /** + * Whether the object supported this interface. + */ + bool supported; + + template + void serialize(S& s) { + s.value1b(supported); + } + }; + + /** + * Instantiate this instance with arguments read from another interface + * implementation. + */ + YaAudioProcessor(const ConstructArgs&& args); + + inline bool supported() { return arguments.supported; } + + /** + * Message to pass through a call to + * `IAudioProcessor::setBusArrangements(inputs, num_ins, outputs, num_outs)` + * to the Wine plugin host. + */ + struct SetBusArrangements { + using Response = UniversalTResult; + + native_size_t instance_id; + + // These are orginally C-style heap arrays, not normal pointers + std::vector inputs; + int32 num_ins; + std::vector outputs; + int32 num_outs; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.container8b(inputs, max_num_speakers); + s.value4b(num_ins); + s.container8b(outputs, max_num_speakers); + s.value4b(num_outs); + } + }; + + virtual tresult PLUGIN_API + setBusArrangements(Steinberg::Vst::SpeakerArrangement* inputs, + int32 numIns, + Steinberg::Vst::SpeakerArrangement* outputs, + int32 numOuts) override = 0; + + /** + * The response code and written state for a call to + * `IAudioProcessor::getBusArrangement(dir, index, arr)`. + */ + struct GetBusArrangementResponse { + UniversalTResult result; + Steinberg::Vst::SpeakerArrangement updated_arr; + + template + void serialize(S& s) { + s.object(result); + s.value8b(updated_arr); + } + }; + + /** + * Message to pass through a call to + * `IAudioProcessor::getBusArrangement(dir, index, arr)` to the Wine + * plugin host. + */ + struct GetBusArrangement { + using Response = GetBusArrangementResponse; + + native_size_t instance_id; + + Steinberg::Vst::BusDirection dir; + int32 index; + Steinberg::Vst::SpeakerArrangement arr; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.value4b(dir); + s.value4b(index); + s.value8b(arr); + } + }; + + virtual tresult PLUGIN_API + getBusArrangement(Steinberg::Vst::BusDirection dir, + int32 index, + Steinberg::Vst::SpeakerArrangement& arr) override = 0; + + /** + * Message to pass through a call to + * `IAudioProcessor::canProcessSampleSize(symbolic_sample_size)` to the Wine + * plugin host. + */ + struct CanProcessSampleSize { + using Response = UniversalTResult; + + native_size_t instance_id; + + int32 symbolic_sample_size; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.value4b(symbolic_sample_size); + } + }; + + virtual tresult PLUGIN_API + canProcessSampleSize(int32 symbolicSampleSize) override = 0; + + /** + * Message to pass through a call to `IAudioProcessor::getLatencySamples()` + * to the Wine plugin host. + */ + struct GetLatencySamples { + using Response = PrimitiveWrapper; + + native_size_t instance_id; + + template + void serialize(S& s) { + s.value8b(instance_id); + } + }; + + virtual uint32 PLUGIN_API getLatencySamples() override = 0; + + /** + * Message to pass through a call to + * `IAudioProcessor::setupProcessing(setup)` to the Wine plugin host. + */ + struct SetupProcessing { + using Response = UniversalTResult; + + native_size_t instance_id; + + Steinberg::Vst::ProcessSetup setup; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.object(setup); + } + }; + + virtual tresult PLUGIN_API + setupProcessing(Steinberg::Vst::ProcessSetup& setup) override = 0; + + /** + * Message to pass through a call to `IAudioProcessor::setProcessing(state)` + * to the Wine plugin host. + */ + struct SetProcessing { + using Response = UniversalTResult; + + native_size_t instance_id; + + TBool state; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.value1b(state); + } + }; + + virtual tresult PLUGIN_API setProcessing(TBool state) override = 0; + + /** + * The response code and all the output data resulting from a call to + * `IAudioProcessor::process(data)`. + */ + struct ProcessResponse { + UniversalTResult result; + YaProcessDataResponse output_data; + + template + void serialize(S& s) { + s.object(result); + s.object(output_data); + } + }; + + /** + * Message to pass through a call to `IAudioProcessor::process(data)` to the + * Wine plugin host. This `YaProcessData` object wraps around all input + * audio buffers, parameter changes and events along with all context data + * provided by the host so we can send it to the Wine plugin host. We can + * then use `YaProcessData::get()` on the Wine plugin host side to + * reconstruct the original `ProcessData` object, and we then finally use + * `YaProcessData::move_outputs_to_response()` to create a response object + * that we can write back to the `ProcessData` object provided by the host. + */ + struct Process { + using Response = ProcessResponse; + + native_size_t instance_id; + + YaProcessData data; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.object(data); + } + }; + + virtual tresult PLUGIN_API + process(Steinberg::Vst::ProcessData& data) override = 0; + + /** + * Message to pass through a call to `IAudioProcessor::getTailSamples()` + * to the Wine plugin host. + */ + struct GetTailSamples { + using Response = PrimitiveWrapper; + + native_size_t instance_id; + + template + void serialize(S& s) { + s.value8b(instance_id); + } + }; + + virtual uint32 PLUGIN_API getTailSamples() override = 0; + + protected: + ConstructArgs arguments; +}; + +#pragma GCC diagnostic pop + +namespace Steinberg { +namespace Vst { +template +void serialize(S& s, Steinberg::Vst::BusInfo& info) { + s.value4b(info.mediaType); + s.value4b(info.direction); + s.value4b(info.channelCount); + s.container2b(info.name); + s.value4b(info.busType); + s.value4b(info.flags); +} + +template +void serialize(S& s, Steinberg::Vst::RoutingInfo& info) { + s.value4b(info.mediaType); + s.value4b(info.busIndex); + s.value4b(info.channel); +} + +template +void serialize(S& s, Steinberg::Vst::ProcessSetup& setup) { + s.value4b(setup.processMode); + s.value4b(setup.symbolicSampleSize); + s.value4b(setup.maxSamplesPerBlock); + s.value8b(setup.sampleRate); +} +} // namespace Vst +} // namespace Steinberg diff --git a/src/common/serialization/vst3/component.cpp b/src/common/serialization/vst3/component.cpp index 754168ab..1ae2c548 100644 --- a/src/common/serialization/vst3/component.cpp +++ b/src/common/serialization/vst3/component.cpp @@ -22,8 +22,8 @@ YaComponent::ConstructArgs::ConstructArgs( Steinberg::IPtr component, size_t instance_id) : instance_id(instance_id), - audio_processor_supported( - Steinberg::FUnknownPtr(component)) { + audio_processor_args(component), + plugin_base_args(component) { // `IComponent::getControllerClassId` Steinberg::TUID cid; if (component->getControllerClassId(cid) == Steinberg::kResultOk) { @@ -32,7 +32,8 @@ YaComponent::ConstructArgs::ConstructArgs( } YaComponent::YaComponent(const ConstructArgs&& args) - : YaPluginBase(std::move(args.plugin_base_args)), + : YaAudioProcessor(std::move(args.audio_processor_args)), + YaPluginBase(std::move(args.plugin_base_args)), arguments(std::move(args)){FUNKNOWN_CTOR} YaComponent::~YaComponent() { @@ -61,7 +62,7 @@ tresult PLUGIN_API YaComponent::queryInterface(Steinberg::FIDString _iid, } QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IComponent::iid, Steinberg::Vst::IComponent) - if (arguments.audio_processor_supported) { + if (YaAudioProcessor::supported()) { QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IAudioProcessor::iid, Steinberg::Vst::IAudioProcessor) } diff --git a/src/common/serialization/vst3/component.h b/src/common/serialization/vst3/component.h index 3899a4fd..09c4f714 100644 --- a/src/common/serialization/vst3/component.h +++ b/src/common/serialization/vst3/component.h @@ -25,15 +25,14 @@ #include #include #include -#include #include #include "../../bitsery/ext/vst3.h" #include "../common.h" +#include "audio-processor.h" #include "base.h" #include "host-application.h" #include "plugin-base.h" -#include "process-data.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" @@ -55,8 +54,8 @@ * the SDK's `AudioEffect` component. */ class YaComponent : public Steinberg::Vst::IComponent, - public YaPluginBase, - public Steinberg::Vst::IAudioProcessor { + public YaAudioProcessor, + public YaPluginBase { public: /** * These are the arguments for creating a `YaComponentPluginImpl`. @@ -77,11 +76,9 @@ class YaComponent : public Steinberg::Vst::IComponent, */ native_size_t instance_id; + YaAudioProcessor::ConstructArgs audio_processor_args; YaPluginBase::ConstructArgs plugin_base_args; - // TODO: Remove, transitional - bool audio_processor_supported; - /** * The class ID of this component's corresponding editor controller. You * can't use C-style array in `std::optional`s. @@ -91,8 +88,8 @@ class YaComponent : public Steinberg::Vst::IComponent, template void serialize(S& s) { s.value8b(instance_id); + s.object(audio_processor_args); s.object(plugin_base_args); - s.value1b(audio_processor_supported); s.ext(edit_controller_cid, bitsery::ext::StdOptional{}, [](S& s, auto& cid) { s.container1b(cid); }); } @@ -101,8 +98,7 @@ class YaComponent : public Steinberg::Vst::IComponent, /** * Message to request the Wine plugin host to instantiate a new IComponent * to pass through a call to `IComponent::createInstance(cid, - * IComponent::iid, - * ...)`. + * IComponent::iid, ...)`. */ struct Construct { using Response = std::variant; @@ -145,7 +141,6 @@ class YaComponent : public Steinberg::Vst::IComponent, DECLARE_FUNKNOWN_METHODS - // From `IComponent` tresult PLUGIN_API getControllerClassId(Steinberg::TUID classId) override; /** @@ -381,222 +376,6 @@ class YaComponent : public Steinberg::Vst::IComponent, virtual tresult PLUGIN_API getState(Steinberg::IBStream* state) override = 0; - // From `IAudioProcessor` - - /** - * Message to pass through a call to - * `IAudioProcessor::setBusArrangements(inputs, num_ins, outputs, num_outs)` - * to the Wine plugin host. - */ - struct SetBusArrangements { - using Response = UniversalTResult; - - native_size_t instance_id; - - // These are orginally C-style heap arrays, not normal pointers - std::vector inputs; - int32 num_ins; - std::vector outputs; - int32 num_outs; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.container8b(inputs, max_num_speakers); - s.value4b(num_ins); - s.container8b(outputs, max_num_speakers); - s.value4b(num_outs); - } - }; - - virtual tresult PLUGIN_API - setBusArrangements(Steinberg::Vst::SpeakerArrangement* inputs, - int32 numIns, - Steinberg::Vst::SpeakerArrangement* outputs, - int32 numOuts) override = 0; - - /** - * The response code and written state for a call to - * `IAudioProcessor::getBusArrangement(dir, index, arr)`. - */ - struct GetBusArrangementResponse { - UniversalTResult result; - Steinberg::Vst::SpeakerArrangement updated_arr; - - template - void serialize(S& s) { - s.object(result); - s.value8b(updated_arr); - } - }; - - /** - * Message to pass through a call to - * `IAudioProcessor::getBusArrangement(dir, index, arr)` to the Wine - * plugin host. - */ - struct GetBusArrangement { - using Response = GetBusArrangementResponse; - - native_size_t instance_id; - - Steinberg::Vst::BusDirection dir; - int32 index; - Steinberg::Vst::SpeakerArrangement arr; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.value4b(dir); - s.value4b(index); - s.value8b(arr); - } - }; - - virtual tresult PLUGIN_API - getBusArrangement(Steinberg::Vst::BusDirection dir, - int32 index, - Steinberg::Vst::SpeakerArrangement& arr) override = 0; - - /** - * Message to pass through a call to - * `IAudioProcessor::canProcessSampleSize(symbolic_sample_size)` to the Wine - * plugin host. - */ - struct CanProcessSampleSize { - using Response = UniversalTResult; - - native_size_t instance_id; - - int32 symbolic_sample_size; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.value4b(symbolic_sample_size); - } - }; - - virtual tresult PLUGIN_API - canProcessSampleSize(int32 symbolicSampleSize) override = 0; - - /** - * Message to pass through a call to `IAudioProcessor::getLatencySamples()` - * to the Wine plugin host. - */ - struct GetLatencySamples { - using Response = PrimitiveWrapper; - - native_size_t instance_id; - - template - void serialize(S& s) { - s.value8b(instance_id); - } - }; - - virtual uint32 PLUGIN_API getLatencySamples() override = 0; - - /** - * Message to pass through a call to - * `IAudioProcessor::setupProcessing(setup)` to the Wine plugin host. - */ - struct SetupProcessing { - using Response = UniversalTResult; - - native_size_t instance_id; - - Steinberg::Vst::ProcessSetup setup; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.object(setup); - } - }; - - virtual tresult PLUGIN_API - setupProcessing(Steinberg::Vst::ProcessSetup& setup) override = 0; - - /** - * Message to pass through a call to `IAudioProcessor::setProcessing(state)` - * to the Wine plugin host. - */ - struct SetProcessing { - using Response = UniversalTResult; - - native_size_t instance_id; - - TBool state; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.value1b(state); - } - }; - - virtual tresult PLUGIN_API setProcessing(TBool state) override = 0; - - /** - * The response code and all the output data resulting from a call to - * `IAudioProcessor::process(data)`. - */ - struct ProcessResponse { - UniversalTResult result; - YaProcessDataResponse output_data; - - template - void serialize(S& s) { - s.object(result); - s.object(output_data); - } - }; - - /** - * Message to pass through a call to `IAudioProcessor::process(data)` to the - * Wine plugin host. This `YaProcessData` object wraps around all input - * audio buffers, parameter changes and events along with all context data - * provided by the host so we can send it to the Wine plugin host. We can - * then use `YaProcessData::get()` on the Wine plugin host side to - * reconstruct the original `ProcessData` object, and we then finally use - * `YaProcessData::move_outputs_to_response()` to create a response object - * that we can write back to the `ProcessData` object provided by the host. - */ - struct Process { - using Response = ProcessResponse; - - native_size_t instance_id; - - YaProcessData data; - - template - void serialize(S& s) { - s.value8b(instance_id); - s.object(data); - } - }; - - virtual tresult PLUGIN_API - process(Steinberg::Vst::ProcessData& data) override = 0; - - /** - * Message to pass through a call to `IAudioProcessor::getTailSamples()` - * to the Wine plugin host. - */ - struct GetTailSamples { - using Response = PrimitiveWrapper; - - native_size_t instance_id; - - template - void serialize(S& s) { - s.value8b(instance_id); - } - }; - - virtual uint32 PLUGIN_API getTailSamples() override = 0; - protected: ConstructArgs arguments; }; @@ -609,32 +388,3 @@ void serialize( std::variant& result) { s.ext(result, bitsery::ext::StdVariant{}); } - -namespace Steinberg { -namespace Vst { -template -void serialize(S& s, Steinberg::Vst::BusInfo& info) { - s.value4b(info.mediaType); - s.value4b(info.direction); - s.value4b(info.channelCount); - s.container2b(info.name); - s.value4b(info.busType); - s.value4b(info.flags); -} - -template -void serialize(S& s, Steinberg::Vst::RoutingInfo& info) { - s.value4b(info.mediaType); - s.value4b(info.busIndex); - s.value4b(info.channel); -} - -template -void serialize(S& s, Steinberg::Vst::ProcessSetup& setup) { - s.value4b(setup.processMode); - s.value4b(setup.symbolicSampleSize); - s.value4b(setup.maxSamplesPerBlock); - s.value8b(setup.sampleRate); -} -} // namespace Vst -} // namespace Steinberg diff --git a/src/common/serialization/vst3/plugin-base.h b/src/common/serialization/vst3/plugin-base.h index 47ee27c6..503fe2f4 100644 --- a/src/common/serialization/vst3/plugin-base.h +++ b/src/common/serialization/vst3/plugin-base.h @@ -40,9 +40,8 @@ class YaPluginBase : public Steinberg::IPluginBase { ConstructArgs(); /** - * Read arguments from an existing implementation. Depending on the - * supported interface function more or less of this struct will be left - * empty, and `known_iids` will be set accordingly. + * Check whether an existing implementation implements `IPluginBase` and + * read arguments from it. */ ConstructArgs(Steinberg::IPtr object); diff --git a/src/plugin/bridges/vst3-impls/component.cpp b/src/plugin/bridges/vst3-impls/component.cpp index 4a96aa50..92329b35 100644 --- a/src/plugin/bridges/vst3-impls/component.cpp +++ b/src/plugin/bridges/vst3-impls/component.cpp @@ -40,6 +40,77 @@ YaComponentPluginImpl::queryInterface(const Steinberg::TUID _iid, void** obj) { return result; } +tresult PLUGIN_API YaComponentPluginImpl::setBusArrangements( + Steinberg::Vst::SpeakerArrangement* inputs, + int32 numIns, + Steinberg::Vst::SpeakerArrangement* outputs, + int32 numOuts) { + assert(inputs && outputs); + return bridge.send_message(YaAudioProcessor::SetBusArrangements{ + .instance_id = arguments.instance_id, + .inputs = std::vector( + inputs, &inputs[numIns]), + .num_ins = numIns, + .outputs = std::vector( + outputs, &outputs[numOuts]), + .num_outs = numOuts, + }); +} + +tresult PLUGIN_API YaComponentPluginImpl::getBusArrangement( + Steinberg::Vst::BusDirection dir, + int32 index, + Steinberg::Vst::SpeakerArrangement& arr) { + const GetBusArrangementResponse response = + bridge.send_message(YaAudioProcessor::GetBusArrangement{ + .instance_id = arguments.instance_id, + .dir = dir, + .index = index, + .arr = arr}); + + arr = response.updated_arr; + + return response.result; +} + +tresult PLUGIN_API +YaComponentPluginImpl::canProcessSampleSize(int32 symbolicSampleSize) { + return bridge.send_message(YaAudioProcessor::CanProcessSampleSize{ + .instance_id = arguments.instance_id, + .symbolic_sample_size = symbolicSampleSize}); +} + +uint32 PLUGIN_API YaComponentPluginImpl::getLatencySamples() { + return bridge.send_message(YaAudioProcessor::GetLatencySamples{ + .instance_id = arguments.instance_id}); +} + +tresult PLUGIN_API +YaComponentPluginImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) { + return bridge.send_message(YaAudioProcessor::SetupProcessing{ + .instance_id = arguments.instance_id, .setup = setup}); +} + +tresult PLUGIN_API YaComponentPluginImpl::setProcessing(TBool state) { + return bridge.send_message(YaAudioProcessor::SetProcessing{ + .instance_id = arguments.instance_id, .state = state}); +} + +tresult PLUGIN_API +YaComponentPluginImpl::process(Steinberg::Vst::ProcessData& data) { + ProcessResponse response = bridge.send_message(YaAudioProcessor::Process{ + .instance_id = arguments.instance_id, .data = data}); + + response.output_data.write_back_outputs(data); + + return response.result; +} + +uint32 PLUGIN_API YaComponentPluginImpl::getTailSamples() { + return bridge.send_message( + YaAudioProcessor::GetTailSamples{.instance_id = arguments.instance_id}); +} + tresult PLUGIN_API YaComponentPluginImpl::setIoMode(Steinberg::Vst::IoMode mode) { return bridge.send_message(YaComponent::SetIoMode{ @@ -142,73 +213,3 @@ tresult PLUGIN_API YaComponentPluginImpl::terminate() { return bridge.send_message( YaPluginBase::Terminate{.instance_id = arguments.instance_id}); } - -tresult PLUGIN_API YaComponentPluginImpl::setBusArrangements( - Steinberg::Vst::SpeakerArrangement* inputs, - int32 numIns, - Steinberg::Vst::SpeakerArrangement* outputs, - int32 numOuts) { - assert(inputs && outputs); - return bridge.send_message(YaComponent::SetBusArrangements{ - .instance_id = arguments.instance_id, - .inputs = std::vector( - inputs, &inputs[numIns]), - .num_ins = numIns, - .outputs = std::vector( - outputs, &outputs[numOuts]), - .num_outs = numOuts, - }); -} - -tresult PLUGIN_API YaComponentPluginImpl::getBusArrangement( - Steinberg::Vst::BusDirection dir, - int32 index, - Steinberg::Vst::SpeakerArrangement& arr) { - const GetBusArrangementResponse response = bridge.send_message( - YaComponent::GetBusArrangement{.instance_id = arguments.instance_id, - .dir = dir, - .index = index, - .arr = arr}); - - arr = response.updated_arr; - - return response.result; -} - -tresult PLUGIN_API -YaComponentPluginImpl::canProcessSampleSize(int32 symbolicSampleSize) { - return bridge.send_message(YaComponent::CanProcessSampleSize{ - .instance_id = arguments.instance_id, - .symbolic_sample_size = symbolicSampleSize}); -} - -uint32 PLUGIN_API YaComponentPluginImpl::getLatencySamples() { - return bridge.send_message( - YaComponent::GetLatencySamples{.instance_id = arguments.instance_id}); -} - -tresult PLUGIN_API -YaComponentPluginImpl::setupProcessing(Steinberg::Vst::ProcessSetup& setup) { - return bridge.send_message(YaComponent::SetupProcessing{ - .instance_id = arguments.instance_id, .setup = setup}); -} - -tresult PLUGIN_API YaComponentPluginImpl::setProcessing(TBool state) { - return bridge.send_message(YaComponent::SetProcessing{ - .instance_id = arguments.instance_id, .state = state}); -} - -tresult PLUGIN_API -YaComponentPluginImpl::process(Steinberg::Vst::ProcessData& data) { - ProcessResponse response = bridge.send_message(YaComponent::Process{ - .instance_id = arguments.instance_id, .data = data}); - - response.output_data.write_back_outputs(data); - - return response.result; -} - -uint32 PLUGIN_API YaComponentPluginImpl::getTailSamples() { - return bridge.send_message( - YaComponent::GetTailSamples{.instance_id = arguments.instance_id}); -} diff --git a/src/plugin/bridges/vst3-impls/component.h b/src/plugin/bridges/vst3-impls/component.h index fd9a4ca5..8d2a293c 100644 --- a/src/plugin/bridges/vst3-impls/component.h +++ b/src/plugin/bridges/vst3-impls/component.h @@ -39,6 +39,24 @@ class YaComponentPluginImpl : public YaComponent { tresult PLUGIN_API queryInterface(const Steinberg::TUID _iid, void** obj) override; + // From `IAudioProcessor` + tresult PLUGIN_API + setBusArrangements(Steinberg::Vst::SpeakerArrangement* inputs, + int32 numIns, + Steinberg::Vst::SpeakerArrangement* outputs, + int32 numOuts) override; + tresult PLUGIN_API + getBusArrangement(Steinberg::Vst::BusDirection dir, + int32 index, + Steinberg::Vst::SpeakerArrangement& arr) override; + tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override; + uint32 PLUGIN_API getLatencySamples() override; + tresult PLUGIN_API + setupProcessing(Steinberg::Vst::ProcessSetup& setup) override; + tresult PLUGIN_API setProcessing(TBool state) override; + tresult PLUGIN_API process(Steinberg::Vst::ProcessData& data) override; + uint32 PLUGIN_API getTailSamples() override; + // From `IComponent` tresult PLUGIN_API setIoMode(Steinberg::Vst::IoMode mode) override; int32 PLUGIN_API getBusCount(Steinberg::Vst::MediaType type, @@ -63,23 +81,6 @@ class YaComponentPluginImpl : public YaComponent { tresult PLUGIN_API initialize(FUnknown* context) override; tresult PLUGIN_API terminate() override; - tresult PLUGIN_API - setBusArrangements(Steinberg::Vst::SpeakerArrangement* inputs, - int32 numIns, - Steinberg::Vst::SpeakerArrangement* outputs, - int32 numOuts) override; - tresult PLUGIN_API - getBusArrangement(Steinberg::Vst::BusDirection dir, - int32 index, - Steinberg::Vst::SpeakerArrangement& arr) override; - tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) override; - uint32 PLUGIN_API getLatencySamples() override; - tresult PLUGIN_API - setupProcessing(Steinberg::Vst::ProcessSetup& setup) override; - tresult PLUGIN_API setProcessing(TBool state) override; - tresult PLUGIN_API process(Steinberg::Vst::ProcessData& data) override; - uint32 PLUGIN_API getTailSamples() override; - private: Vst3PluginBridge& bridge; diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 6d60c3ce..1c0e9c36 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -60,6 +60,59 @@ void Vst3Bridge::run() { sockets.host_vst_control.receive_messages( std::nullopt, overload{ + [&](YaAudioProcessor::SetBusArrangements& request) + -> YaAudioProcessor::SetBusArrangements::Response { + return component_instances[request.instance_id] + .audio_processor->setBusArrangements( + request.inputs.data(), request.num_ins, + request.outputs.data(), request.num_outs); + }, + [&](YaAudioProcessor::GetBusArrangement& request) + -> YaAudioProcessor::GetBusArrangement::Response { + const tresult result = + component_instances[request.instance_id] + .audio_processor->getBusArrangement( + request.dir, request.index, request.arr); + + return YaAudioProcessor::GetBusArrangementResponse{ + .result = result, .updated_arr = request.arr}; + }, + [&](const YaAudioProcessor::CanProcessSampleSize& request) + -> YaAudioProcessor::CanProcessSampleSize::Response { + return component_instances[request.instance_id] + .audio_processor->canProcessSampleSize( + request.symbolic_sample_size); + }, + [&](const YaAudioProcessor::GetLatencySamples& request) + -> YaAudioProcessor::GetLatencySamples::Response { + return component_instances[request.instance_id] + .audio_processor->getLatencySamples(); + }, + [&](YaAudioProcessor::SetupProcessing& request) + -> YaAudioProcessor::SetupProcessing::Response { + return component_instances[request.instance_id] + .audio_processor->setupProcessing(request.setup); + }, + [&](const YaAudioProcessor::SetProcessing& request) + -> YaAudioProcessor::SetProcessing::Response { + return component_instances[request.instance_id] + .audio_processor->setProcessing(request.state); + }, + [&](YaAudioProcessor::Process& request) + -> YaAudioProcessor::Process::Response { + const tresult result = + component_instances[request.instance_id] + .audio_processor->process(request.data.get()); + + return YaAudioProcessor::ProcessResponse{ + .result = result, + .output_data = request.data.move_outputs_to_response()}; + }, + [&](const YaAudioProcessor::GetTailSamples& request) + -> YaAudioProcessor::GetTailSamples::Response { + return component_instances[request.instance_id] + .audio_processor->getTailSamples(); + }, [&](const YaComponent::Construct& args) -> YaComponent::Construct::Response { Steinberg::TUID cid; @@ -88,30 +141,6 @@ void Vst3Bridge::run() { return Ack{}; }, - [&](YaPluginBase::Initialize& request) - -> YaPluginBase::Initialize::Response { - // If we got passed a host context, we'll create a proxy object - // and pass that to the initialize function. This object should - // be cleaned up again during `YaComponent::Destruct`. - Steinberg::FUnknown* context = nullptr; - if (request.host_application_context_args) { - component_instances[request.instance_id] - .hsot_application_context = - Steinberg::owned(new YaHostApplicationHostImpl( - *this, - std::move(*request.host_application_context_args))); - context = component_instances[request.instance_id] - .hsot_application_context; - } - - return component_instances[request.instance_id] - .plugin_base->initialize(context); - }, - [&](const YaPluginBase::Terminate& request) - -> YaPluginBase::Terminate::Response { - return component_instances[request.instance_id] - .plugin_base->terminate(); - }, [&](const YaComponent::SetIoMode& request) -> YaComponent::SetIoMode::Response { return component_instances[request.instance_id] @@ -169,58 +198,29 @@ void Vst3Bridge::run() { return YaComponent::GetStateResponse{ .result = result, .updated_state = std::move(stream)}; }, - [&](YaComponent::SetBusArrangements& request) - -> YaComponent::SetBusArrangements::Response { - return component_instances[request.instance_id] - .audio_processor->setBusArrangements( - request.inputs.data(), request.num_ins, - request.outputs.data(), request.num_outs); - }, - [&](YaComponent::GetBusArrangement& request) - -> YaComponent::GetBusArrangement::Response { - const tresult result = + [&](YaPluginBase::Initialize& request) + -> YaPluginBase::Initialize::Response { + // If we got passed a host context, we'll create a proxy object + // and pass that to the initialize function. This object should + // be cleaned up again during `YaComponent::Destruct`. + Steinberg::FUnknown* context = nullptr; + if (request.host_application_context_args) { component_instances[request.instance_id] - .audio_processor->getBusArrangement( - request.dir, request.index, request.arr); + .hsot_application_context = + Steinberg::owned(new YaHostApplicationHostImpl( + *this, + std::move(*request.host_application_context_args))); + context = component_instances[request.instance_id] + .hsot_application_context; + } - return YaComponent::GetBusArrangementResponse{ - .result = result, .updated_arr = request.arr}; - }, - [&](const YaComponent::CanProcessSampleSize& request) - -> YaComponent::CanProcessSampleSize::Response { return component_instances[request.instance_id] - .audio_processor->canProcessSampleSize( - request.symbolic_sample_size); + .plugin_base->initialize(context); }, - [&](const YaComponent::GetLatencySamples& request) - -> YaComponent::GetLatencySamples::Response { + [&](const YaPluginBase::Terminate& request) + -> YaPluginBase::Terminate::Response { return component_instances[request.instance_id] - .audio_processor->getLatencySamples(); - }, - [&](YaComponent::SetupProcessing& request) - -> YaComponent::SetupProcessing::Response { - return component_instances[request.instance_id] - .audio_processor->setupProcessing(request.setup); - }, - [&](const YaComponent::SetProcessing& request) - -> YaComponent::SetProcessing::Response { - return component_instances[request.instance_id] - .audio_processor->setProcessing(request.state); - }, - [&](YaComponent::Process& request) - -> YaComponent::Process::Response { - const tresult result = - component_instances[request.instance_id] - .audio_processor->process(request.data.get()); - - return YaComponent::ProcessResponse{ - .result = result, - .output_data = request.data.move_outputs_to_response()}; - }, - [&](const YaComponent::GetTailSamples& request) - -> YaComponent::GetTailSamples::Response { - return component_instances[request.instance_id] - .audio_processor->getTailSamples(); + .plugin_base->terminate(); }, [&](const YaPluginFactory::Construct&) -> YaPluginFactory::Construct::Response {