mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
AudioProcessor: Add extensions API for VST3 clients
This commit is contained in:
parent
442369bd6b
commit
63a40188d9
7 changed files with 325 additions and 76 deletions
|
|
@ -125,10 +125,7 @@ public:
|
|||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Steinberg::Linux::IEventHandler)
|
||||
|
||||
*obj = nullptr;
|
||||
return kNoInterface;
|
||||
return testFor (*this, targetIID, UniqueBase<Steinberg::Linux::IEventHandler>{}).extract (obj);
|
||||
}
|
||||
|
||||
void PLUGIN_API onFDIsSet (Steinberg::Linux::FileDescriptor fd) override
|
||||
|
|
@ -280,6 +277,45 @@ private:
|
|||
ThreadLocalValue<bool>& toSet;
|
||||
};
|
||||
|
||||
template <typename Member>
|
||||
static QueryInterfaceResult queryAdditionalInterfaces (AudioProcessor* processor,
|
||||
const TUID targetIID,
|
||||
Member&& member)
|
||||
{
|
||||
if (processor == nullptr)
|
||||
return {};
|
||||
|
||||
void* obj = nullptr;
|
||||
|
||||
if (auto* extensions = dynamic_cast<VST3ClientExtensions*> (processor))
|
||||
{
|
||||
const auto result = (extensions->*member) (targetIID, &obj);
|
||||
return { result, obj };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
tresult extractResult (const QueryInterfaceResult& userInterface,
|
||||
const InterfaceResultWithDeferredAddRef& juceInterface,
|
||||
void** obj)
|
||||
{
|
||||
if (userInterface.isOk() && juceInterface.isOk())
|
||||
{
|
||||
// If you hit this assertion, you've provided a custom implementation of an interface
|
||||
// that JUCE implements already. As a result, your plugin may not behave correctly.
|
||||
// Consider removing your custom implementation.
|
||||
jassertfalse;
|
||||
|
||||
return userInterface.extract (obj);
|
||||
}
|
||||
|
||||
if (userInterface.isOk())
|
||||
return userInterface.extract (obj);
|
||||
|
||||
return juceInterface.extract (obj);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JuceAudioProcessor : public Vst::IUnitInfo
|
||||
{
|
||||
|
|
@ -629,27 +665,13 @@ public:
|
|||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, FObject)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, JuceVST3EditController)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IEditController)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IEditController2)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IConnectionPoint)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IMidiMapping)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IUnitInfo)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::ChannelContext::IInfoListener)
|
||||
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, IPluginBase, Vst::IEditController)
|
||||
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, IDependent, Vst::IEditController)
|
||||
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, FUnknown, Vst::IEditController)
|
||||
const auto userProvidedInterface = queryAdditionalInterfaces (getPluginInstance(),
|
||||
targetIID,
|
||||
&VST3ClientExtensions::queryIEditController);
|
||||
|
||||
if (doUIDsMatch (targetIID, JuceAudioProcessor::iid))
|
||||
{
|
||||
audioProcessor->addRef();
|
||||
*obj = audioProcessor;
|
||||
return kResultOk;
|
||||
}
|
||||
const auto juceProvidedInterface = queryInterfaceInternal (targetIID);
|
||||
|
||||
*obj = nullptr;
|
||||
return kNoInterface;
|
||||
return extractResult (userProvidedInterface, juceProvidedInterface, obj);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -1151,7 +1173,7 @@ public:
|
|||
&& (pluginInstance->getActiveEditor() == nullptr || getHostType().isAdobeAudition());
|
||||
|
||||
if (mayCreateEditor)
|
||||
return new JuceVST3Editor (*this, *pluginInstance);
|
||||
return new JuceVST3Editor (*this, *audioProcessor);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -1328,10 +1350,41 @@ private:
|
|||
float lastScaleFactorReceived = 1.0f;
|
||||
#endif
|
||||
|
||||
InterfaceResultWithDeferredAddRef queryInterfaceInternal (const TUID targetIID)
|
||||
{
|
||||
const auto result = testForMultiple (*this,
|
||||
targetIID,
|
||||
UniqueBase<FObject>{},
|
||||
UniqueBase<JuceVST3EditController>{},
|
||||
UniqueBase<Vst::IEditController>{},
|
||||
UniqueBase<Vst::IEditController2>{},
|
||||
UniqueBase<Vst::IConnectionPoint>{},
|
||||
UniqueBase<Vst::IMidiMapping>{},
|
||||
UniqueBase<Vst::IUnitInfo>{},
|
||||
UniqueBase<Vst::ChannelContext::IInfoListener>{},
|
||||
SharedBase<IPluginBase, Vst::IEditController>{},
|
||||
UniqueBase<IDependent>{},
|
||||
SharedBase<FUnknown, Vst::IEditController>{});
|
||||
|
||||
if (result.isOk())
|
||||
return result;
|
||||
|
||||
if (doUIDsMatch (targetIID, JuceAudioProcessor::iid))
|
||||
return { kResultOk, audioProcessor.get() };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void installAudioProcessor (const VSTComSmartPtr<JuceAudioProcessor>& newAudioProcessor)
|
||||
{
|
||||
audioProcessor = newAudioProcessor;
|
||||
|
||||
if (auto* extensions = dynamic_cast<VST3ClientExtensions*> (audioProcessor->get()))
|
||||
{
|
||||
extensions->setIComponentHandler (componentHandler);
|
||||
extensions->setIHostApplication (hostContext.get());
|
||||
}
|
||||
|
||||
if (auto* pluginInstance = getPluginInstance())
|
||||
{
|
||||
lastLatencySamples = pluginInstance->getLatencySamples();
|
||||
|
|
@ -1420,10 +1473,10 @@ private:
|
|||
private Timer
|
||||
{
|
||||
public:
|
||||
JuceVST3Editor (JuceVST3EditController& ec, AudioProcessor& p)
|
||||
: Vst::EditorView (&ec, nullptr),
|
||||
owner (&ec),
|
||||
pluginInstance (p)
|
||||
JuceVST3Editor (JuceVST3EditController& ec, JuceAudioProcessor& p)
|
||||
: EditorView (&ec, nullptr),
|
||||
owner (&ec),
|
||||
pluginInstance (*p.get())
|
||||
{
|
||||
createContentWrapperComponentIfNeeded();
|
||||
|
||||
|
|
@ -1438,7 +1491,11 @@ private:
|
|||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Steinberg::IPlugViewContentScaleSupport)
|
||||
const auto result = testFor (*this, targetIID, UniqueBase<IPlugViewContentScaleSupport>{});
|
||||
|
||||
if (result.isOk())
|
||||
return result.extract (obj);
|
||||
|
||||
return Vst::EditorView::queryInterface (targetIID, obj);
|
||||
}
|
||||
|
||||
|
|
@ -2059,23 +2116,13 @@ public:
|
|||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, IPluginBase)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, JuceVST3Component)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IComponent)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IAudioProcessor)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IUnitInfo)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, Vst::IConnectionPoint)
|
||||
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (targetIID, FUnknown, Vst::IComponent)
|
||||
const auto userProvidedInterface = queryAdditionalInterfaces (&getPluginInstance(),
|
||||
targetIID,
|
||||
&VST3ClientExtensions::queryIAudioProcessor);
|
||||
|
||||
if (doUIDsMatch (targetIID, JuceAudioProcessor::iid))
|
||||
{
|
||||
comPluginInstance->addRef();
|
||||
*obj = comPluginInstance;
|
||||
return kResultOk;
|
||||
}
|
||||
const auto juceProvidedInterface = queryInterfaceInternal (targetIID);
|
||||
|
||||
*obj = nullptr;
|
||||
return kNoInterface;
|
||||
return extractResult (userProvidedInterface, juceProvidedInterface, obj);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -3006,6 +3053,27 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
InterfaceResultWithDeferredAddRef queryInterfaceInternal (const TUID targetIID)
|
||||
{
|
||||
const auto result = testForMultiple (*this,
|
||||
targetIID,
|
||||
UniqueBase<IPluginBase>{},
|
||||
UniqueBase<JuceVST3Component>{},
|
||||
UniqueBase<Vst::IComponent>{},
|
||||
UniqueBase<Vst::IAudioProcessor>{},
|
||||
UniqueBase<Vst::IUnitInfo>{},
|
||||
UniqueBase<Vst::IConnectionPoint>{},
|
||||
SharedBase<FUnknown, Vst::IComponent>{});
|
||||
|
||||
if (result.isOk())
|
||||
return result;
|
||||
|
||||
if (doUIDsMatch (targetIID, JuceAudioProcessor::iid))
|
||||
return { kResultOk, comPluginInstance.get() };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ScopedInSetupProcessingSetter
|
||||
{
|
||||
|
|
@ -3287,6 +3355,7 @@ private:
|
|||
}
|
||||
|
||||
T* operator->() { return ptr.operator->(); }
|
||||
T* get() const noexcept { return ptr.get(); }
|
||||
operator T*() const noexcept { return ptr.get(); }
|
||||
|
||||
template <typename... Args>
|
||||
|
|
@ -3523,10 +3592,15 @@ struct JucePluginFactory : public IPluginFactory3
|
|||
|
||||
tresult PLUGIN_API queryInterface (const TUID targetIID, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, IPluginFactory3)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, IPluginFactory2)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, IPluginFactory)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (targetIID, FUnknown)
|
||||
const auto result = testForMultiple (*this,
|
||||
targetIID,
|
||||
UniqueBase<IPluginFactory3>{},
|
||||
UniqueBase<IPluginFactory2>{},
|
||||
UniqueBase<IPluginFactory>{},
|
||||
UniqueBase<FUnknown>{});
|
||||
|
||||
if (result.isOk())
|
||||
return result.extract (obj);
|
||||
|
||||
jassertfalse; // Something new?
|
||||
*obj = nullptr;
|
||||
|
|
|
|||
|
|
@ -127,4 +127,3 @@
|
|||
#endif
|
||||
|
||||
#include "utility/juce_CreatePluginFilter.h"
|
||||
#include "VST/juce_VSTCallbackHandler.h"
|
||||
|
|
|
|||
|
|
@ -46,22 +46,106 @@ static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexc
|
|||
return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0;
|
||||
}
|
||||
|
||||
#define TEST_FOR_AND_RETURN_IF_VALID(iidToTest, ClassType) \
|
||||
if (doUIDsMatch (iidToTest, ClassType::iid)) \
|
||||
{ \
|
||||
addRef(); \
|
||||
*obj = dynamic_cast<ClassType*> (this); \
|
||||
return Steinberg::kResultOk; \
|
||||
/* Holds a tresult and a pointer to an object.
|
||||
|
||||
Useful for holding intermediate results of calls to queryInterface.
|
||||
*/
|
||||
class QueryInterfaceResult
|
||||
{
|
||||
public:
|
||||
QueryInterfaceResult() = default;
|
||||
|
||||
QueryInterfaceResult (Steinberg::tresult resultIn, void* ptrIn)
|
||||
: result (resultIn), ptr (ptrIn) {}
|
||||
|
||||
bool isOk() const noexcept { return result == Steinberg::kResultOk; }
|
||||
|
||||
Steinberg::tresult extract (void** obj) const
|
||||
{
|
||||
*obj = result == Steinberg::kResultOk ? ptr : nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(iidToTest, CommonClassType, SourceClassType) \
|
||||
if (doUIDsMatch (iidToTest, CommonClassType::iid)) \
|
||||
{ \
|
||||
addRef(); \
|
||||
*obj = (CommonClassType*) static_cast<SourceClassType*> (this); \
|
||||
return Steinberg::kResultOk; \
|
||||
private:
|
||||
Steinberg::tresult result = Steinberg::kResultFalse;
|
||||
void* ptr = nullptr;
|
||||
};
|
||||
|
||||
/* Holds a tresult and a pointer to an object.
|
||||
|
||||
Calling InterfaceResultWithDeferredAddRef::extract() will also call addRef
|
||||
on the pointed-to object. It is expected that users will use
|
||||
InterfaceResultWithDeferredAddRef to hold intermediate results of a queryInterface
|
||||
call. When a suitable interface is found, the function can be exited with
|
||||
`return suitableInterface.extract (obj)`, which will set the obj pointer,
|
||||
add a reference to the interface, and return the appropriate result code.
|
||||
*/
|
||||
class InterfaceResultWithDeferredAddRef
|
||||
{
|
||||
public:
|
||||
InterfaceResultWithDeferredAddRef() = default;
|
||||
|
||||
template <typename Ptr>
|
||||
InterfaceResultWithDeferredAddRef (Steinberg::tresult resultIn, Ptr* ptrIn)
|
||||
: result (resultIn, ptrIn),
|
||||
addRefFn (doAddRef<Ptr>) {}
|
||||
|
||||
bool isOk() const noexcept { return result.isOk(); }
|
||||
|
||||
Steinberg::tresult extract (void** obj) const
|
||||
{
|
||||
const auto toReturn = result.extract (obj);
|
||||
|
||||
if (result.isOk() && addRefFn != nullptr && *obj != nullptr)
|
||||
addRefFn (*obj);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Ptr>
|
||||
static void doAddRef (void* obj) { static_cast<Ptr*> (obj)->addRef(); }
|
||||
|
||||
QueryInterfaceResult result;
|
||||
void (*addRefFn) (void*) = nullptr;
|
||||
};
|
||||
|
||||
template <typename ClassType> struct UniqueBase {};
|
||||
template <typename CommonClassType, typename SourceClassType> struct SharedBase {};
|
||||
|
||||
template <typename ToTest, typename CommonClassType, typename SourceClassType>
|
||||
InterfaceResultWithDeferredAddRef testFor (ToTest& toTest,
|
||||
const Steinberg::TUID targetIID,
|
||||
SharedBase<CommonClassType, SourceClassType>)
|
||||
{
|
||||
if (! doUIDsMatch (targetIID, CommonClassType::iid))
|
||||
return {};
|
||||
|
||||
return { Steinberg::kResultOk, static_cast<CommonClassType*> (static_cast<SourceClassType*> (std::addressof (toTest))) };
|
||||
}
|
||||
|
||||
template <typename ToTest, typename ClassType>
|
||||
InterfaceResultWithDeferredAddRef testFor (ToTest& toTest,
|
||||
const Steinberg::TUID targetIID,
|
||||
UniqueBase<ClassType>)
|
||||
{
|
||||
return testFor (toTest, targetIID, SharedBase<ClassType, ClassType>{});
|
||||
}
|
||||
|
||||
template <typename ToTest>
|
||||
InterfaceResultWithDeferredAddRef testForMultiple (ToTest&, const Steinberg::TUID) { return {}; }
|
||||
|
||||
template <typename ToTest, typename Head, typename... Tail>
|
||||
InterfaceResultWithDeferredAddRef testForMultiple (ToTest& toTest, const Steinberg::TUID targetIID, Head head, Tail... tail)
|
||||
{
|
||||
const auto result = testFor (toTest, targetIID, head);
|
||||
|
||||
if (result.isOk())
|
||||
return result;
|
||||
|
||||
return testForMultiple (toTest, targetIID, tail...);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if VST_VERSION < 0x030608
|
||||
#define kAmbi1stOrderACN kBFormat
|
||||
|
|
|
|||
|
|
@ -552,16 +552,15 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0
|
|||
return kResultOk;
|
||||
}
|
||||
|
||||
TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler2)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler3)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IContextMenuTarget)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IHostApplication)
|
||||
TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IUnitHandler)
|
||||
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (iid, FUnknown, Vst::IComponentHandler)
|
||||
|
||||
*obj = nullptr;
|
||||
return kNotImplemented;
|
||||
return testForMultiple (*this,
|
||||
iid,
|
||||
UniqueBase<Vst::IComponentHandler>{},
|
||||
UniqueBase<Vst::IComponentHandler2>{},
|
||||
UniqueBase<Vst::IComponentHandler3>{},
|
||||
UniqueBase<Vst::IContextMenuTarget>{},
|
||||
UniqueBase<Vst::IHostApplication>{},
|
||||
UniqueBase<Vst::IUnitHandler>{},
|
||||
SharedBase<FUnknown, Vst::IComponentHandler>{}).extract (obj);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -2643,11 +2642,10 @@ public:
|
|||
|
||||
tresult PLUGIN_API queryInterface (const TUID queryIid, void** obj) override
|
||||
{
|
||||
TEST_FOR_AND_RETURN_IF_VALID (queryIid, Vst::IAttributeList)
|
||||
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (queryIid, FUnknown, Vst::IAttributeList)
|
||||
|
||||
*obj = nullptr;
|
||||
return kNotImplemented;
|
||||
return testForMultiple (*this,
|
||||
queryIid,
|
||||
UniqueBase<Vst::IAttributeList>{},
|
||||
SharedBase<FUnknown, Vst::IAttributeList>{}).extract (obj);
|
||||
}
|
||||
|
||||
tresult PLUGIN_API setInt (AttrID, Steinberg::int64) override { return kOutOfMemory; }
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@
|
|||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "utilities/juce_VSTCallbackHandler.h"
|
||||
#include "utilities/juce_VST3ClientExtensions.h"
|
||||
#include "utilities/juce_ExtensionsVisitor.h"
|
||||
#include "processors/juce_AudioProcessorEditor.h"
|
||||
#include "processors/juce_AudioProcessorListener.h"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
// Forward declaration to avoid leaking implementation details.
|
||||
namespace Steinberg
|
||||
{
|
||||
class FUnknown;
|
||||
using TUID = char[16];
|
||||
}
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** An interface to allow an AudioProcessor to implement extended VST3-specific
|
||||
functionality.
|
||||
|
||||
To use this class, ensure that your AudioProcessor publicly inherits
|
||||
from VST3ClientExtensions.
|
||||
|
||||
@see VSTCallbackHandler
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct VST3ClientExtensions
|
||||
{
|
||||
virtual ~VST3ClientExtensions() = default;
|
||||
|
||||
/** This function may be used by implementations of queryInterface()
|
||||
in the VST3's implementation of IEditController to return
|
||||
additional supported interfaces.
|
||||
*/
|
||||
virtual int32_t queryIEditController (const Steinberg::TUID, void** obj)
|
||||
{
|
||||
*obj = nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** This function may be used by implementations of queryInterface()
|
||||
in the VST3's implementation of IAudioProcessor to return
|
||||
additional supported interfaces.
|
||||
*/
|
||||
virtual int32_t queryIAudioProcessor (const Steinberg::TUID, void** obj)
|
||||
{
|
||||
*obj = nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** This may be called by the VST3 wrapper when the host sets an
|
||||
IComponentHandler for the plugin to use.
|
||||
|
||||
You should not make any assumptions about how and when this will be
|
||||
called - this function may not be called at all!
|
||||
*/
|
||||
virtual void setIComponentHandler (Steinberg::FUnknown*) {}
|
||||
|
||||
/** This may be called shortly after the AudioProcessor is constructed
|
||||
with the current IHostApplication.
|
||||
|
||||
You should not make any assumptions about how and when this will be
|
||||
called - this function may not be called at all!
|
||||
*/
|
||||
virtual void setIHostApplication (Steinberg::FUnknown*) {}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -29,6 +29,11 @@ namespace juce
|
|||
/** An interface to allow an AudioProcessor to send and receive VST specific calls from
|
||||
the host.
|
||||
|
||||
To use this class, ensure that your AudioProcessor publicly inherits
|
||||
from VSTCallbackHandler.
|
||||
|
||||
@see VST3ClientExtensions
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
struct VSTCallbackHandler
|
||||
Loading…
Add table
Add a link
Reference in a new issue