diff --git a/meson.build b/meson.build index cca4136c..783c6428 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/component.cpp', 'src/common/serialization/vst3/plugin-factory.cpp', 'src/common/configuration.cpp', 'src/common/plugins.cpp', @@ -108,6 +109,7 @@ host_sources = [ if with_vst3 host_sources += [ 'src/common/logging/vst3.cpp', + 'src/common/serialization/vst3/component.cpp', 'src/common/serialization/vst3/plugin-factory.cpp', 'src/wine-host/bridges/vst3.cpp', 'src/wine-host/bridges/vst3-impls.cpp', diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index 19e30adb..f75010be 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -56,20 +56,26 @@ void Vst3Logger::log_request(bool is_host_vst, const WantsPluginFactory&) { } } -void Vst3Logger::log_response(bool is_host_vst, const Configuration&) { +void Vst3Logger::log_response( + bool is_host_vst, + const std::optional& args) { if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) { std::ostringstream message; - message << get_log_prefix(is_host_vst) << " "; + if (args) { + message << get_log_prefix(is_host_vst) << " instance_id << ">"; + } else { + message << get_log_prefix(is_host_vst) << " "; + } log(message.str()); } } -void Vst3Logger::log_response(bool is_host_vst, const YaComponent&) { +void Vst3Logger::log_response(bool is_host_vst, const Configuration&) { if (BOOST_UNLIKELY(logger.verbosity >= Logger::Verbosity::most_events)) { std::ostringstream message; - // TODO: Add the instance ID after we implement that - message << get_log_prefix(is_host_vst) << " "; + message << get_log_prefix(is_host_vst) << " "; log(message.str()); } diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h index 48b1d0b3..11d75739 100644 --- a/src/common/logging/vst3.h +++ b/src/common/logging/vst3.h @@ -49,8 +49,9 @@ class Vst3Logger { void log_request(bool is_host_vst, const WantsConfiguration&); void log_request(bool is_host_vst, const WantsPluginFactory&); + void log_response(bool is_host_vst, + const std::optional&); void log_response(bool is_host_vst, const Configuration&); - void log_response(bool is_host_vst, const YaComponent&); void log_response(bool is_host_vst, const YaPluginFactory&); Logger& logger; diff --git a/src/common/serialization/vst3/README.md b/src/common/serialization/vst3/README.md index 0757e74e..94d6f9c2 100644 --- a/src/common/serialization/vst3/README.md +++ b/src/common/serialization/vst3/README.md @@ -3,6 +3,12 @@ TODO: Once this is more fleshed out, move this document to `docs/`, and perhaps replace this readme with a link to that document. +TODO: There are now two approaches in use: the factory takes an interface +pointer for serialization and deserializes into an object directly, and the +component uses an args struct because the alternative involving pointers is just +too unsafe (as we also have to communicate additional payload data). This should +probably be unified into only using the latter appraoch. + The VST3 SDK uses an architecture where every object inherits from an interface, and every interface inherits from `FUnknown` which offers a dynamic casting interface through `queryInterface()`. Every interface gets a unique identifier. diff --git a/src/common/serialization/vst3/component.cpp b/src/common/serialization/vst3/component.cpp index efe19abf..9e521646 100644 --- a/src/common/serialization/vst3/component.cpp +++ b/src/common/serialization/vst3/component.cpp @@ -16,14 +16,19 @@ #include "component.h" -YaComponent::YaComponent(){FUNKNOWN_CTOR} - -YaComponent::YaComponent( - Steinberg::IPtr component) { - FUNKNOWN_CTOR - +YaComponent::Arguments::Arguments( + Steinberg::IPtr component, + size_t instance_id) + : instance_id(instance_id) { // `IComponent::getControllerClassId` - component->getControllerClassId(edit_controller_cid); + Steinberg::TUID cid; + if (component->getControllerClassId(cid) == Steinberg::kResultOk) { + edit_controller_cid = std::to_array(cid); + } +} + +YaComponent::YaComponent(const Arguments&& args) : arguments(std::move(args)) { + FUNKNOWN_CTOR // Everything else is handled directly through callbacks to minimize the // potential for errors @@ -49,3 +54,13 @@ tresult PLUGIN_API YaComponent::queryInterface(Steinberg::FIDString _iid, *obj = nullptr; return Steinberg::kNoInterface; } + +tresult PLUGIN_API YaComponent::getControllerClassId(Steinberg::TUID classId) { + if (arguments.edit_controller_cid) { + std::copy(arguments.edit_controller_cid->begin(), + arguments.edit_controller_cid->end(), classId); + return Steinberg::kResultOk; + } else { + return Steinberg::kNotImplemented; + } +} diff --git a/src/common/serialization/vst3/component.h b/src/common/serialization/vst3/component.h index 41f6718a..2d285e69 100644 --- a/src/common/serialization/vst3/component.h +++ b/src/common/serialization/vst3/component.h @@ -16,9 +16,15 @@ #pragma once +#include +#include "src/common/serialization/common.h" + +#include +#include +#include #include -using Steinberg::TBool, Steinberg::int32, Steinberg::tresult; +using Steinberg::TBool, Steinberg::int8, Steinberg::int32, Steinberg::tresult; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" @@ -36,14 +42,44 @@ using Steinberg::TBool, Steinberg::int32, Steinberg::tresult; class YaComponent : public Steinberg::Vst::IComponent { public: /** - * Request the Wine plugin host to instantiate a new IComponent to pass - * through a call to `IPluginFactory::createInstance(cid, IComponent::iid, + * These are the arguments for creating a `YaComponentPluginImpl`. + */ + struct Arguments { + /** + * Read arguments from an existing implementation. + */ + Arguments(Steinberg::IPtr component, + size_t isntance_id); + + /** + * The unique identifier for this specific instance. + */ + native_size_t instance_id; + + /** + * The class ID of this component's corresponding editor controller. You + * can't use C-style array in `std::optional`s. + */ + std::optional>> + edit_controller_cid; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.ext(edit_controller_cid, bitsery::ext::StdOptional{}, + [](S& s, auto& cid) { s.container1b(cid); }); + } + }; + + /** + * Message to request the Wine plugin host to instantiate a new IComponent + * to pass through a call to `IPluginFactory::createInstance(cid, + * IComponent::iid, * ...)`. */ struct Create { - // TODO: This should be `std::optional`, and we need a way - // to deserialize that into an existing YaComponent. - using Response = YaComponent&; + // TODO: Create a `native_tvalue` wrapper, and then also add them here + using Response = std::optional; Steinberg::TUID cid; @@ -53,12 +89,11 @@ class YaComponent : public Steinberg::Vst::IComponent { } }; - YaComponent(); - /** - * Create a copy of an existing component. + * Instantiate this instance with arguments read from another interface + * implementation. */ - explicit YaComponent(Steinberg::IPtr component); + YaComponent(const Arguments&& args); /** * @remark The plugin side implementation should send a control message to @@ -97,19 +132,16 @@ class YaComponent : public Steinberg::Vst::IComponent { virtual tresult PLUGIN_API getState(Steinberg::IBStream* state) override = 0; - template - void serialize(S& s) { - s.container1b(edit_controller_cid); - } - private: - /** - * The class ID of this component's corresponding editor controller. - */ - Steinberg::TUID edit_controller_cid; + Arguments arguments; // TODO: As explained in a few other places, `YaComponent` objects should be // assigned a unique ID for identification }; +template +void serialize(S& s, std::optional& args) { + s.ext(args, bitsery::ext::StdOptional{}); +} + #pragma GCC diagnostic pop diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index f62f1068..791810b8 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -66,17 +66,13 @@ void Vst3Bridge::run() { // ID to that. // - Add that ID to `YaComponent` and set it in the object // we create here. - // - In case `factory` is a null pointer, allow returning - // `nullopt`. Not sure how that is going to work with - // the deserialization. - if (!component) { - // TODO: Handle + if (component) { + // TODO: Generate a unique instance ID + return std::make_optional( + component, 420691337); + } else { + return std::nullopt; } - - // TODO: Implement `YaComponentHostImpl` and create an instance - // based on `component` - YaComponent* removeme = nullptr; - return *removeme; }, [&](const WantsPluginFactory&) -> WantsPluginFactory::Response { return *plugin_factory;