// 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 "../vst3.h" #include "plug-view-proxy.h" class Vst3PluginProxyImpl : public Vst3PluginProxy { public: Vst3PluginProxyImpl(Vst3PluginBridge& bridge, Vst3PluginProxy::ConstructArgs&& args); /** * When the reference count reaches zero and this destructor is called, * we'll send a request to the Wine plugin host to destroy the corresponding * object. */ ~Vst3PluginProxyImpl(); /** * We'll override the query interface to log queries for interfaces we do * not (yet) support. */ tresult PLUGIN_API queryInterface(const Steinberg::TUID _iid, void** obj) override; /** * Add a context menu created by a call to * `IComponentHandler3::createContextMenu` to our list of registered cotnext * menus. This way we can refer to it later when the plugin calls a function * on the proxy object we'll create for it. */ size_t register_context_menu( Steinberg::IPtr menu); /** * Unregister a context menu using the ID generated by a previous call to * `register_context_menu()`. This will release the context menu object * returned by the host. */ bool unregister_context_menu(size_t context_menu_id); // From `IAudioPresentationLatency` tresult PLUGIN_API setAudioPresentationLatencySamples(Steinberg::Vst::BusDirection dir, int32 busIndex, uint32 latencyInSamples) 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 `IAutomationState` tresult PLUGIN_API setAutomationState(int32 state) override; // From `IComponent` tresult PLUGIN_API getControllerClassId(Steinberg::TUID classId) override; tresult PLUGIN_API setIoMode(Steinberg::Vst::IoMode mode) override; int32 PLUGIN_API getBusCount(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir) override; tresult PLUGIN_API getBusInfo(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir, int32 index, Steinberg::Vst::BusInfo& bus /*out*/) override; tresult PLUGIN_API getRoutingInfo(Steinberg::Vst::RoutingInfo& inInfo, Steinberg::Vst::RoutingInfo& outInfo /*out*/) override; tresult PLUGIN_API activateBus(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir, int32 index, TBool state) override; tresult PLUGIN_API setActive(TBool state) override; tresult PLUGIN_API setState(Steinberg::IBStream* state) override; tresult PLUGIN_API getState(Steinberg::IBStream* state) override; // From `IConnectionPoint` tresult PLUGIN_API connect(IConnectionPoint* other) override; tresult PLUGIN_API disconnect(IConnectionPoint* other) override; tresult PLUGIN_API notify(Steinberg::Vst::IMessage* message) override; // From `IEditController` tresult PLUGIN_API setComponentState(Steinberg::IBStream* state) override; // `IEditController` also contains `getState()` and `setState()` functions. // These are identical to those defiend in `IComponent` and they're thus // handled in in the same function. int32 PLUGIN_API getParameterCount() override; tresult PLUGIN_API getParameterInfo(int32 paramIndex, Steinberg::Vst::ParameterInfo& info /*out*/) override; tresult PLUGIN_API getParamStringByValue(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue valueNormalized /*in*/, Steinberg::Vst::String128 string /*out*/) override; tresult PLUGIN_API getParamValueByString( Steinberg::Vst::ParamID id, Steinberg::Vst::TChar* string /*in*/, Steinberg::Vst::ParamValue& valueNormalized /*out*/) override; Steinberg::Vst::ParamValue PLUGIN_API normalizedParamToPlain(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue valueNormalized) override; Steinberg::Vst::ParamValue PLUGIN_API plainParamToNormalized(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue plainValue) override; Steinberg::Vst::ParamValue PLUGIN_API getParamNormalized(Steinberg::Vst::ParamID id) override; tresult PLUGIN_API setParamNormalized(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue value) override; tresult PLUGIN_API setComponentHandler(Steinberg::Vst::IComponentHandler* handler) override; Steinberg::IPlugView* PLUGIN_API createView(Steinberg::FIDString name) override; // From `IEditController2` tresult PLUGIN_API setKnobMode(Steinberg::Vst::KnobMode mode) override; tresult PLUGIN_API openHelp(TBool onlyCheck) override; tresult PLUGIN_API openAboutBox(TBool onlyCheck) override; // From `IEditControllerHostEditing` tresult PLUGIN_API beginEditFromHost(Steinberg::Vst::ParamID paramID) override; tresult PLUGIN_API endEditFromHost(Steinberg::Vst::ParamID paramID) override; // From `IInfoListener` tresult PLUGIN_API setChannelContextInfos(Steinberg::Vst::IAttributeList* list) override; // From `IKeyswitchController` int32 PLUGIN_API getKeyswitchCount(int32 busIndex, int16 channel) override; tresult PLUGIN_API getKeyswitchInfo(int32 busIndex, int16 channel, int32 keySwitchIndex, Steinberg::Vst::KeyswitchInfo& info /*out*/) override; // From `IMidiMapping` tresult PLUGIN_API getMidiControllerAssignment(int32 busIndex, int16 channel, Steinberg::Vst::CtrlNumber midiControllerNumber, Steinberg::Vst::ParamID& id /*out*/) override; // From `INoteExpressionController` int32 PLUGIN_API getNoteExpressionCount(int32 busIndex, int16 channel) override; tresult PLUGIN_API getNoteExpressionInfo( int32 busIndex, int16 channel, int32 noteExpressionIndex, Steinberg::Vst::NoteExpressionTypeInfo& info /*out*/) override; tresult PLUGIN_API getNoteExpressionStringByValue( int32 busIndex, int16 channel, Steinberg::Vst::NoteExpressionTypeID id, Steinberg::Vst::NoteExpressionValue valueNormalized /*in*/, Steinberg::Vst::String128 string /*out*/) override; tresult PLUGIN_API getNoteExpressionValueByString( int32 busIndex, int16 channel, Steinberg::Vst::NoteExpressionTypeID id, const Steinberg::Vst::TChar* string /*in*/, Steinberg::Vst::NoteExpressionValue& valueNormalized /*out*/) override; // From `IPluginBase` tresult PLUGIN_API initialize(FUnknown* context) override; tresult PLUGIN_API terminate() override; // From `IPrefetchableSupport` tresult PLUGIN_API getPrefetchableSupport( Steinberg::Vst::PrefetchableSupport& prefetchable /*out*/) override; // From `IProgramListData` tresult PLUGIN_API programDataSupported(Steinberg::Vst::ProgramListID listId) override; tresult PLUGIN_API getProgramData(Steinberg::Vst::ProgramListID listId, int32 programIndex, Steinberg::IBStream* data) override; tresult PLUGIN_API setProgramData(Steinberg::Vst::ProgramListID listId, int32 programIndex, Steinberg::IBStream* data) override; // From `IUnitData` tresult PLUGIN_API unitDataSupported(Steinberg::Vst::UnitID unitId) override; tresult PLUGIN_API getUnitData(Steinberg::Vst::UnitID unitId, Steinberg::IBStream* data) override; tresult PLUGIN_API setUnitData(Steinberg::Vst::UnitID unitId, Steinberg::IBStream* data) override; // From `IUnitInfo` int32 PLUGIN_API getUnitCount() override; tresult PLUGIN_API getUnitInfo(int32 unitIndex, Steinberg::Vst::UnitInfo& info /*out*/) override; int32 PLUGIN_API getProgramListCount() override; tresult PLUGIN_API getProgramListInfo(int32 listIndex, Steinberg::Vst::ProgramListInfo& info /*out*/) override; tresult PLUGIN_API getProgramName(Steinberg::Vst::ProgramListID listId, int32 programIndex, Steinberg::Vst::String128 name /*out*/) override; tresult PLUGIN_API getProgramInfo(Steinberg::Vst::ProgramListID listId, int32 programIndex, Steinberg::Vst::CString attributeId /*in*/, Steinberg::Vst::String128 attributeValue /*out*/) override; tresult PLUGIN_API hasProgramPitchNames(Steinberg::Vst::ProgramListID listId, int32 programIndex) override; tresult PLUGIN_API getProgramPitchName(Steinberg::Vst::ProgramListID listId, int32 programIndex, int16 midiPitch, Steinberg::Vst::String128 name /*out*/) override; Steinberg::Vst::UnitID PLUGIN_API getSelectedUnit() override; tresult PLUGIN_API selectUnit(Steinberg::Vst::UnitID unitId) override; tresult PLUGIN_API getUnitByBus(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection dir, int32 busIndex, int32 channel, Steinberg::Vst::UnitID& unitId /*out*/) override; tresult PLUGIN_API setUnitProgramData(int32 listOrUnitId, int32 programIndex, Steinberg::IBStream* data) override; // From `IXmlRepresentationController` tresult PLUGIN_API getXmlRepresentationStream(Steinberg::Vst::RepresentationInfo& info /*in*/, Steinberg::IBStream* stream /*out*/); /** * The component handler the host passed to us during * `IEditController::setComponentHandler()`. When the plugin makes a * callback on a component handler proxy object, we'll pass the call through * to this object. */ Steinberg::IPtr component_handler; /** * If the host doesn't connect two objects directly in * `IConnectionPoint::connect` but instead connects them through a proxy, * we'll store that proxy here. This way we can then route messages sent by * the plugin through this proxy. So far this is only needed for Ardour. */ Steinberg::IPtr connection_point_proxy; /** * An unmanaged, raw pointer to the `IPlugView` instance returned in our * implementation of `IEditController::createView()`. We need this to handle * `IPlugFrame::resizeView()`, since that expects a pointer to the view that * gets resized. * * XXX: This approach of course won't work with multiple views, but the SDK * currently only defines a single type of view so that shouldn't be an * issue */ Vst3PlugViewProxyImpl* last_created_plug_view = nullptr; /** * A pointer to a context menu returned by the host as a response to a call * to `IComponentHandler3::createContextMenu`, as well as all targets we've * created for it. This way we can drop both all at once. */ struct ContextMenu { ContextMenu(Steinberg::IPtr menu); Steinberg::IPtr menu; /** * All targets we pass to `IContextMenu::addItem`. We'll store them per * item tag, so we can drop them together with the menu. We probably * don't have to use smart pointers for this, but the docs are missing a * lot of details o how this should be implemented and there's no * example implementation around. */ std::map> targets; }; /** * All context menus created by this object through * `IComponentHandler3::createContextMenu()`. We'll generate a unique * identifier for each context menu just like we do for plugin objects. When * the plugin drops the context menu object, we'll also remove the * corresponding entry for this map causing the original pointer returned by * the host to get dropped a well. * * @see Vst3PluginProxyImpl::register_context_menu * @see Vst3PluginProxyImpl::unregister_context_menu */ std::map context_menus; std::mutex context_menus_mutex; // The following pointers are cast from `host_context` if // `IPluginBase::initialize()` has been called Steinberg::FUnknownPtr host_application; // The following pointers are cast from `component_handler` if // `IEditController::setComponentHandler()` has been called Steinberg::FUnknownPtr component_handler_2; Steinberg::FUnknownPtr component_handler_3; Steinberg::FUnknownPtr unit_handler; Steinberg::FUnknownPtr unit_handler_2; private: Vst3PluginBridge& bridge; /** * An host context if we get passed one through `IPluginBase::initialize()`. * We'll read which interfaces it supports and we'll then create a proxy * object that supports those same interfaces. This should be the same for * all plugin instances so we should not have to store it here separately, * but for the sake of correctness we will. */ Steinberg::IPtr host_context; /** * Used to assign unique identifiers to context menus created by * `IComponentHandler3::CreateContextMenu`. * * @related Vst3PluginProxyImpl::register_context_menu */ std::atomic_size_t current_context_menu_id; };