Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,51 @@
if(NOT SMTG_LINUX)
set(target aax_wrapper)
set(${target}_sources
${SDK_ROOT}/public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp
aaxentry.cpp
aaxlibrary.cpp
aaxwrapper.cpp
aaxwrapper.h
aaxwrapper_description.h
aaxwrapper_gui.cpp
aaxwrapper_gui.h
aaxwrapper_parameters.cpp
aaxwrapper_parameters.h
resource/PlugIn.ico
)
add_library(${target} STATIC ${${target}_sources})
target_include_directories(${target}
PRIVATE
"${SMTG_AAX_SDK_PATH}/Interfaces"
"${SMTG_AAX_SDK_PATH}/Interfaces/ACF"
"${SMTG_AAX_SDK_PATH}/Libs/AAXLibrary/Include"
)
target_link_libraries(${target}
PRIVATE
base
)
smtg_target_setup_universal_binary(${target})
target_compile_features(aax_wrapper
PUBLIC
cxx_std_17
)
if(XCODE)
add_compile_options(-Wno-incompatible-ms-struct)
elseif(SMTG_WIN)
# too much warnings in the AAX SDK!!
add_compile_options(/wd4996)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_compile_options(/GR)
if(MSVC)
target_compile_options(${target}
PRIVATE
/wd4127 # conditional expression is constant
/wd5033 # 'register' is no longer a supported storage class
)
endif(MSVC)
endif(XCODE)
endif()
@@ -0,0 +1,213 @@
//------------------------------------------------------------------------
// Flags : clang-format SMTGSequencer
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxentry.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
/**
* plugin entry from AAX_Exports.cpp
*/
//-----------------------------------------------------------------------------
#include "pluginterfaces/base/fplatform.h"
// change names to avoid different linkage
#define ACFRegisterPlugin ACFRegisterPlugin_
#define ACFRegisterComponent ACFRegisterComponent_
#define ACFGetClassFactory ACFGetClassFactory_
#define ACFCanUnloadNow ACFCanUnloadNow_
#define ACFStartup ACFStartup_
#define ACFShutdown ACFShutdown_
//#define INITACFIDS // Make sure all of the AVX2 uids are defined.
#include "AAX.h"
#include "AAX_Init.h"
#include "acfresult.h"
#include "acfunknown.h"
#undef ACFRegisterPlugin
#undef ACFRegisterComponent
#undef ACFGetClassFactory
#undef ACFCanUnloadNow
#undef ACFStartup
#undef ACFShutdown
// defined in basewrapper.cpp
extern bool _InitModule ();
extern bool _DeinitModule ();
// reference this in the plugin to force inclusion of the wrapper in the link
int AAXWrapper_linkAnchor;
//------------------------------------------------------------------------
#if defined(__GNUC__)
#define AAX_EXPORT extern "C" __attribute__ ((visibility ("default"))) ACFRESULT
#else
#define AAX_EXPORT extern "C" __declspec (dllexport) ACFRESULT __stdcall
#endif
AAX_EXPORT ACFRegisterPlugin (IACFUnknown* pUnkHost, IACFPluginDefinition** ppPluginDefinition);
AAX_EXPORT ACFRegisterComponent (IACFUnknown* pUnkHost, acfUInt32 index,
IACFComponentDefinition** ppComponentDefinition);
AAX_EXPORT ACFGetClassFactory (IACFUnknown* pUnkHost, const acfCLSID& clsid, const acfIID& iid,
void** ppOut);
AAX_EXPORT ACFCanUnloadNow (IACFUnknown* pUnkHost);
AAX_EXPORT ACFStartup (IACFUnknown* pUnkHost);
AAX_EXPORT ACFShutdown (IACFUnknown* pUnkHost);
AAX_EXPORT ACFGetSDKVersion (acfUInt64* oSDKVersion);
//------------------------------------------------------------------------
// \func ACFRegisterPlugin
// \brief Determines the number of components defined in the dll.
//
ACFAPI ACFRegisterPlugin (IACFUnknown* pUnkHostVoid, IACFPluginDefinition** ppPluginDefinitionVoid)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXRegisterPlugin (pUnkHostVoid, ppPluginDefinitionVoid);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFRegisterComponent
// \brief Registers a specific component in the DLL.
//
ACFAPI ACFRegisterComponent (IACFUnknown* pUnkHost, acfUInt32 index,
IACFComponentDefinition** ppComponentDefinition)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXRegisterComponent (pUnkHost, index, ppComponentDefinition);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFGetClassFactory
// \brief Gets the factory for a given class ID.
//
ACFAPI ACFGetClassFactory (IACFUnknown* pUnkHost, const acfCLSID& clsid, const acfIID& iid,
void** ppOut)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXGetClassFactory (pUnkHost, clsid, iid, ppOut);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFCanUnloadNow
// \brief Figures out if all objects are released so we can unload.
//
ACFAPI ACFCanUnloadNow (IACFUnknown* pUnkHost)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXCanUnloadNow (pUnkHost);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFStartup
// \brief Called once at init time.
//
ACFAPI ACFStartup (IACFUnknown* pUnkHost)
{
ACFRESULT result = ACF_OK;
try
{
result = AAXStartup (pUnkHost);
if (result == ACF_OK)
{
if (!_InitModule ())
{
AAXShutdown (pUnkHost);
result = ACF_E_UNEXPECTED;
}
}
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
// \func ACFShutdown
// \brief Called once at termination of dll.
//
ACFAPI ACFShutdown (IACFUnknown* pUnkHost)
{
ACFRESULT result = ACF_OK;
try
{
_DeinitModule ();
result = AAXShutdown (pUnkHost);
}
catch (...)
{
result = ACF_E_UNEXPECTED;
}
return result;
}
//------------------------------------------------------------------------
ACFAPI ACFGetSDKVersion (acfUInt64* oSDKVersion)
{
return AAXGetSDKVersion (oSDKVersion);
}
/// \endcond
@@ -0,0 +1,95 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxlibrary.cpp
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
// instead of linking to a library, we just include the sources here to have
// full control over compile settings
#define I18N_LIB 1
#define PLUGIN_SDK_BUILD 1
#define DPA_PLUGIN_BUILD 1
#define INITACFIDS // Make sure all of the AVX2 uids are defined.
#define UNICODE 1
#ifdef _WIN32
#ifndef WIN32
#define WIN32 // for CMutex.cpp
#endif
#define WINDOWS_VERSION 1 // for AAXWrapper_GUI.h
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreorder"
#pragma clang diagnostic ignored "-Wundef-prefix"
#endif
#include "AAX_Atomic.h"
#include "../Interfaces/ACF/CACFClassFactory.cpp"
#include "../Libs/AAXLibrary/source/AAX_CACFUnknown.cpp"
#include "../Libs/AAXLibrary/source/AAX_CChunkDataParser.cpp"
#include "../Libs/AAXLibrary/source/AAX_CEffectDirectData.cpp"
#include "../Libs/AAXLibrary/source/AAX_CEffectGUI.cpp"
#include "../Libs/AAXLibrary/source/AAX_CEffectParameters.cpp"
#include "../Libs/AAXLibrary/source/AAX_CHostProcessor.cpp"
#include "../Libs/AAXLibrary/source/AAX_CHostServices.cpp"
#include "../Libs/AAXLibrary/source/AAX_CMutex.cpp"
#include "../Libs/AAXLibrary/source/AAX_CPacketDispatcher.cpp"
#include "../Libs/AAXLibrary/source/AAX_CParameter.cpp"
#include "../Libs/AAXLibrary/source/AAX_CParameterManager.cpp"
#include "../Libs/AAXLibrary/source/AAX_CString.cpp"
#include "../Libs/AAXLibrary/source/AAX_CUIDs.cpp"
#include "../Libs/AAXLibrary/source/AAX_CommonConversions.cpp"
#include "../Libs/AAXLibrary/source/AAX_IEffectDirectData.cpp"
#include "../Libs/AAXLibrary/source/AAX_IEffectGUI.cpp"
#include "../Libs/AAXLibrary/source/AAX_IEffectParameters.cpp"
#include "../Libs/AAXLibrary/source/AAX_IHostProcessor.cpp"
#include "../Libs/AAXLibrary/source/AAX_Init.cpp"
#include "../Libs/AAXLibrary/source/AAX_Properties.cpp"
#include "../Libs/AAXLibrary/source/AAX_VAutomationDelegate.cpp"
#include "../Libs/AAXLibrary/source/AAX_VCollection.cpp"
#include "../Libs/AAXLibrary/source/AAX_VComponentDescriptor.cpp"
#include "../Libs/AAXLibrary/source/AAX_VController.cpp"
#include "../Libs/AAXLibrary/source/AAX_VDescriptionHost.cpp"
#include "../Libs/AAXLibrary/source/AAX_VEffectDescriptor.cpp"
#include "../Libs/AAXLibrary/source/AAX_VFeatureInfo.cpp"
#include "../Libs/AAXLibrary/source/AAX_VHostProcessorDelegate.cpp"
#include "../Libs/AAXLibrary/source/AAX_VHostServices.cpp"
#include "../Libs/AAXLibrary/source/AAX_VPageTable.cpp"
#include "../Libs/AAXLibrary/source/AAX_VPrivateDataAccess.cpp"
#include "../Libs/AAXLibrary/source/AAX_VPropertyMap.cpp"
#include "../Libs/AAXLibrary/source/AAX_VTransport.cpp"
#include "../Libs/AAXLibrary/source/AAX_VViewContainer.cpp"
#ifdef _WIN32
#include "../Libs/AAXLibrary/source/AAX_CAutoreleasePool.Win.cpp"
#else
//#include "../Libs/AAXLibrary/source/AAX_CAutoreleasePool.OSX.mm"
#endif
#undef min
#undef max
// put at the very end, uses "using namespace std"
#include "../Libs/AAXLibrary/source/AAX_SliderConversions.cpp"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
/// \endcond
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,178 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "public.sdk/source/vst/basewrapper/basewrapper.h"
#include "base/thread/include/flock.h"
#include <bitset>
#include <list>
#include <memory>
struct AAX_Plugin_Desc;
struct AAX_Effect_Desc;
class AAX_IComponentDescriptor;
class AAXWrapper_Parameters;
class AAXWrapper_GUI;
namespace Steinberg {
namespace Vst {
class IAudioProcessor;
class IEditController;
}
}
struct AAXWrapper_Context
{
void* ptr[1]; // array of numDataPointers pointers
};
//------------------------------------------------------------------------
class AAXWrapper : public Steinberg::Vst::BaseWrapper,
public Steinberg::Vst::IComponentHandler2,
public Steinberg::Vst::IVst3ToAAXWrapper
{
public:
// static creation method (will owned factory)
static AAXWrapper* create (Steinberg::IPluginFactory* factory,
const Steinberg::TUID vst3ComponentID, AAX_Plugin_Desc* desc,
AAXWrapper_Parameters* p);
AAXWrapper (Steinberg::Vst::BaseWrapper::SVST3Config& config, AAXWrapper_Parameters* p, AAX_Plugin_Desc* desc);
~AAXWrapper ();
//--- VST 3 Interfaces ------------------------------------------------------
// IHostApplication
Steinberg::tresult PLUGIN_API getName (Steinberg::Vst::String128 name) SMTG_OVERRIDE;
// IComponentHandler
Steinberg::tresult PLUGIN_API beginEdit (Steinberg::Vst::ParamID tag) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API performEdit (
Steinberg::Vst::ParamID tag, Steinberg::Vst::ParamValue valueNormalized) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API endEdit (Steinberg::Vst::ParamID tag) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API restartComponent (Steinberg::int32 flags) SMTG_OVERRIDE;
// IComponentHandler2
Steinberg::tresult PLUGIN_API setDirty (Steinberg::TBool state) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API requestOpenEditor (
Steinberg::FIDString name = Steinberg::Vst::ViewType::kEditor) SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API startGroupEdit () SMTG_OVERRIDE;
Steinberg::tresult PLUGIN_API finishGroupEdit () SMTG_OVERRIDE;
// FUnknown
DEF_INTERFACES_2 (Steinberg::Vst::IComponentHandler2, Steinberg::Vst::IVst3ToAAXWrapper, BaseWrapper);
REFCOUNT_METHODS (BaseWrapper);
// AAXWrapper_Parameters callbacks
void setGUI (AAXWrapper_GUI* gui) { mAAXGUI = gui; }
Steinberg::int32 /*AAX_Result*/ getParameterInfo (const char* aaxId,
Steinberg::Vst::ParameterInfo& paramInfo);
Steinberg::int32 /*AAX_Result*/ ResetFieldData (Steinberg::int32 index, void* inData,
Steinberg::uint32 inDataSize);
Steinberg::int32 Process (AAXWrapper_Context* instance);
Steinberg::uint32 getNumMIDIports () const { return mCountMIDIports; }
void setSideChainEnable (bool enable);
bool generatePageTables (const char* outputFile);
void setRenderingOffline (bool val);
static void DescribeAlgorithmComponent (AAX_IComponentDescriptor* outDesc,
const AAX_Effect_Desc* desc,
const AAX_Plugin_Desc* pdesc);
//--- ---------------------------------------------------------------------
Steinberg::uint32 getNumAAXOutputs () const { return mAAXOutputs; }
//------------------------------------------------------------------------
// BaseWrapper overrides ---------------------------------
//------------------------------------------------------------------------
bool init () SMTG_OVERRIDE;
bool _sizeWindow (Steinberg::int32 width, Steinberg::int32 height) SMTG_OVERRIDE;
void onTimer (Steinberg::Timer* timer) SMTG_OVERRIDE;
Steinberg::int32 _getChunk (void** data, bool isPreset) SMTG_OVERRIDE;
Steinberg::int32 _setChunk (void* data, Steinberg::int32 byteSize, bool isPreset) SMTG_OVERRIDE;
void setupProcessTimeInfo () SMTG_OVERRIDE;
//------------------------------------------------------------------------
private:
void processOutputParametersChanges () SMTG_OVERRIDE;
Steinberg::tresult setupBusArrangements (AAX_Plugin_Desc* desc);
Steinberg::int32 countSidechainBusChannels (Steinberg::Vst::BusDirection dir,
Steinberg::uint64& scBusBitset);
void guessActiveOutputs (float** out, Steinberg::uint32 num);
void updateActiveOutputState ();
AAXWrapper_Parameters* mAAXParams = nullptr;
AAXWrapper_GUI* mAAXGUI = nullptr;
Steinberg::uint32 mAAXOutputs = 0;
Steinberg::Base::Thread::FLock mSyncCalls; // synchronize calls expected in the same thread in VST3
AAX_Plugin_Desc* mPluginDesc = nullptr;
Steinberg::uint32 mCountMIDIports = 0;
// as of ProTools 12 (?) the context struct does no longer allow unused slots,
// so we have to generate indices into the context struct dynamically
// context pointer to AAXWrapper always first
static const Steinberg::int32 idxContext = 0;
static const Steinberg::int32 idxBufferSize = 1;
Steinberg::int32 idxInputChannels = -1;
Steinberg::int32 idxOutputChannels = -1;
Steinberg::int32 idxSideChainInputChannels = -1;
Steinberg::int32 idxMidiPorts = -1;
Steinberg::int32 idxAuxOutputs = -1;
Steinberg::int32 idxMeters = -1;
Steinberg::int32 numDataPointers = 0;
static const Steinberg::int32 maxActiveChannels = 128;
std::bitset<maxActiveChannels> mActiveChannels;
std::bitset<maxActiveChannels> mPropagatedChannels;
Steinberg::uint32 mCntMeters = 0;
std::unique_ptr<Steinberg::Vst::ParamID[]> mMeterIds;
struct GetChunkMessage;
void* mainThread = nullptr;
Steinberg::Base::Thread::FLock msgQueueLock;
std::list<GetChunkMessage*> msgQueue;
float mBypassGain = 1.0;
float* mMetersTmp = nullptr;
Steinberg::Vst::TQuarterNotes mLastPpqPos = 0;
Steinberg::Vst::TQuarterNotes mNextPpqPos = 0;
bool mWantsSetChunk = false;
bool mSettingChunk = false;
bool mSimulateBypass = false;
bool mBypass = false;
bool mPresetChanged = false;
bool mBypassBeforePresetChanged = false;
bool mWantsSetChunkIsPreset = false;
friend class AAXWrapper_Parameters;
friend class AAXWrapper_GUI;
};
/// \endcond
@@ -0,0 +1,84 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_description.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "base/source/fstring.h"
using namespace Steinberg;
struct AAX_Aux_Desc
{
const char* mName;
int32 mChannels; // -1 for same as output channel
};
struct AAX_Meter_Desc
{
const char* mName;
uint32 mID;
uint32 mOrientation; // see AAX_EMeterOrientation
uint32 mType; // see AAX_EMeterType
};
struct AAX_MIDI_Desc
{
const char* mName;
uint32 mMask;
};
struct AAX_Plugin_Desc
{
const char* mEffectID; // unique for each channel layout as in "com.steinberg.aaxwrapper.mono"
const char* mName;
uint32 mPlugInIDNative; // unique for each channel layout
uint32 mPlugInIDAudioSuite; // unique for each channel layout
int32 mInputChannels;
int32 mOutputChannels;
int32 mSideChainInputChannels;
AAX_MIDI_Desc* mMIDIports;
AAX_Aux_Desc* mAuxOutputChannels; // zero terminated
AAX_Meter_Desc* mMeters;
uint32 mLatency;
};
struct AAX_Effect_Desc
{
const char* mManufacturer;
const char* mProduct;
uint32 mManufacturerID;
uint32 mProductID;
const char* mCategory;
TUID mVST3PluginID;
uint32 mVersion;
const char* mPageFile;
AAX_Plugin_Desc* mPluginDesc;
};
// reference this in the Plug-In to force inclusion of the wrapper in the link
extern int AAXWrapper_linkAnchor;
AAX_Effect_Desc* AAXWrapper_GetDescription (); // to be defined by the Plug-In
/// \endcond
@@ -0,0 +1,135 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.cpp
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundef-prefix"
#endif
#include "aaxwrapper_gui.h"
#include "aaxwrapper.h"
#include "aaxwrapper_parameters.h"
#include "AAX_IViewContainer.h"
using namespace Steinberg;
using namespace Steinberg::Vst;
using namespace Steinberg::Base::Thread;
//------------------------------------------------------------------------
void AAXWrapper_GUI::CreateViewContainer ()
{
if (GetViewContainerType () == AAX_eViewContainer_Type_HWND ||
GetViewContainerType () == AAX_eViewContainer_Type_NSView)
{
mHWND = this->GetViewContainerPtr ();
AAXWrapper* wrapper =
static_cast<AAXWrapper_Parameters*> (GetEffectParameters ())->getWrapper ();
FGuard guard (wrapper->mSyncCalls);
wrapper->setGUI (this);
mInOpen = true;
if (auto* editor = wrapper->getEditor ())
editor->_open (mHWND);
mInOpen = false;
}
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_GUI::GetViewSize (AAX_Point* oEffectViewSize) const
{
oEffectViewSize->horz = 1024;
oEffectViewSize->vert = 768;
auto* that = const_cast<AAXWrapper_GUI*> (this);
auto* params = static_cast<AAXWrapper_Parameters*> (that->GetEffectParameters ());
int32 width, height;
if (params->getWrapper ()->getEditorSize (width, height))
{
oEffectViewSize->horz = static_cast<float> (width);
oEffectViewSize->vert = static_cast<float> (height);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_GUI::SetControlHighlightInfo (AAX_CParamID iParameterID,
AAX_CBoolean /*iIsHighlighted*/,
AAX_EHighlightColor /*iColor*/)
{
AAXWrapper* wrapper =
static_cast<AAXWrapper_Parameters*> (GetEffectParameters ())->getWrapper ();
Vst::ParamID id = getVstParamID (iParameterID);
if (id == kNoParamId)
return AAX_ERROR_INVALID_PARAMETER_ID;
// TODO
wrapper;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
void AAXWrapper_GUI::DeleteViewContainer ()
{
AAXWrapper* wrapper =
static_cast<AAXWrapper_Parameters*> (GetEffectParameters ())->getWrapper ();
wrapper->setGUI (nullptr);
if (auto* editor = wrapper->getEditor ())
editor->_close ();
}
//------------------------------------------------------------------------
// METHOD: CreateViewContents
//------------------------------------------------------------------------
void AAXWrapper_GUI::CreateViewContents ()
{
}
//------------------------------------------------------------------------
bool AAXWrapper_GUI::setWindowSize (AAX_Point& size)
{
if (mInOpen)
mRefreshSize = true; // redo later, resizing might silently not work during opening the UI
if (AAX_IViewContainer* vc = GetViewContainer ())
if (vc->SetViewSize (size) == AAX_SUCCESS)
return true;
return false;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_GUI::TimerWakeup ()
{
if (mRefreshSize)
{
mRefreshSize = false;
AAX_Point size;
if (GetViewSize (&size) == AAX_SUCCESS)
if (!setWindowSize (size))
mRefreshSize = true;
}
return AAX_CEffectGUI::TimerWakeup ();
}
/// \endcond
#ifdef __clang__
#pragma clang diagnostic pop
#endif
@@ -0,0 +1,53 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_gui.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "AAX_CEffectGUI.h"
#include "pluginterfaces/base/fplatform.h"
//==============================================================================
class AAXWrapper_GUI : public AAX_CEffectGUI
{
public:
static AAX_IEffectGUI* AAX_CALLBACK Create ();
AAXWrapper_GUI () = default;
virtual ~AAXWrapper_GUI () = default;
void CreateViewContents () SMTG_OVERRIDE;
void CreateViewContainer () SMTG_OVERRIDE;
void DeleteViewContainer () SMTG_OVERRIDE;
AAX_Result GetViewSize (AAX_Point* oEffectViewSize) const SMTG_OVERRIDE;
AAX_Result SetControlHighlightInfo (AAX_CParamID /* iParameterID */,
AAX_CBoolean /* iIsHighlighted */,
AAX_EHighlightColor /* iColor */) SMTG_OVERRIDE;
AAX_Result TimerWakeup () SMTG_OVERRIDE;
bool setWindowSize (AAX_Point& size); // calback from AAXWrapper
private:
bool mInOpen = false;
bool mRefreshSize = false;
void* mHWND = nullptr;
};
/// \endcond
@@ -0,0 +1,858 @@
//------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.cpp
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "aaxwrapper_parameters.h"
#include "aaxwrapper.h"
#include "aaxwrapper_description.h"
#include "AAX_CBinaryDisplayDelegate.h"
#include "AAX_CBinaryTaperDelegate.h"
#include "AAX_CLinearTaperDelegate.h"
#include "AAX_CNumberDisplayDelegate.h"
#include "AAX_CUnitDisplayDelegateDecorator.h"
#include "../hosting/hostclasses.h"
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/base/futils.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstchannelcontextinfo.h"
using namespace Steinberg;
using namespace Steinberg::Vst;
using namespace Steinberg::Base::Thread;
#define USE_TRACE 1
#if USE_TRACE
#define HAPI AAX_eTracePriorityHost_Normal
#define HLOG AAX_TRACE
#else
#define HAPI 0
#if SMTG_OS_WINDOWS
#define HLOG __noop
#else
#define HLOG(...) \
do \
{ \
} while (false)
#endif
#endif
SMTG_EXPORT_SYMBOL IPluginFactory* PLUGIN_API GetPluginFactory ();
const char* kBypassId = "Byp";
ParameterInfo kParamInfoBypass = {CCONST ('B', 'y', 'p', 0),
STR ("Bypass"),
STR ("Bypass"),
STR (""),
1,
0,
-1,
Vst::ParameterInfo::kCanAutomate | Vst::ParameterInfo::kIsBypass};
//------------------------------------------------------------------------
// AAXWrapper_Parameters
//------------------------------------------------------------------------
AAXWrapper_Parameters::AAXWrapper_Parameters (int32_t plugIndex)
: AAX_CEffectParameters (), mSimulateBypass (false)
{
HLOG (HAPI, "%s", __FUNCTION__);
AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription ();
mPluginDesc = effDesc->mPluginDesc + plugIndex;
mWrapper = AAXWrapper::create (GetPluginFactory (), effDesc->mVST3PluginID, mPluginDesc, this);
if (!mWrapper)
return;
#if DEVELOPMENT
static bool writePagetableFile;
if (writePagetableFile) // use debugger to set variable or jump into function
mWrapper->generatePageTables ("c:/tmp/pagetable.xml");
#endif
// if no VST3 Bypass found then simulate it
mSimulateBypass = (mWrapper->mBypassParameterID == Vst::kNoParamId);
mWrapper->mSimulateBypass = mSimulateBypass;
if (mParamNames.size () < (size_t)mWrapper->mNumParams)
{
mParamNames.resize (mWrapper->mNumParams);
for (size_t i = 0; i < mParamNames.size (); i++)
mParamNames[i].set (mWrapper->mParameterMap[i].vst3ID);
}
}
//------------------------------------------------------------------------
AAXWrapper_Parameters::~AAXWrapper_Parameters ()
{
delete mWrapper;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::EffectInit ()
{
HLOG (HAPI, "%s", __FUNCTION__);
if (AAX_IController* ctrl = Controller ())
{
AAX_CSampleRate sampleRate;
if (ctrl->GetSampleRate (&sampleRate) == AAX_SUCCESS)
mWrapper->_setSampleRate (sampleRate);
if (mWrapper->mProcessor)
ctrl->SetSignalLatency (
static_cast<int32> (mWrapper->mProcessor->getLatencySamples ()));
}
for (uint32 i = 0; i < static_cast<uint32> (mWrapper->mNumParams); i++)
{
AAX_CParamID iParameterID = mParamNames[i];
ParameterInfo paramInfo = {};
if (AAX_Result result = mWrapper->getParameterInfo (iParameterID, paramInfo))
return result;
String title = paramInfo.title;
AAX_IParameter* param = nullptr;
param = NEW AAX_CParameter<double> (
iParameterID, AAX_CString (title), paramInfo.defaultNormalizedValue,
AAX_CLinearTaperDelegate<double> (0, 1),
AAX_CUnitDisplayDelegateDecorator<double> (AAX_CNumberDisplayDelegate<double> (),
AAX_CString (title)),
true);
mParameterManager.AddParameter (param);
}
if (mSimulateBypass)
{
AAX_IParameter* param = NEW AAX_CParameter<bool> (
kBypassId, AAX_CString ("Bypass"), false, AAX_CBinaryTaperDelegate<bool> (),
AAX_CBinaryDisplayDelegate<bool> ("off", "on"), true);
mParameterManager.AddParameter (param);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::ResetFieldData (AAX_CFieldIndex index, void* inData,
uint32_t inDataSize) const
{
HLOG (HAPI, "%s", __FUNCTION__);
return mWrapper->ResetFieldData (index, inData, inDataSize);
}
//------------------------------------------------------------------------
// METHOD: AAX_UpdateMIDINodes
// This will be called by the host if there are MIDI packets that need
// to be handled in the Data Model.
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::UpdateMIDINodes (AAX_CFieldIndex inFieldIndex,
AAX_CMidiPacket& inPacket)
{
HLOG (HAPI, "%s", __FUNCTION__);
AAX_Result result;
result = AAX_SUCCESS;
inFieldIndex;
inPacket;
// Do some MIDI work if necessary.
return result;
}
//------------------------------------------------------------------------
int32 AAXWrapper_Parameters::getParameterInfo (AAX_CParamID aaxId,
Vst::ParameterInfo& paramInfo) const
{
AAX_Result result = mWrapper->getParameterInfo (aaxId, paramInfo);
if (result != AAX_SUCCESS)
{
if (mSimulateBypass && strcmp (aaxId, kBypassId) == 0)
{
paramInfo = kParamInfoBypass;
result = AAX_SUCCESS;
}
}
return result;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetNumberOfParameters (int32_t* oNumControls) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*oNumControls = mWrapper->mNumParams;
if (mSimulateBypass)
*oNumControls += 1;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetMasterBypassParameter (AAX_IString* oIDString) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*oIDString = mSimulateBypass ? kBypassId : AAX_CID (mWrapper->mBypassParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterIsAutomatable (AAX_CParamID iParameterID,
AAX_CBoolean* oAutomatable) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oAutomatable = (paramInfo.flags & ParameterInfo::kCanAutomate) != 0;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterNumberOfSteps (AAX_CParamID iParameterID,
int32_t* oNumSteps) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
if (paramInfo.stepCount == 0)
*oNumSteps = 1024;
else
*oNumSteps = paramInfo.stepCount + 1;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterName (AAX_CParamID iParameterID,
AAX_IString* oName) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oName = String (paramInfo.title);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterNameOfLength (AAX_CParamID iParameterID,
AAX_IString* oName,
int32_t iNameLength) const
{
HLOG (HAPI, "%s(id=%s, len=%d)", __FUNCTION__, iParameterID, iNameLength);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
if (iNameLength >= tstrlen (paramInfo.title))
*oName = String (paramInfo.title);
else
{
if (iNameLength < tstrlen (paramInfo.shortTitle))
paramInfo.shortTitle[iNameLength] = 0;
*oName = String (paramInfo.shortTitle);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double* oValue) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oValue = paramInfo.defaultNormalizedValue;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double iValue)
{
iParameterID;
iValue;
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterType (AAX_CParamID iParameterID,
AAX_EParameterType* oParameterType) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
ParameterInfo paramInfo = {};
if (AAX_Result result = getParameterInfo (iParameterID, paramInfo))
return result;
*oParameterType =
paramInfo.stepCount == 0 ? AAX_eParameterType_Continuous : AAX_eParameterType_Discrete;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterOrientation (
AAX_CParamID iParameterID, AAX_EParameterOrientation* oParameterOrientation) const
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
*oParameterOrientation = AAX_eParameterOrientation_BottomMinTopMax; // we don't care
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameter (AAX_CParamID iParameterID,
AAX_IParameter** /*oParameter*/)
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
SMTG_ASSERT (!"the host is not supposed to retrieve the AAX_IParameter interface");
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterIndex (AAX_CParamID iParameterID,
int32_t* oControlIndex) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
*oControlIndex = mWrapper->mNumParams;
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
*oControlIndex = -1;
int32_t idx = 0;
for (auto& item : mParamNames)
{
if (strcmp (item, iParameterID) == 0)
{
*oControlIndex = idx;
return AAX_SUCCESS;
}
idx++;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterIDFromIndex (int32_t iControlIndex,
AAX_IString* oParameterIDString) const
{
HLOG (HAPI, "%s(idx=%x)", __FUNCTION__, iControlIndex);
if ((size_t)iControlIndex >= mWrapper->mParameterMap.size ())
{
if (mSimulateBypass && iControlIndex == mWrapper->mNumParams)
{
oParameterIDString->Set (kBypassId);
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_INDEX;
}
*oParameterIDString = mParamNames[iControlIndex];
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterValueInfo (AAX_CParamID iParameterID,
int32_t /*iSelector*/,
int32_t* oValue) const
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
*oValue = 0;
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterValueFromString (
AAX_CParamID iParameterID, double* oValue, const AAX_IString& iValueString) const
{
HLOG (HAPI, "%s(id=%s, string=%s)", __FUNCTION__, iParameterID, iValueString.Get ());
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
*oValue = strcmp (iValueString.Get (), "on") == 0;
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
String tmp (iValueString.Get ());
if (mWrapper->mController->getParamValueByString (id, (Vst::TChar*)tmp.text16 (), *oValue) !=
kResultTrue)
return AAX_ERROR_INVALID_PARAMETER_ID;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterStringFromValue (AAX_CParamID iParameterID,
double iValue,
AAX_IString* oValueString,
int32_t maxLength) const
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
oValueString->Set (iValue >= 0.5 ? "on" : "off");
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
String128 tmp = {0};
if (mWrapper->mController->getParamStringByValue (id, iValue, tmp) != kResultTrue)
return AAX_ERROR_INVALID_PARAMETER_ID;
if (maxLength < tstrlen (tmp))
tmp[maxLength] = 0;
*oValueString = String (tmp);
// String str (tmp);
// str.copyTo8 (text, 0, kVstMaxParamStrLen);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterValueString (AAX_CParamID iParameterID,
AAX_IString* oValueString,
int32_t iMaxLength) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
double value;
if (AAX_Result result = GetParameterNormalizedValue (iParameterID, &value))
return result;
return GetParameterStringFromValue (iParameterID, value, oValueString, iMaxLength);
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetParameterNormalizedValue (AAX_CParamID iParameterID,
double* oValuePtr) const
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
*oValuePtr = (mWrapper->mBypass ? 1 : 0);
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
ParamValue value = 0;
if (!mWrapper->getLastParamChange (id, value))
value = mWrapper->mController->getParamNormalized (id);
*oValuePtr = value;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetParameterNormalizedValue (AAX_CParamID iParameterID,
double iValue)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
return AAX_SUCCESS;
return AAX_ERROR_INVALID_PARAMETER_ID;
}
// mWrapper->addParameterChange (id, iValue, 0);
if (auto ad = AutomationDelegate ())
{
// Touch the control, Send that token, Release the control
ad->PostTouchRequest (iParameterID);
ad->PostSetValueRequest (iParameterID, iValue);
ad->PostReleaseRequest (iParameterID);
}
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetParameterNormalizedRelative (AAX_CParamID iParameterID,
double iValue)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
mWrapper->mBypass = (mWrapper->mBypass + iValue >= 0.5);
mWrapper->_setBypass (mWrapper->mBypass);
return AAX_SUCCESS;
}
return AAX_ERROR_INVALID_PARAMETER_ID;
}
ParamValue value = 0;
if (!mWrapper->getLastParamChange (id, value))
value = mWrapper->mController->getParamNormalized (id);
value = value + iValue;
if (value < 0)
value = 0;
else if (value > 1)
value = 1;
SetParameterNormalizedValue (iParameterID, value);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::TouchParameter (AAX_CParamID iParameterID)
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
if (auto ad = AutomationDelegate ())
return ad->PostTouchRequest (iParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::ReleaseParameter (AAX_CParamID iParameterID)
{
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
if (auto ad = AutomationDelegate ())
return ad->PostReleaseRequest (iParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::UpdateParameterTouch (AAX_CParamID iParameterID,
AAX_CBoolean /*iTouchState*/)
{
iParameterID;
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, iParameterID);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::UpdateParameterNormalizedValue (AAX_CParamID iParameterID,
double iValue,
AAX_EUpdateSource iSource)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
Vst::ParamID id = getVstParamID (iParameterID);
if (id == -1)
{
if (mSimulateBypass && strcmp (iParameterID, kBypassId) == 0)
{
mWrapper->mBypass = iValue >= 0.5;
mWrapper->_setBypass (mWrapper->mBypass);
}
else
return AAX_ERROR_INVALID_PARAMETER_ID;
}
else
mWrapper->addParameterChange (id, iValue, 0);
#if 1
return AAX_CEffectParameters::UpdateParameterNormalizedValue (iParameterID, iValue, iSource);
#else
if (AutomationDelegate ())
AutomationDelegate ()->PostCurrentValue (iParameterID, iValue);
// if (AAX_Result result = SetParameterNormalizedValue (iParameterID, iValue))
// return result;
// Now the control has changed
AAX_Result result = mPacketDispatcher.SetDirty (iParameterID);
++mNumPlugInChanges;
return result;
#endif
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::_UpdateParameterNormalizedRelative (AAX_CParamID iParameterID,
double iValue)
{
HLOG (HAPI, "%s(id=%s, value=%lf)", __FUNCTION__, iParameterID, iValue);
if (AAX_Result result = SetParameterNormalizedRelative (iParameterID, iValue))
return result;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::_GenerateCoefficients ()
{
HLOG (HAPI, "%s", __FUNCTION__);
AAX_Result result = mPacketDispatcher.Dispatch ();
return result;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetNumberOfChunks (int32_t* numChunks) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*numChunks = 1;
return AAX_SUCCESS;
}
const AAX_CTypeID AAXWRAPPER_CONTROLS_CHUNK_ID = CCONST ('a', 'w', 'c', 'k');
const char AAXWRAPPER_CONTROLS_CHUNK_DESCRIPTION[] = "AAXWrapper State";
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const
{
HLOG (HAPI, "%s", __FUNCTION__);
if (index != 0)
return AAX_ERROR_INVALID_CHUNK_INDEX;
*chunkID = AAXWRAPPER_CONTROLS_CHUNK_ID;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const
{
HLOG (HAPI, "%s", __FUNCTION__);
if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID)
return AAX_ERROR_INVALID_CHUNK_ID;
FGuard guard (mWrapper->mSyncCalls);
bool isPreset = false;
void* data;
*oSize = static_cast<uint32> (mWrapper->_getChunk (&data, isPreset));
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const
{
HLOG (HAPI, "%s", __FUNCTION__);
if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID)
return AAX_ERROR_INVALID_CHUNK_ID;
FGuard guard (mWrapper->mSyncCalls);
// assume GetChunkSize called before and size of oChunk correct
oChunk->fVersion = 1;
oChunk->fSize = static_cast<int32_t> (mWrapper->mChunk.getSize ());
memcpy (oChunk->fData, mWrapper->mChunk.getData (),
static_cast<size_t> (mWrapper->mChunk.getSize ()));
strncpy (reinterpret_cast<char*> (oChunk->fName), AAXWRAPPER_CONTROLS_CHUNK_DESCRIPTION, 31);
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* iChunk)
{
HLOG (HAPI, "%s", __FUNCTION__);
if (chunkID != AAXWRAPPER_CONTROLS_CHUNK_ID)
return AAX_ERROR_INVALID_CHUNK_ID;
FGuard guard (mWrapper->mSyncCalls);
bool isPreset = mPresetOpened;
mWrapper->_setChunk (const_cast<char*> (iChunk->fData), iChunk->fSize, isPreset);
mPresetOpened = false;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::CompareActiveChunk (const AAX_SPlugInChunk* /*iChunk*/,
AAX_CBoolean* /*oIsEqual*/) const
{
HLOG (HAPI, "%s", __FUNCTION__);
return AAX_ERROR_UNIMPLEMENTED;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::_GetNumberOfChanges (int32_t* oValue) const
{
HLOG (HAPI, "%s", __FUNCTION__);
*oValue = mNumPlugInChanges;
return AAX_SUCCESS;
}
//------------------------------------------------------------------------
void AAXWrapper_Parameters::setDirty (bool state)
{
if (state)
mNumPlugInChanges++;
}
//------------------------------------------------------------------------
AAX_Result AAXWrapper_Parameters::NotificationReceived (AAX_CTypeID iNotificationType,
const void* iNotificationData,
uint32_t iNotificationDataSize)
{
switch (iNotificationType)
{
//--- Tell the plug-in about connection of the sidechain input
case AAX_eNotificationEvent_SideChainBeingConnected:
mWrapper->setSideChainEnable (true);
break;
//--- Tell the plug-in about disconnection of the sidechain
case AAX_eNotificationEvent_SideChainBeingDisconnected:
mWrapper->setSideChainEnable (false);
break;
//--- The host has changed its latency compensation for this plug-in instance.
case AAX_eNotificationEvent_SignalLatencyChanged:
{
int32_t outSample;
Controller ()->GetSignalLatency (&outSample);
if (mPluginDesc)
mPluginDesc->mLatency = static_cast<uint32> (outSample);
if (mWrapper->isActive ())
{
mWrapper->_suspend ();
mWrapper->_resume ();
}
break;
}
//--- Tell the plug-in that chunk data is coming from a TFX
case AAX_eNotificationEvent_PresetOpened:
{
// do not wanted to overwrite the bypass state when loading preset
double value;
if (GetParameterNormalizedValue (
mSimulateBypass ? kBypassId : AAX_CID (mWrapper->mBypassParameterID), &value) ==
AAX_SUCCESS)
mWrapper->mBypassBeforePresetChanged = (value >= 0.5);
mWrapper->mPresetChanged = true;
mPresetOpened = true;
break;
}
//--- Tell the plug-in that chunk data is coming from a PTX
case AAX_eNotificationEvent_SessionBeingOpened:
{
mPresetOpened = false;
break;
}
//--- Entering offline processing mode (i.e.offline bounce)
case AAX_eNotificationEvent_EnteringOfflineMode:
{
mWrapper->setRenderingOffline (true);
break;
}
//--- Exiting offline processing mode (i.e. offline bounce)
case AAX_eNotificationEvent_ExitingOfflineMode:
{
mWrapper->setRenderingOffline (false);
break;
}
//--- A string representing the path of the current session
case AAX_eNotificationEvent_SessionPathChanged:
{
AAX_CString str (*reinterpret_cast<const AAX_IString*> (iNotificationData));
mSessionPath = str.StdString ();
break;
}
//--- The current name of this plug-in instance's track
case AAX_eNotificationEvent_TrackNameChanged:
{
AAX_CString str (*reinterpret_cast<const AAX_IString*> (iNotificationData));
mChannelName = str.StdString ();
if (mWrapper->mController)
{
if (auto iChannelContextInfoListener =
U::cast<Vst::ChannelContext::IInfoListener> (mWrapper->mController))
{
auto list = Vst::HostAttributeList::make ();
String string;
string.fromUTF8 (mChannelName.data ());
list->setString (Vst::ChannelContext::kChannelNameKey, string);
list->setInt (Vst::ChannelContext::kChannelNameLengthKey, string.length ());
iChannelContextInfoListener->setChannelContextInfos (list);
}
}
break;
}
//--- The zero-indexed insert position of this plug-in instance within its track
case AAX_eNotificationEvent_InsertPositionChanged:
{
// auto tmp = *reinterpret_cast<const int32_t*> (iNotificationData);
break;
}
//--- Tell the plug-in the maximum allowed GUI dimensions
case AAX_eNotificationEvent_MaxViewSizeChanged:
{
// auto tmp = *reinterpret_cast<const AAX_Point*> (iNotificationData);
break;
}
}
return AAX_CEffectParameters::NotificationReceived (iNotificationType, iNotificationData,
iNotificationDataSize);
}
/// \endcond
@@ -0,0 +1,157 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper_parameters.h
// Created by : Steinberg, 08/2017
// Description : VST 3 -> AAX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "public.sdk/source/vst/basewrapper/basewrapper.h"
#include "AAX_CEffectParameters.h"
#include "AAX_Push8ByteStructAlignment.h"
class AAXWrapper;
struct AAX_Plugin_Desc;
//------------------------------------------------------------------------
// helper to convert to/from AAX/Vst IDs
struct AAX_CID
{
char str[10] {0};
AAX_CID () {}
AAX_CID (Steinberg::Vst::ParamID id) { set (id); }
void set (Steinberg::Vst::ParamID id) { snprintf (str, 10, "p%lX", static_cast<unsigned long> (id)); }
operator const char* () const { return str; }
};
Steinberg::Vst::ParamID getVstParamID (const char* aaxid);
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wignored-attributes"
#pragma clang diagnostic ignored "-Wincompatible-ms-struct"
#endif
//------------------------------------------------------------------------
class AAXWrapper_Parameters : public AAX_CEffectParameters
{
public:
// Constructor
AAXWrapper_Parameters (int32_t plugIndex);
~AAXWrapper_Parameters ();
static AAX_CEffectParameters* AAX_CALLBACK Create ();
// Overrides from AAX_CEffectParameters
AAX_Result EffectInit () SMTG_OVERRIDE;
AAX_Result ResetFieldData (AAX_CFieldIndex index, void* inData,
uint32_t inDataSize) const SMTG_OVERRIDE;
AAX_Result NotificationReceived (AAX_CTypeID iNotificationType, const void* iNotificationData,
uint32_t iNotificationDataSize) SMTG_OVERRIDE;
/* Parameter information */
AAX_Result GetNumberOfParameters (int32_t* oNumControls) const SMTG_OVERRIDE;
AAX_Result GetMasterBypassParameter (AAX_IString* oIDString) const SMTG_OVERRIDE;
AAX_Result GetParameterIsAutomatable (AAX_CParamID iParameterID,
AAX_CBoolean* oAutomatable) const SMTG_OVERRIDE;
AAX_Result GetParameterNumberOfSteps (AAX_CParamID iParameterID,
int32_t* oNumSteps) const SMTG_OVERRIDE;
AAX_Result GetParameterName (AAX_CParamID iParameterID, AAX_IString* oName) const SMTG_OVERRIDE;
AAX_Result GetParameterNameOfLength (AAX_CParamID iParameterID, AAX_IString* oName,
int32_t iNameLength) const SMTG_OVERRIDE;
AAX_Result GetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double* oValue) const SMTG_OVERRIDE;
AAX_Result SetParameterDefaultNormalizedValue (AAX_CParamID iParameterID,
double iValue) SMTG_OVERRIDE;
AAX_Result GetParameterType (AAX_CParamID iParameterID,
AAX_EParameterType* oParameterType) const SMTG_OVERRIDE;
AAX_Result GetParameterOrientation (AAX_CParamID iParameterID,
AAX_EParameterOrientation* oParameterOrientation) const
SMTG_OVERRIDE;
AAX_Result GetParameter (AAX_CParamID iParameterID, AAX_IParameter** oParameter) SMTG_OVERRIDE;
AAX_Result GetParameterIndex (AAX_CParamID iParameterID,
int32_t* oControlIndex) const SMTG_OVERRIDE;
AAX_Result GetParameterIDFromIndex (int32_t iControlIndex,
AAX_IString* oParameterIDString) const SMTG_OVERRIDE;
AAX_Result GetParameterValueInfo (AAX_CParamID iParameterID, int32_t iSelector,
int32_t* oValue) const SMTG_OVERRIDE;
/** Parameter setters and getters */
AAX_Result GetParameterValueFromString (AAX_CParamID iParameterID, double* oValue,
const AAX_IString& iValueString) const SMTG_OVERRIDE;
AAX_Result GetParameterStringFromValue (AAX_CParamID iParameterID, double iValue,
AAX_IString* oValueString,
int32_t maxLength) const SMTG_OVERRIDE;
AAX_Result GetParameterValueString (AAX_CParamID iParameterID, AAX_IString* oValueString,
int32_t iMaxLength) const SMTG_OVERRIDE;
AAX_Result GetParameterNormalizedValue (AAX_CParamID iParameterID,
double* oValuePtr) const SMTG_OVERRIDE;
AAX_Result SetParameterNormalizedValue (AAX_CParamID iParameterID, double iValue) SMTG_OVERRIDE;
AAX_Result SetParameterNormalizedRelative (AAX_CParamID iParameterID,
double iValue) SMTG_OVERRIDE;
/* Automated parameter helpers */
AAX_Result TouchParameter (AAX_CParamID iParameterID) SMTG_OVERRIDE;
AAX_Result ReleaseParameter (AAX_CParamID iParameterID) SMTG_OVERRIDE;
AAX_Result UpdateParameterTouch (AAX_CParamID iParameterID,
AAX_CBoolean iTouchState) SMTG_OVERRIDE;
/* Asynchronous parameter update methods */
AAX_Result UpdateParameterNormalizedValue (AAX_CParamID iParameterID, double iValue,
AAX_EUpdateSource iSource) SMTG_OVERRIDE;
AAX_Result _UpdateParameterNormalizedRelative (AAX_CParamID iParameterID, double iValue);
AAX_Result _GenerateCoefficients ();
/* Chunk methods */
AAX_Result GetNumberOfChunks (int32_t* numChunks) const SMTG_OVERRIDE;
AAX_Result GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const SMTG_OVERRIDE;
AAX_Result GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const SMTG_OVERRIDE;
AAX_Result GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const SMTG_OVERRIDE;
AAX_Result SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* iChunk) SMTG_OVERRIDE;
AAX_Result CompareActiveChunk (const AAX_SPlugInChunk* iChunk,
AAX_CBoolean* oIsEqual) const SMTG_OVERRIDE;
AAX_Result _GetNumberOfChanges (int32_t* oValue) const;
// Override this method to receive MIDI
// packets for the described MIDI nodes
AAX_Result UpdateMIDINodes (AAX_CFieldIndex inFieldIndex,
AAX_CMidiPacket& inPacket) SMTG_OVERRIDE;
AAXWrapper* getWrapper () { return mWrapper; }
void setDirty (bool state);
private:
Steinberg::int32 getParameterInfo (AAX_CParamID aaxId,
Steinberg::Vst::ParameterInfo& paramInfo) const;
AAXWrapper* mWrapper = nullptr;
std::vector<AAX_CID> mParamNames;
AAX_Plugin_Desc* mPluginDesc = nullptr;
std::string mChannelName;
std::string mSessionPath;
bool mSimulateBypass = false;
bool mPresetOpened = false;
};
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include "AAX_PopStructAlignment.h"
/// \endcond
Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

@@ -0,0 +1,30 @@
set OutDir=%1
if not exist %OutDir%\..\..\Contents mkdir %OutDir%\..\..\Contents
if errorlevel 1 goto err
if not exist %OutDir%\..\..\Contents\Resources mkdir %OutDir%\..\..\Contents\Resources
if errorlevel 1 goto err
echo Copy "aaxwrapperPages.xml"
copy /Y ..\resource\aaxwrapperPages.xml %OutDir%\..\..\Contents\Resources\ > NUL
if errorlevel 1 goto err
attrib -r %OutDir%\..\..
if exist %OutDir%\..\..\PlugIn.ico goto PlugIn_ico_exists
copy /Y ..\resource\PlugIn.ico %OutDir%\..\..\ > NUL
if errorlevel 1 goto err
attrib +h +r +s %OutDir%\..\..\PlugIn.ico
if errorlevel 1 goto err
:PlugIn_ico_exists
if exist %OutDir%\..\..\desktop.ini goto desktop_ini_exists
copy /Y ..\resource\desktop.ini %OutDir%\..\..\ > NUL
if errorlevel 1 goto err
attrib +h +r +s %OutDir%\..\..\desktop.ini
if errorlevel 1 goto err
:desktop_ini_exists
attrib +r %OutDir%\..\..
:err
@@ -0,0 +1,102 @@
// Microsoft Visual C++ generated resource script.
//
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"DemoMIDIResource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 7,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "AAXWrapper Plug-In"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "AAXWrapper.aaxplugin"
VALUE "LegalCopyright", "(c) Steinberg Media Technologies 2020"
VALUE "OriginalFilename", "AAXWrapper.aaxplugin"
VALUE "ProductName", "AAX Wrapper"
VALUE "ProductVersion", "0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
@@ -0,0 +1,135 @@
<?xml version='1.0' encoding='US-ASCII' standalone='yes'?>
<PageTables vers='6.4.0.89'>
<PageTableLayouts>
<Plugin manID='AVID' prodID='DmGn' plugID='DGDR'>
<Desc>DemoGain 1 -&gt; 1 by Avid Inc.</Desc>
<Layout>PageTable 1</Layout>
</Plugin><!--manID='AVID' prodID='DmGn' plugID='DGDR'-->
<PTLayout name='PageTable 1'>
<PageTable type='PgTL' pgsz='1'>
<Page num='1'>
<ID>MasterBypass</ID>
</Page><!--num='1'-->
<Page num='2'>
<ID>Gain</ID>
</Page><!--num='2'-->
</PageTable><!--type='PgTL' pgsz='1'-->
<PageTable type='MkTL' pgsz='8'>
<Page num='1'>
<ID></ID>
<ID>Gain</ID>
<ID>MasterBypass</ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='MkTL' pgsz='8'-->
<PageTable type='PcTL' pgsz='16'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain </ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='PcTL' pgsz='16'-->
<PageTable type='FrTL' pgsz='24'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain</ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='FrTL' pgsz='24'-->
<PageTable type='HgTL' pgsz='8'>
<Page num='1'>
<ID></ID>
<ID>Gain</ID>
<ID>MasterBypass</ID>
</Page><!--num='1'-->
<FirstPg cat='0'>1</FirstPg>
<FirstPg cat='1'>1</FirstPg>
<FirstPg cat='2'>1</FirstPg>
<FirstPg cat='4'>1</FirstPg>
<FirstPg cat='8'>1</FirstPg>
<FirstPg cat='16'>1</FirstPg>
<FirstPg cat='32'>1</FirstPg>
<FirstPg cat='64'>1</FirstPg>
<FirstPg cat='128'>1</FirstPg>
<FirstPg cat='256'>1</FirstPg>
<FirstPg cat='512'>1</FirstPg>
<FirstPg cat='1024'>1</FirstPg>
<FirstPg cat='2048'>1</FirstPg>
</PageTable><!--type='HgTL' pgsz='8'-->
<PageTable type='BkCS' pgsz='12'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain</ID>
</Page><!--num='1'-->
</PageTable><!--type='BkCS' pgsz='12'-->
<PageTable type='BkSF' pgsz='16'>
<Page num='1'>
<ID>MasterBypass</ID>
<ID>Gain</ID>
</Page><!--num='1'-->
</PageTable><!--type='BkSF' pgsz='16'-->
</PTLayout><!--name='PageTable 1'-->
</PageTableLayouts>
<ControlNamesVariations>
<Ctrl ID='Gain'>
<name typ='PgTL' sz='1'>Ga</name>
<name typ='PgTL' sz='3'>Gn </name>
</Ctrl><!--ID='Gain'-->
<Ctrl ID='MasterBypass'>
<name typ='PgTL' sz='1'>Ma</name>
<name typ='PgTL' sz='3'>Byp</name>
<name typ='PgTL' sz='4'>MByp</name>
<name typ='PgTL' sz='8'>Mstr Byp</name>
</Ctrl><!--ID='MasterBypass'-->
</ControlNamesVariations>
<Editor vers='1.1.0.1'>
<PluginList>
<TDM>
</TDM>
<RTAS>
<PluginID manID='AVID' prodID='DmGn' plugID='DGDR'>
<MenuStr>RTAS: DemoGain, 1 in X 1 out</MenuStr>
</PluginID><!--manID='AVID' prodID='DmGn' plugID='DGDR'-->
</RTAS>
</PluginList>
<DiscCtrls>
<CtrlID>MasterBypass</CtrlID>
</DiscCtrls>
</Editor><!--vers='1.1.0.1'-->
</PageTables><!--vers='6.4.0.89'-->
@@ -0,0 +1,5 @@
[.ShellClassInfo]
IconResource=PlugIn.ico,0
;For compatibility with Windows XP
IconFile=PlugIn.ico
IconIndex=0
@@ -0,0 +1,21 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename :
// Created by : Steinberg, 03/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import <Cocoa/Cocoa.h>
int main (int argc, const char* argv[])
{
return NSApplicationMain (argc, argv);
}
@@ -0,0 +1,75 @@
include(SMTG_AddVST3AuV3)
# iOS target
if(SMTG_MAC)
if(XCODE)
set(auv3wrapperlib_sources
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.mm
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3Wrapper.h
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.mm
${SDK_ROOT}/public.sdk/source/vst/auv3wrapper/Shared/AUv3AudioEngine.h
${SDK_ROOT}/public.sdk/source/vst/auwrapper/NSDataIBStream.mm
${SDK_ROOT}/public.sdk/source/vst/auwrapper/NSDataIBStream.h
${SDK_ROOT}/public.sdk/source/vst/utility/mpeprocessor.cpp
${SDK_ROOT}/public.sdk/source/vst/utility/mpeprocessor.h
)
# --------------------------------------------------------------------------------------------------------
set(target auv3_wrapper_macos)
add_library(${target}
STATIC
${auv3wrapperlib_sources}
)
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER} XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
)
target_compile_features(${target}
PUBLIC
cxx_std_17
)
target_link_libraries(${target}
PRIVATE
sdk_hosting
)
if (SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY)
target_compile_definitions(${target}
PRIVATE
SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY=1)
endif()
# --------------------------------------------------------------------------------------------------------
if(SMTG_ENABLE_IOS_TARGETS)
set(target auv3_wrapper_ios)
add_library(${target}
STATIC
${auv3wrapperlib_sources}
)
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER} XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
)
set_target_properties(${target}
PROPERTIES
LINK_FLAGS "-Wl,-F/Library/Frameworks"
)
smtg_target_set_platform_ios(${target})
target_compile_features(${target}
PUBLIC
cxx_std_17
)
target_link_libraries(${target}
PRIVATE
sdk_hosting_ios
)
if (SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY)
target_compile_definitions(${target}
PRIVATE
SMTG_AUV3_WRAPPER_EXTERNAL_PLUGIN_FACTORY=1
)
endif()
endif()
else()
message("* To enable building the AUv3 Wrapper example for iOS you need to set the SMTG_IOS_DEVELOPMENT_TEAM and use the Xcode generator")
endif()
endif()
@@ -0,0 +1,31 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : AUv3AudioEngine.h
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#import <AVFoundation/AVFoundation.h>
@interface AUv3AudioEngine : NSObject
@property (assign) AUAudioUnit* currentAudioUnit;
- (NSError*)loadAudioFile:(NSURL*)url;
- (instancetype)initWithComponentType:(uint32_t)unitComponentType;
- (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
completion:(void (^) (void))completionBlock;
- (BOOL)startStop;
- (void)shutdown;
@end
@@ -0,0 +1,424 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : AUv3AudioEngine.mm
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "AUv3AudioEngine.h"
#import <CoreMIDI/CoreMIDI.h>
#import <functional>
#import <vector>
#import <utility>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class MidiIO
{
public:
using ReadCallback = std::function<void (const MIDIPacketList* pktlist)>;
MidiIO (ReadCallback&& callback) : readCallback (std::move (callback)) { activate (); }
~MidiIO () = default;
private:
bool activate ();
bool deactivate ();
void onSourceAdded (MIDIObjectRef source);
void onSetupChanged ();
void disconnectSources ();
void onInput (const MIDIPacketList* pktlist);
static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon);
static void notifyProc (const MIDINotification* message, void* refCon);
MIDIClientRef client {0};
MIDIPortRef inputPort {0};
MIDIEndpointRef destPort {0};
ReadCallback readCallback;
using ConnectionList = std::vector<MIDIEndpointRef>;
ConnectionList connectedSources;
};
//------------------------------------------------------------------------
bool MidiIO::activate ()
{
if (client)
return true;
OSStatus err;
NSString* name = [[NSBundle mainBundle] bundleIdentifier];
if ((err = MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr))
return false;
if ((err = MIDIInputPortCreate (client, CFSTR ("Input"), readProc, this, &inputPort) != noErr))
{
MIDIClientDispose (client);
client = 0;
return false;
}
name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleName"];
if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this,
&destPort) != noErr))
{
MIDIPortDispose (inputPort);
inputPort = 0;
MIDIClientDispose (client);
client = 0;
return false;
}
onSetupChanged ();
return true;
}
//------------------------------------------------------------------------
bool MidiIO::deactivate ()
{
if (client == 0)
return true;
disconnectSources ();
auto status = MIDIEndpointDispose (destPort);
destPort = 0;
status |= MIDIPortDispose (inputPort);
inputPort = 0;
status |= MIDIClientDispose (client);
client = 0;
return status == noErr;
}
//------------------------------------------------------------------------
void MidiIO::onSourceAdded (MIDIObjectRef source)
{
connectedSources.push_back ((MIDIEndpointRef)source);
MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL);
}
//------------------------------------------------------------------------
void MidiIO::onSetupChanged ()
{
disconnectSources ();
ItemCount numSources = MIDIGetNumberOfSources ();
for (ItemCount i = 0; i < numSources; i++)
{
onSourceAdded (MIDIGetSource (i));
}
}
//------------------------------------------------------------------------
void MidiIO::disconnectSources ()
{
for (auto source : connectedSources)
MIDIPortDisconnectSource (inputPort, source);
connectedSources.clear ();
}
//------------------------------------------------------------------------
void MidiIO::onInput (const MIDIPacketList* pktlist)
{
if (readCallback)
readCallback (pktlist);
}
//------------------------------------------------------------------------
void MidiIO::readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon)
{
MidiIO* io = static_cast<MidiIO*> (readProcRefCon);
io->onInput (pktlist);
}
//------------------------------------------------------------------------
void MidiIO::notifyProc (const MIDINotification* message, void* refCon)
{
if (message->messageID == kMIDIMsgSetupChanged)
{
MidiIO* mio = (MidiIO*)refCon;
mio->onSetupChanged ();
}
}
using MidiIOPtr = std::unique_ptr<MidiIO>;
//------------------------------------------------------------------------
} // Vst
} // Steinberg
//------------------------------------------------------------------------
@implementation AUv3AudioEngine
{
AVAudioEngine* audioEngine;
AVAudioFile* audioFile;
AVAudioPlayerNode* playerNode;
AVAudioUnit* avAudioUnit;
Steinberg::Vst::MidiIOPtr midi;
UInt32 componentType;
BOOL playing;
BOOL isDone;
}
//------------------------------------------------------------------------
- (instancetype)initWithComponentType:(uint32_t)unitComponentType
{
self = [super init];
isDone = false;
if (self)
{
audioEngine = [[AVAudioEngine alloc] init];
componentType = unitComponentType;
}
#if TARGET_OS_IPHONE
NSError* error = nil;
BOOL success =
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (NO == success)
{
NSLog (@"Error setting category: %@", [error localizedDescription]);
}
#endif
playing = false;
return self;
}
//------------------------------------------------------------------------
- (void)shutdown
{
if (playing)
[self stopPlaying];
audioEngine = nil;
midi.reset ();
}
//------------------------------------------------------------------------
- (void)onAudioUnitInstantiated:(AVAudioUnit* __nullable)audioUnit
error:(NSError* __nullable)error
completion:(void (^) (void))completionBlock
{
if (audioUnit == nil)
return;
avAudioUnit = audioUnit;
_currentAudioUnit = avAudioUnit.AUAudioUnit;
[audioEngine attachNode:avAudioUnit];
[audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat];
completionBlock ();
}
//------------------------------------------------------------------------
- (void)loadAudioUnitWithComponentDescription:(AudioComponentDescription)desc
completion:(void (^) (void))completionBlock
{
[AVAudioUnit instantiateWithComponentDescription:desc
options:0
completionHandler:^(AVAudioUnit* __nullable audioUnit,
NSError* __nullable error) {
[self onAudioUnitInstantiated:audioUnit
error:error
completion:completionBlock];
}];
if (componentType == kAudioUnitType_MusicDevice)
{
midi = Steinberg::Vst::MidiIOPtr (
new Steinberg::Vst::MidiIO ([=] (const MIDIPacketList* pktlist) {
[self scheduleMIDIPackets:pktlist];
}));
}
}
//------------------------------------------------------------------------
- (NSError*)loadAudioFile:(NSURL*)url
{
BOOL isPlaying = playing;
if (isPlaying)
[self startStop];
if (playerNode)
{
[playerNode stop];
[audioEngine detachNode:playerNode];
}
NSError* error = nil;
audioFile = [[AVAudioFile alloc] initForReading:url error:&error];
if (error)
return error;
[audioEngine detachNode:avAudioUnit];
[audioEngine attachNode:avAudioUnit];
[audioEngine connect:avAudioUnit to:audioEngine.outputNode format:audioFile.processingFormat];
playerNode = [[AVAudioPlayerNode alloc] init];
[audioEngine attachNode:playerNode];
[audioEngine connect:playerNode to:avAudioUnit format:audioFile.processingFormat];
if (isPlaying)
[self startStop];
return nil;
}
//------------------------------------------------------------------------
- (BOOL)startStop
{
playing = !playing;
playing ? ([self startPlaying]) : ([self stopPlaying]);
return playing;
}
//------------------------------------------------------------------------
- (void)startPlaying
{
[self activateSession:true];
NSError* error = nil;
if (![audioEngine startAndReturnError:&error])
{
NSLog (@"engine failed to start: %@", error);
return;
}
if (playerNode)
{
[self loopAudioFile];
[playerNode play];
}
}
//------------------------------------------------------------------------
- (void)stopPlaying
{
if (playerNode)
[playerNode stop];
[audioEngine stop];
[self activateSession:false];
}
//------------------------------------------------------------------------
- (void)loopAudioFile
{
if (playerNode)
{
[playerNode scheduleFile:audioFile
atTime:nil
completionHandler:^{
if (playerNode.playing)
[self loopAudioFile];
}];
}
}
//------------------------------------------------------------------------
- (void)scheduleMIDIPackets:(const MIDIPacketList*) pktlist
{
if (!_currentAudioUnit || _currentAudioUnit.scheduleMIDIEventBlock == nil)
return;
auto packet = &pktlist->packet[0];
for (auto i = 0u; i < pktlist->numPackets; i++)
{
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, packet->length,
packet->data);
packet = MIDIPacketNext (packet);
}
}
//------------------------------------------------------------------------
- (void)loopMIDIsequence
{
UInt8 cbytes[3], *cbytesPtr;
cbytesPtr = cbytes;
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
cbytesPtr[0] = 0xB0;
cbytesPtr[1] = 123;
cbytesPtr[2] = 0;
if (_currentAudioUnit.scheduleMIDIEventBlock == nil)
return;
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr);
usleep (useconds_t (0.1 * 1e6));
float releaseTime = 0.05;
usleep (useconds_t (0.1 * 1e6));
int i = 0;
@synchronized (self)
{
while (playing)
{
if (releaseTime < 10.0)
releaseTime = (releaseTime * 1.05) > 10.0 ? (releaseTime * 1.05) : 10.0;
cbytesPtr[0] = 0x90;
cbytesPtr[1] = UInt8 (60 + i);
cbytesPtr[2] = UInt8 (64); // note on
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3,
cbytesPtr);
usleep (useconds_t (0.2 * 1e6));
cbytesPtr[0] = 0x80;
cbytesPtr[1] = UInt8 (60 + i);
cbytesPtr[2] = UInt8 (0); // note off
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3,
cbytesPtr);
i += 2;
if (i >= 24)
{
i = -12;
}
}
cbytesPtr[0] = 0xB0;
cbytesPtr[1] = 123;
cbytesPtr[2] = 0;
_currentAudioUnit.scheduleMIDIEventBlock (AUEventSampleTimeImmediate, 0, 3, cbytesPtr);
isDone = true;
}
});
}
//------------------------------------------------------------------------
- (void)activateSession:(BOOL)active
{
#if TARGET_OS_IPHONE
NSError* error = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive:active error:nil];
if (NO == success)
{
NSLog (@"Error setting category: %@", [error localizedDescription]);
}
#endif
}
@end
@@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : AUv3Wrapper.h
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#import <CoreAudioKit/AUViewController.h>
@class AUv3Wrapper;
//------------------------------------------------------------------------
@interface AUv3WrapperViewController : AUViewController
@property (nonatomic, strong) AUv3Wrapper* audioUnit;
@end
//------------------------------------------------------------------------
@interface AUv3Wrapper : AUAudioUnit
- (void)beginEdit:(int32_t)tag;
- (void)endEdit:(int32_t)tag;
- (void)performEdit:(int32_t)tag value:(double)value;
- (void)syncParameterValues;
- (void)updateParameters;
- (void)onTimer;
- (void)onParamTitlesChanged;
- (void)onNoteExpressionChanged;
- (void)onLatencyChanged;
- (BOOL)enableMPESupport:(BOOL)state;
- (BOOL)setMPEInputDeviceMasterChannel:(NSInteger)masterChannel
memberBeginChannel:(NSInteger)memberBeginChannel
memberEndChannel:(NSInteger)memberEndChannel;
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename :
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#import "AUv3Wrapper.h"
@interface AUv3WrapperViewController (AUAudioUnitFactory) <AUAudioUnitFactory>
@end
@@ -0,0 +1,41 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename :
// Created by : Steinberg, 07/2017.
// Description : VST 3 AUv3Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "AUv3WrapperFactory.h"
@implementation AUv3WrapperViewController (AUAudioUnitFactory)
- (AUv3Wrapper *) createAudioUnitWithComponentDescription:(AudioComponentDescription) desc error:(NSError **)error {
@synchronized (self)
{
if (!self.audioUnit)
{
if (![NSThread isMainThread])
{
dispatch_sync(dispatch_get_main_queue(), [&]{
self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error];
});
}
else
{
self.audioUnit = [[AUv3Wrapper alloc] initWithComponentDescription:desc error:error];
}
}
}
return self.audioUnit;
}
@end
@@ -0,0 +1,95 @@
if(SMTG_MAC)
if (XCODE AND SMTG_ENABLE_AUV2_BUILDS)
option(SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES
"Activate only the buses that have the kDefaultActive flag set in the AUWrapper. This may not work on some hosts because they never activate a bus later."
OFF
)
string(RANDOM LENGTH 20 CocoaId)
file(CONFIGURE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/au/aucocoaclassprefix.h"
CONTENT "#define SMTG_AUCocoaUIBase_CLASS_NAME SMTG_AUCocoaUIBase_${CocoaId}"
)
set(target au_wrapper)
set(${target}_sources
aucarbonview.mm
aucarbonview.h
aucocoaview.mm
aucocoaview.h
auwrapper.mm
auwrapper.h
NSDataIBStream.mm
NSDataIBStream.h
)
add_library(${target}
STATIC
${${target}_sources}
)
smtg_target_setup_universal_binary(${target})
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER}
)
target_compile_features(${target}
PUBLIC
cxx_std_17
)
if(SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES)
target_compile_definitions(${target}
PRIVATE
SMTG_AUWRAPPER_ACTIVATE_ONLY_DEFAULT_ACTIVE_BUSES
)
endif()
target_link_libraries(${target}
PRIVATE
sdk_hosting
"-framework AudioUnit" "-framework CoreMIDI"
"-framework AudioToolbox"
"-framework CoreFoundation"
"-framework Carbon"
"-framework Cocoa"
"-framework CoreAudio"
)
target_include_directories(${target}
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}/au/"
)
if(NOT ${SMTG_COREAUDIO_SDK_PATH} STREQUAL "")
target_sources(${target} PRIVATE
ausdk.mm
)
target_include_directories(${target}
PRIVATE
"${SMTG_COREAUDIO_SDK_PATH}/**"
)
elseif(NOT ${SMTG_AUDIOUNIT_SDK_PATH} STREQUAL "")
target_compile_definitions(${target}
PRIVATE
SMTG_AUWRAPPER_USES_AUSDK
)
## Adding the xcodeproj will crash Xcode when closing and reopening the cmake generated project
# target_sources(${target} PRIVATE
# "${SMTG_AUDIOUNIT_SDK_PATH}/AudioUnitSDK.xcodeproj"
# )
target_include_directories(${target}
PRIVATE
"${SMTG_AUDIOUNIT_SDK_PATH}/include/**"
)
target_link_libraries(${target}
PRIVATE
AudioUnitSDK
)
else()
message(${SMTG_AUDIOUNIT_SDK_PATH})
message(FATAL_ERROR "The option SMTG_ENABLE_AUV2_BUILDS is set but the audio unit SDK paths are not set")
endif()
else()
message("[SMTG] * To enable building the AudioUnit wrapper, you need to use the Xcode generator and set SMTG_COREAUDIO_SDK_PATH to the path of your installation of the CoreAudio SDK!")
endif(XCODE AND SMTG_ENABLE_AUV2_BUILDS)
endif(SMTG_MAC)
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#import <Foundation/Foundation.h>
#import "pluginterfaces/base/ibstream.h"
#import "public.sdk/source/vst/hosting/hostclasses.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class NSDataIBStream : public IBStream, Vst::IStreamAttributes
{
public:
NSDataIBStream (NSData* data, bool hideAttributes = false);
virtual ~NSDataIBStream ();
//---from IBStream-------------------
tresult PLUGIN_API read (void* buffer, int32 numBytes, int32* numBytesRead = 0) SMTG_OVERRIDE;
tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE;
tresult PLUGIN_API seek (int64 pos, int32 mode, int64* result = 0) SMTG_OVERRIDE;
tresult PLUGIN_API tell (int64* pos) SMTG_OVERRIDE;
//---from Vst::IStreamAttributes-----
tresult PLUGIN_API getFileName (String128 name) SMTG_OVERRIDE;
IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
NSData* data;
int64 currentPos;
IPtr<IAttributeList> attrList;
bool hideAttributes;
};
//------------------------------------------------------------------------
class NSMutableDataIBStream : public NSDataIBStream
{
public:
NSMutableDataIBStream (NSMutableData* data);
virtual ~NSMutableDataIBStream ();
tresult PLUGIN_API write (void* buffer, int32 numBytes, int32* numBytesWritten = 0) SMTG_OVERRIDE;
//------------------------------------------------------------------------
protected:
NSMutableData* mdata;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,185 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/NSDataIBStream.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "NSDataIBStream.h"
#include "pluginterfaces/vst/ivstattributes.h"
#include <algorithm>
#if __clang__
#if __has_feature(objc_arc) && __clang_major__ >= 3
#define ARC_ENABLED 1
#endif // __has_feature(objc_arc)
#endif // __clang__
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
NSDataIBStream::NSDataIBStream (NSData* data, bool hideAttributes)
: data (data)
, currentPos (0)
, hideAttributes (hideAttributes)
{
FUNKNOWN_CTOR
if (!hideAttributes)
attrList = HostAttributeList::make ();
#if !ARC_ENABLED
[data retain];
#endif
}
//------------------------------------------------------------------------
NSDataIBStream::~NSDataIBStream ()
{
#if !ARC_ENABLED
[data release];
#endif
FUNKNOWN_DTOR
}
//------------------------------------------------------------------------
IMPLEMENT_REFCOUNT (NSDataIBStream)
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::queryInterface (const TUID iid, void** obj)
{
QUERY_INTERFACE (iid, obj, FUnknown::iid, IBStream)
QUERY_INTERFACE (iid, obj, IBStream::iid, IBStream)
if (!hideAttributes)
QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes)
*obj = 0;
return kNoInterface;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::read (void* buffer, int32 numBytes, int32* numBytesRead)
{
int32 useBytes = std::min (numBytes, (int32)([data length] - currentPos));
if (useBytes > 0)
{
[data getBytes: buffer range: NSMakeRange (currentPos, useBytes)];
if (numBytesRead)
*numBytesRead = useBytes;
currentPos += useBytes;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::seek (int64 pos, int32 mode, int64* result)
{
switch (mode)
{
case kIBSeekSet:
{
if (pos <= [data length])
{
currentPos = pos;
if (result)
tell (result);
return kResultTrue;
}
break;
}
case kIBSeekCur:
{
if (currentPos + pos <= [data length])
{
currentPos += pos;
if (result)
tell (result);
return kResultTrue;
}
break;
}
case kIBSeekEnd:
{
if ([data length] + pos <= [data length])
{
currentPos = [data length] + pos;
if (result)
tell (result);
return kResultTrue;
}
break;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::tell (int64* pos)
{
if (pos)
{
*pos = currentPos;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSDataIBStream::getFileName (String128 name)
{
return kNotImplemented;
}
//------------------------------------------------------------------------
IAttributeList* PLUGIN_API NSDataIBStream::getAttributes ()
{
return attrList;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
NSMutableDataIBStream::NSMutableDataIBStream (NSMutableData* data)
: NSDataIBStream (data, true)
, mdata (data)
{
}
//------------------------------------------------------------------------
NSMutableDataIBStream::~NSMutableDataIBStream ()
{
[mdata setLength:currentPos];
}
//------------------------------------------------------------------------
tresult PLUGIN_API NSMutableDataIBStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
[mdata replaceBytesInRange:NSMakeRange (currentPos, numBytes) withBytes:buffer];
if (numBytesWritten)
*numBytesWritten = numBytes;
currentPos += numBytes;
return kResultTrue;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,68 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucarbonview.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#include "pluginterfaces/base/fplatform.h"
#if !SMTG_PLATFORM_64
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "AUPublic/AUCarbonViewBase/AUCarbonViewBase.h"
#pragma clang diagnostic pop
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "base/source/fobject.h"
#include "pluginterfaces/gui/iplugview.h"
namespace Steinberg {
namespace Vst {
class AUCarbonPlugFrame;
//------------------------------------------------------------------------
class AUCarbonView : public AUCarbonViewBase, public IPlugFrame, public FObject
{
public:
AUCarbonView (AudioUnitCarbonView auv);
~AUCarbonView ();
OSStatus CreateUI (Float32 xoffset, Float32 yoffset) override;
OBJ_METHODS(AUCarbonView, FObject)
DEF_INTERFACES_1(IPlugFrame, FObject)
REFCOUNT_METHODS(FObject)
protected:
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) SMTG_OVERRIDE;
static OSStatus HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void* inUserData);
IEditController* editController;
AUCarbonPlugFrame* plugFrame;
IPlugView* plugView;
HIViewRef hiPlugView;
EventHandlerRef eventHandler;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
#endif // !SMTG_PLATFORM_64
/// \endcond
@@ -0,0 +1,146 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucarbonview.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "aucarbonview.h"
#if !SMTG_PLATFORM_64
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
AUCarbonView::AUCarbonView (AudioUnitCarbonView auv)
: AUCarbonViewBase (auv)
, editController (0)
, plugView (0)
, hiPlugView (0)
{
}
//------------------------------------------------------------------------
AUCarbonView::~AUCarbonView ()
{
if (plugView)
{
plugView->setFrame (0);
plugView->removed ();
plugView->release ();
}
}
//------------------------------------------------------------------------
OSStatus AUCarbonView::HIViewAdded (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
UInt32 eventClass = GetEventClass (inEvent);
UInt32 eventKind = GetEventKind (inEvent);
if (eventClass == kEventClassControl && eventKind == kEventControlAddedSubControl)
{
HIViewRef newControl;
if (GetEventParameter (inEvent, kEventParamControlSubControl, typeControlRef, NULL, sizeof (HIViewRef) , NULL , &newControl) == noErr)
{
AUCarbonView* wrapper = (AUCarbonView*)inUserData;
wrapper->hiPlugView = newControl;
RemoveEventHandler (wrapper->eventHandler);
wrapper->eventHandler = 0;
}
}
return eventNotHandledErr;
}
//------------------------------------------------------------------------
OSStatus AUCarbonView::CreateUI (Float32 xoffset, Float32 yoffset)
{
AudioUnit unit = GetEditAudioUnit ();
if (unit)
{
if (!editController)
{
UInt32 size = sizeof (IEditController*);
if (AudioUnitGetProperty (unit, 64000, kAudioUnitScope_Global, 0, &editController, &size) != noErr)
return kAudioUnitErr_NoConnection;
}
if (editController)
{
plugView = editController->createView (ViewType::kEditor);
if (!plugView)
return kAudioUnitErr_NoConnection;
HIViewRef contentView;
const EventTypeSpec eventTypes[] = {
{ kEventClassControl, kEventControlAddedSubControl },
};
OSStatus err = HIViewFindByID (HIViewGetRoot (GetCarbonWindow ()), kHIViewWindowContentID, &contentView);
err = InstallControlEventHandler (contentView, HIViewAdded, 1, eventTypes, this, &eventHandler);
plugView->setFrame (this);
if (plugView->attached (GetCarbonWindow (), kPlatformTypeHIView) == kResultTrue)
{
HIViewRemoveFromSuperview (hiPlugView);
EmbedControl (hiPlugView);
HIViewMoveBy (hiPlugView, xoffset, yoffset);
return noErr;
}
else
plugView->setFrame (0);
}
}
return kAudioUnitErr_NoConnection;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AUCarbonView::resizeView (IPlugView* view, ViewRect* vr)
{
if (vr == 0 || view != plugView)
return kInvalidArgument;
HIViewRef hiView = GetCarbonPane ();
if (hiView)
{
HIRect r;
if (HIViewGetFrame (hiView, &r) != noErr)
return kResultFalse;
r.size.width = vr->right - vr->left;
r.size.height = vr->bottom - vr->top;
if (HIViewSetFrame (hiView, &r) != noErr)
return kResultFalse;
if (plugView)
plugView->onSize (vr);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
//COMPONENT_ENTRY(AUCarbonView)
//------------------------------------------------------------------------
extern "C" {
ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj);
__attribute__ ((visibility ("default"))) ComponentResult AUCarbonViewEntry(ComponentParameters *params, AUCarbonView *obj)
{
return ComponentEntryPoint<AUCarbonView>::Dispatch(params, obj);
}
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
#endif // !SMTG_PLATFORM_64
/// \endcond
@@ -0,0 +1,31 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucocoaview.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#ifndef SMTG_AUCocoaUIBase_CLASS_NAME
#import "aucocoaclassprefix.h"
#endif
#import <Foundation/Foundation.h>
#import <AudioUnit/AUCocoaUIView.h>
//------------------------------------------------------------------------
@interface SMTG_AUCocoaUIBase_CLASS_NAME : NSObject<AUCocoaUIBase>
@end
/// \endcond
@@ -0,0 +1,275 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/aucocoaview.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#import "aucocoaview.h"
#import "auwrapper.h"
#import "public.sdk/source/vst/utility/objcclassbuilder.h"
#import "pluginterfaces/base/funknownimpl.h"
#import "pluginterfaces/gui/iplugview.h"
#import "pluginterfaces/vst/ivsteditcontroller.h"
//------------------------------------------------------------------------
@interface NSObject (SMTG_AUView)
- (id)initWithEditController:(Steinberg::Vst::IEditController*)editController
audioUnit:(AudioUnit)au
preferredSize:(NSSize)size;
@end
//------------------------------------------------------------------------
namespace Steinberg {
namespace {
//------------------------------------------------------------------------
struct AUPlugFrame : U::Implements<U::Directly<IPlugFrame>>
{
AUPlugFrame (NSView* parent) : parent (parent) {}
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* vr) override
{
NSRect newSize = NSMakeRect ([parent frame].origin.x, [parent frame].origin.y,
vr->right - vr->left, vr->bottom - vr->top);
[parent setFrame:newSize];
return kResultTrue;
}
NSView* parent;
};
//------------------------------------------------------------------------
struct AUView
{
static constexpr auto VarNamePlugView = "plugView";
static constexpr auto VarNameEditController = "editController";
static constexpr auto VarNameAudioUnit = "audioUnit";
static constexpr auto VarNameDynlib = "dynlib";
static constexpr auto VarNamePlugFrame = "plugFrame";
static constexpr auto VarNameIsAttached = "isAttached";
struct Instance : ObjCInstance
{
using PlugViewVar = std::optional<ObjCVariable<IPlugView*>>;
using EditControllerVar = std::optional<ObjCVariable<Vst::IEditController*>>;
using AudioUnitVar = std::optional<ObjCVariable<AudioUnit>>;
using DynlibVar = std::optional<ObjCVariable<FObject*>>;
using PlugFrameVar = std::optional<ObjCVariable<AUPlugFrame*>>;
using IsAttachedVar = std::optional<ObjCVariable<BOOL>>;
Instance (__unsafe_unretained id obj) : ObjCInstance (obj, [NSView class])
{
plugView = getVariable<IPlugView*> (VarNamePlugView);
editController = getVariable<Vst::IEditController*> (VarNameEditController);
audioUnit = getVariable<AudioUnit> (VarNamePlugView);
dynlib = getVariable<FObject*> (VarNameDynlib);
plugFrame = getVariable<AUPlugFrame*> (VarNamePlugFrame);
isAttached = getVariable<BOOL> (VarNameIsAttached);
}
PlugViewVar plugView;
EditControllerVar editController;
AudioUnitVar audioUnit;
DynlibVar dynlib;
PlugFrameVar plugFrame;
IsAttachedVar isAttached;
};
static id alloc ()
{
static ObjCClass gInstance;
return [gInstance.cl alloc];
}
private:
struct ObjCClass
{
Class cl;
ObjCClass ()
{
cl = ObjCClassBuilder ()
.init ("SMTG_AUView", [NSView class])
.addIvar<IPlugView*> (VarNamePlugView)
.addIvar<Vst::IEditController*> (VarNameEditController)
.addIvar<AudioUnit> (VarNameAudioUnit)
.addIvar<FObject*> (VarNameDynlib)
.addIvar<AUPlugFrame*> (VarNamePlugFrame)
.addIvar<BOOL> (VarNameIsAttached)
.addMethod (@selector (initWithEditController:audioUnit:preferredSize:),
initWithEditController)
.addMethod (@selector (setFrame:), setFrame)
.addMethod (@selector (isFlipped), isFlipped)
.addMethod (@selector (viewDidMoveToSuperview), viewDidMoveToSuperview)
.addMethod (@selector (dealloc), dealloc)
.finalize ();
}
static id initWithEditController (id self, SEL cmd, Vst::IEditController* editController,
AudioUnit au, NSSize size)
{
ObjCInstance obj (self);
self = obj.callSuper<id (NSRect), id> (@selector (initWithFrame:),
NSMakeRect (0, 0, size.width, size.height));
if (self)
{
Instance inst (self);
inst.editController->set (editController);
editController->addRef ();
inst.audioUnit->set (au);
auto plugView = editController->createView (Vst::ViewType::kEditor);
if (!plugView ||
plugView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue)
{
[self dealloc];
return nil;
}
inst.plugView->set (plugView);
auto plugFrame = NEW AUPlugFrame (self);
inst.plugFrame->set (plugFrame);
plugView->setFrame (plugFrame);
if (plugView->attached (self, kPlatformTypeNSView) != kResultTrue)
{
[self dealloc];
return nil;
}
ViewRect vr;
if (plugView->getSize (&vr) == kResultTrue)
{
NSRect newSize = NSMakeRect (0, 0, vr.right - vr.left, vr.bottom - vr.top);
[self setFrame:newSize];
}
inst.isAttached->set (YES);
FObject* fObject = nullptr;
UInt32 size = sizeof (FObject*);
if (AudioUnitGetProperty (au, 64001, kAudioUnitScope_Global, 0, &fObject, &size) ==
noErr)
{
fObject->addRef ();
inst.dynlib->set (fObject);
}
}
return self;
}
static void setFrame (id self, SEL cmd, NSRect newSize)
{
Instance inst (self);
inst.callSuper<void (NSRect)> (@selector (setFrame:), newSize);
ViewRect viewRect (0, 0, newSize.size.width, newSize.size.height);
if (inst.plugView->get ())
inst.plugView->get ()->onSize (&viewRect);
}
static BOOL isFlipped (id self, SEL cmd) { return YES; }
static void viewDidMoveToSuperview (id self, SEL cmd)
{
Instance inst (self);
if (inst.plugView->get ())
{
if ([self superview])
{
if (!inst.isAttached->get ())
{
if (inst.plugView->get ()->attached (self, kPlatformTypeNSView) ==
kResultTrue)
{
inst.isAttached->set (YES);
}
}
}
else
{
if (inst.isAttached->get ())
{
inst.plugView->get ()->removed ();
inst.isAttached->set (NO);
}
}
}
}
static void dealloc (id self, SEL cmd)
{
Instance inst (self);
if (auto plugView = inst.plugView->get ())
{
if (inst.isAttached->get ())
{
plugView->setFrame (0);
plugView->removed ();
}
plugView->release ();
if (auto plugFrame = inst.plugFrame->get ())
plugFrame->release ();
if (auto editController = inst.editController->get ())
{
auto refCount = editController->addRef ();
if (refCount == 2)
editController->terminate ();
editController->release ();
editController->release ();
inst.editController->set (nullptr);
}
}
if (auto dynlib = inst.dynlib->get ())
dynlib->release ();
inst.callSuper<void ()> (@selector (dealloc));
}
};
};
//------------------------------------------------------------------------
} // anonymous
} // Steinberg
//------------------------------------------------------------------------
@implementation SMTG_AUCocoaUIBase_CLASS_NAME
//------------------------------------------------------------------------
- (unsigned)interfaceVersion
{
return 0;
}
//------------------------------------------------------------------------
- (NSString*)description
{
return @"Cocoa View";
}
//------------------------------------------------------------------------
- (NSView*)uiViewForAudioUnit:(AudioUnit)inAU withSize:(NSSize)inPreferredSize
{
using namespace Steinberg;
Vst::IEditController* editController = 0;
UInt32 size = sizeof (Vst::IEditController*);
if (AudioUnitGetProperty (inAU, 64000, kAudioUnitScope_Global, 0, &editController, &size) !=
noErr)
return nil;
return [[AUView::alloc () initWithEditController:editController
audioUnit:inAU
preferredSize:inPreferredSize] autorelease];
}
@end
/// \endcond
@@ -0,0 +1,65 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/auresource.r
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include <AudioUnit/AudioUnit.r>
#include <AudioUnit/AudioUnitCarbonView.r>
#include "audiounitconfig.h"
/* ----------------------------------------------------------------------------------------------------------------------------------------
// audiounitconfig.h needs the following definitions:
#define kAudioUnitVersion 0xFFFFFFFF // Version Number, needs to be in hex
#define kAudioUnitName "Steinberg: MyVST3 as AudioUnit" // Company Name + Effect Name
#define kAudioUnitDescription "My VST3 as AudioUnit" // Effect Description
#define kAudioUnitType kAudioUnitType_Effect // can be kAudioUnitType_Effect or kAudioUnitType_MusicDevice
#define kAudioUnitComponentSubType 'test' // unique id
#define kAudioUnitComponentManuf 'SMTG' // registered company id
#define kAudioUnitCarbonView 1 // if 0 no Carbon view support will be added
*/
#define kAudioUnitResID_Processor 1000
#define kAudioUnitResID_CarbonView 9000
//----------------------Processor----------------------------------------------
#define RES_ID kAudioUnitResID_Processor
#define COMP_TYPE kAudioUnitType
#define COMP_SUBTYPE kAudioUnitComponentSubType
#define COMP_MANUF kAudioUnitComponentManuf
#define VERSION kAudioUnitVersion
#define NAME kAudioUnitName
#define DESCRIPTION kAudioUnitDescription
#define ENTRY_POINT "AUWrapperEntry"
#include "AUResources.r"
#if kAudioUnitCarbonView
//----------------------View----------------------------------------------
#define RES_ID kAudioUnitResID_CarbonView
#define COMP_TYPE kAudioUnitCarbonViewComponentType
#define COMP_SUBTYPE kAudioUnitComponentSubType
#define COMP_MANUF kAudioUnitComponentManuf
#define VERSION kAudioUnitVersion
#define NAME "CarbonView"
#define DESCRIPTION "CarbonView"
#define ENTRY_POINT "AUCarbonViewEntry"
#include "AUResources.r"
#endif
@@ -0,0 +1,72 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/ausdk.mm
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#pragma clang diagnostic ignored "-Wunused-value"
#pragma clang diagnostic ignored "-Wparentheses"
#pragma clang diagnostic ignored "-Woverloaded-virtual"
#ifndef MAC_OS_X_VERSION_10_7
#define MAC_OS_X_VERSION_10_7 1070
#endif
#import "PublicUtility/CAAudioChannelLayout.cpp"
#import "PublicUtility/CABundleLocker.cpp"
#import "PublicUtility/CAHostTimeBase.cpp"
#import "PublicUtility/CAStreamBasicDescription.cpp"
#import "PublicUtility/CAVectorUnit.cpp"
#import "PublicUtility/CAAUParameter.cpp"
#import "AUPublic/AUBase/ComponentBase.cpp"
#import "AUPublic/AUBase/AUScopeElement.cpp"
#import "AUPublic/AUBase/AUOutputElement.cpp"
#import "AUPublic/AUBase/AUInputElement.cpp"
#import "AUPublic/AUBase/AUBase.cpp"
#if !__LP64__
#ifndef verify_noerr
#define verify_noerr(x) x
#endif
#ifndef verify
#define verify(x)
#endif
#import "AUPublic/AUCarbonViewBase/AUCarbonViewBase.cpp"
#import "AUPublic/AUCarbonViewBase/AUCarbonViewControl.cpp"
#import "AUPublic/AUCarbonViewBase/AUCarbonViewDispatch.cpp"
#import "AUPublic/AUCarbonViewBase/AUControlGroup.cpp"
#import "AUPublic/AUCarbonViewBase/CarbonEventHandler.cpp"
#endif
#import "AUPublic/Utility/AUTimestampGenerator.cpp"
#import "AUPublic/Utility/AUBuffer.cpp"
#import "AUPublic/Utility/AUBaseHelper.cpp"
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
#import "AUPublic/OtherBases/AUMIDIEffectBase.cpp"
#import "AUPublic/Utility/AUDebugDispatcher.cpp"
#else
#import "AUPublic/AUBase/AUPlugInDispatch.cpp"
#endif
#if !CA_USE_AUDIO_PLUGIN_ONLY
#import "AUPublic/AUBase/AUDispatch.cpp"
#import "AUPublic/OtherBases/MusicDeviceBase.cpp"
#import "AUPublic/OtherBases/AUMIDIBase.cpp"
#import "AUPublic/OtherBases/AUEffectBase.cpp"
#endif
/// \endcond
@@ -0,0 +1,287 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/auwrapper.h
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
/// \cond ignore
#pragma once
#ifdef SMTG_AUWRAPPER_USES_AUSDK
#if CA_USE_AUDIO_PLUGIN_ONLY
#include "AudioUnitSDK/AUBase.h"
#define AUWRAPPER_BASE_CLASS ausdk::AUBase
#else
#include "AudioUnitSDK/MusicDeviceBase.h"
#define AUWRAPPER_BASE_CLASS ausdk::MusicDeviceBase
#endif // CA_USE_AUDIO_PLUGIN_ONLY
#else
#if CA_USE_AUDIO_PLUGIN_ONLY
#include "AudioUnits/AUPublic/AUBase/AUBase.h"
#define AUWRAPPER_BASE_CLASS AUBase
#else
#include "AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h"
#define AUWRAPPER_BASE_CLASS MusicDeviceBase
#endif // CA_USE_AUDIO_PLUGIN_ONLY
#endif // SMTG_AUWRAPPER_USES_AUSDK
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "public.sdk/source/vst/utility/ringbuffer.h"
#include "public.sdk/source/vst/utility/rttransfer.h"
#include "base/source/fstring.h"
#include "base/source/timer.h"
#include "base/thread/include/flock.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmidilearn.h"
#include "pluginterfaces/vst/ivstprocesscontext.h"
#include "pluginterfaces/vst/ivstunits.h"
#include <AudioToolbox/AudioToolbox.h>
#include <Cocoa/Cocoa.h>
#include <array>
#include <map>
#include <unordered_map>
#include <vector>
namespace Steinberg {
class VST3DynLibrary;
namespace Vst {
//------------------------------------------------------------------------
//------------------------------------------------------------------------
class AUWrapper : public AUWRAPPER_BASE_CLASS, public IComponentHandler, public ITimerCallback
{
public:
#ifdef SMTG_AUWRAPPER_USES_AUSDK
using AUElement = ausdk::AUElement;
#else
using AudioStreamBasicDescription = CAStreamBasicDescription;
#endif
AUWrapper (ComponentInstanceRecord* ci);
~AUWrapper ();
//---ComponentBase---------------------
#if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK)
ComponentResult Version () SMTG_OVERRIDE;
#endif
void PostConstructor () SMTG_OVERRIDE;
//---AUBase-----------------------------
void Cleanup () SMTG_OVERRIDE;
ComponentResult Initialize () SMTG_OVERRIDE;
#ifdef SMTG_AUWRAPPER_USES_AUSDK
std::unique_ptr<AUElement> CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
#else
AUElement* CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
#endif
UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) SMTG_OVERRIDE;
bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
ComponentResult ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, const AudioStreamBasicDescription& inPrevFormat, const AudioStreamBasicDescription& inNewFormat) SMTG_OVERRIDE;
ComponentResult SetConnection (const AudioUnitConnection& inConnection) SMTG_OVERRIDE;
ComponentResult GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) SMTG_OVERRIDE;
ComponentResult SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames) SMTG_OVERRIDE;
ComponentResult SaveState (CFPropertyListRef* outData) SMTG_OVERRIDE;
ComponentResult RestoreState (CFPropertyListRef inData) SMTG_OVERRIDE;
ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, const AudioTimeStamp &inTimeStamp, UInt32 inNumberFrames) SMTG_OVERRIDE;
void processOutputEvents (const AudioTimeStamp &inTimeStamp);
#if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK)
int GetNumCustomUIComponents () SMTG_OVERRIDE;
void GetUIComponentDescs (ComponentDescription* inDescArray) SMTG_OVERRIDE;
#endif
#ifdef SMTG_AUWRAPPER_USES_AUSDK
OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, bool &outWritable) SMTG_OVERRIDE;
#else
OSStatus GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, Boolean &outWritable) SMTG_OVERRIDE;
#endif
ComponentResult GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) SMTG_OVERRIDE;
ComponentResult SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) SMTG_OVERRIDE;
bool CanScheduleParameters() const SMTG_OVERRIDE;
Float64 GetLatency () SMTG_OVERRIDE;
Float64 GetTailTime () SMTG_OVERRIDE;
//---Factory presets
OSStatus GetPresets (CFArrayRef* outData) const SMTG_OVERRIDE;
OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) SMTG_OVERRIDE;
#if !CA_USE_AUDIO_PLUGIN_ONLY
//---MusicDeviceBase-------------------------
OSStatus HandleNoteOn (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) SMTG_OVERRIDE;
OSStatus HandleNoteOff (UInt8 inChannel, UInt8 inNoteNumber, UInt8 inVelocity, UInt32 inStartFrame) SMTG_OVERRIDE;
ComponentResult StartNote (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams) SMTG_OVERRIDE;
ComponentResult StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame) SMTG_OVERRIDE;
OSStatus GetInstrumentCount (UInt32 &outInstCount) const SMTG_OVERRIDE;
//---AUMIDIBase------------------------------
OSStatus HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame) SMTG_OVERRIDE;
#endif
#if AUSDK_MIDI2_AVAILABLE
OSStatus MIDIEventList (UInt32 inOffsetSampleFrame,
const struct MIDIEventList* eventList) override;
bool handleMIDIEventPacket (UInt32 inOffsetSampleFrame, const MIDIEventPacket* packet);
#endif
//---custom----------------------------------
void setControllerParameter (ParamID pid, ParamValue value);
// return for a given midiChannel the unitID and the ProgramListID
bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId);
// restore preset state, add StateType "Project" to stream if loading from project
ComponentResult restoreState (CFPropertyListRef inData, bool fromProject);
//------------------------------------------------------------------------
#if !CA_USE_AUDIO_PLUGIN_ONLY && !defined(SMTG_AUWRAPPER_USES_AUSDK)
static ComponentResult ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This);
#endif
//------------------------------------------------------------------------
static CFBundleRef gBundleRef;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
//---from IComponentHandler-------------------
tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE;
tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE;
tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE;
tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE;
//---from ITimerCallback----------------------
void onTimer (Timer* timer) SMTG_OVERRIDE;
// internal helpers
double getSampleRate () const { return sampleRate; }
void updateProcessContext ();
void syncParameterValues ();
void cacheParameterValues ();
void clearParameterValueCache ();
void updateProgramChangesCache ();
virtual IPluginFactory* getFactory ();
void loadVST3Module ();
void unloadVST3Module ();
bool validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info,
UInt32 numChanInfo) const;
IAudioProcessor* audioProcessor;
IEditController* editController;
Timer* timer;
HostProcessData processData;
ParameterChanges processParamChanges;
ParameterChanges outputParamChanges;
ParameterChangeTransfer transferParamChanges;
ParameterChangeTransfer outputParamTransfer;
ProcessContext processContext;
EventList eventList;
typedef std::map<ParamID, AudioUnitParameterInfo> CachedParameterInfoMap;
typedef std::map<UnitID, UnitInfo> UnitInfoMap;
typedef std::vector<String> ClumpGroupVector;
UnitInfoMap unitInfos;
ClumpGroupVector clumpGroups;
CachedParameterInfoMap cachedParameterInfos;
Steinberg::Base::Thread::FLock parameterCacheChanging;
NoteInstanceID noteCounter;
double sampleRate;
ParamID bypassParamID;
AUPreset* presets;
int32 numPresets;
ParamID factoryProgramChangedID;
AUParameterListenerRef paramListenerRef;
std::vector<ParameterInfo> programParameters;
static constexpr int32 kMaxProgramChangeParameters = 16;
struct ProgramChangeInfo
{
ParamID pid {kNoParamId};
int32 numPrograms {0};
};
using ProgramChangeInfoList = std::array<ProgramChangeInfo, kMaxProgramChangeParameters>;
using ProgramChangeInfoTransfer = RTTransferT<ProgramChangeInfoList>;
ProgramChangeInfoList programChangeInfos;
ProgramChangeInfoTransfer programChangeInfoTransfer;
// midi mapping
struct MidiMapping
{
using CC2ParamMap = std::unordered_map<CtrlNumber, ParamID>;
using ChannelList = std::vector<CC2ParamMap>;
using BusList = std::vector<ChannelList>;
BusList busList;
bool empty () const { return busList.empty () || busList[0].empty (); }
};
using MidiMappingTransfer = RTTransferT<MidiMapping>;
MidiMappingTransfer midiMappingTransfer;
MidiMapping midiMappingCache;
struct MidiLearnEvent
{
int32 busIndex;
int16 channel;
CtrlNumber midiCC;
};
using MidiLearnRingBuffer = OneReaderOneWriter::RingBuffer<MidiLearnEvent>;
MidiLearnRingBuffer midiLearnRingBuffer;
IPtr<IMidiLearn> midiLearn;
struct MIDIOutputCallbackHelper;
int32 midiOutCount; // currently only 0 or 1 supported
std::unique_ptr<MIDIOutputCallbackHelper> mCallbackHelper;
EventList outputEvents;
bool isInstrument;
bool isBypassed;
bool isOfflineRender;
private:
void buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const;
void updateMidiMappingCache ();
IPtr<VST3DynLibrary> dynLib;
};
//------------------------------------------------------------------------
class AutoreleasePool
{
public:
AutoreleasePool () { ap = [[NSAutoreleasePool alloc] init]; }
~AutoreleasePool () { [ap drain]; }
//------------------------------------------------------------------------
protected:
NSAutoreleasePool* ap;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/auwrapper/auwrapper_prefix.pch
// Created by : Steinberg, 12/2007
// Description : VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include <CoreFoundation/CoreFoundation.h>
#include <CoreAudio/CoreAudio.h>
@@ -0,0 +1,17 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : ausdkpath.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
// If you are building with Xcode >= 4.x please add the path to your downloaded Audio Tools for Xcode
CUSTOM_AU_SDK_PATH=/Applications/Xcode.app/Contents/Developer/Extras/CoreAudio/ // AUWRAPPER_CHANGE
@@ -0,0 +1,27 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : auwrapper.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "../../../../../base/mac/config/libc++base"
#include "ausdkpath"
PRODUCT_NAME = auwrapper
HEADER_SEARCH_PATHS = ../../../.. $(DEVELOPER_DIR)/Examples/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/** $(DEVELOPER_DIR)/Extras/CoreAudio/AudioUnits/AUPublic/AUViewBase/** ../../../../external.apple.coreaudio/**
GCC_PREFIX_HEADER = auwrapper_prefix.pch
GCC_PRECOMPILE_PREFIX_HEADER = YES
CLANG_CXX_LANGUAGE_STANDARD = c++17
CLANG_CXX_LIBRARY = libc++
@@ -0,0 +1,20 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : ausdkpath_debug.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "../../../../../base/mac/config/debug"
GCC_OPTIMIZATION_LEVEL = 0
DEPLOYMENT_POSTPROCESSING = NO
@@ -0,0 +1,20 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : auwrapper_release.xcconfig
// Created by : Steinberg, 5/24/12
// Description : Xcode configuration file to specify paths to the AU SDK files, VST 3 -> AU Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "../../../../../base/mac/config/release"
GCC_OPTIMIZATION_LEVEL = 3
DEPLOYMENT_POSTPROCESSING = NO
@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Validator
// Filename : usediids.cpp
// Created by : Steinberg 09.2008
// Description : Interface symbols file
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
//#define INIT_CLASS_IID
// This macro definition modifies the behavior of DECLARE_CLASS_IID (funknown.h)
// and produces the actual symbols for all interface identifiers.
// It must be defined before including the interface headers and
// in only one source file!
//------------------------------------------------------------------------
//#define INIT_CLASS_IID
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include "pluginterfaces/vst/ivstmidilearn.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "pluginterfaces/vst/ivstpluginterfacesupport.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstunits.h"
namespace Steinberg {
DEF_CLASS_IID (Vst::IAttributeList)
DEF_CLASS_IID (Vst::IAudioProcessor)
DEF_CLASS_IID (Vst::IEditController)
DEF_CLASS_IID (Vst::IEditController2)
DEF_CLASS_IID (Vst::IComponent)
DEF_CLASS_IID (Vst::IComponentHandler)
DEF_CLASS_IID (Vst::IConnectionPoint)
DEF_CLASS_IID (Vst::IEventList)
DEF_CLASS_IID (Vst::IHostApplication)
DEF_CLASS_IID (Vst::IMessage)
DEF_CLASS_IID (Vst::IMidiLearn)
DEF_CLASS_IID (Vst::IMidiMapping)
DEF_CLASS_IID (Vst::IParameterChanges)
DEF_CLASS_IID (Vst::IParamValueQueue)
DEF_CLASS_IID (Vst::IPlugInterfaceSupport)
DEF_CLASS_IID (Vst::IProgramListData)
DEF_CLASS_IID (Vst::IStreamAttributes)
DEF_CLASS_IID (Vst::IVst3ToAUWrapper)
DEF_CLASS_IID (Vst::IUnitData)
DEF_CLASS_IID (Vst::IUnitInfo)
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,299 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/basewrapper/basewrapper.h
// Created by : Steinberg, 01/2018
// Description : VST 3 -> XXX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#include "pluginterfaces/base/ftypes.h"
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include "pluginterfaces/vst/ivstprocesscontext.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/common/memorystream.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "base/source/fstring.h"
#include "base/source/timer.h"
#include <map>
#include <vector>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class BaseEditorWrapper : public IPlugFrame,
public FObject
{
public:
//------------------------------------------------------------------------
BaseEditorWrapper (IEditController* controller);
~BaseEditorWrapper () override;
static bool hasEditor (IEditController* controller);
bool getRect (ViewRect& rect);
virtual bool _open (void* ptr);
virtual void _close ();
bool _setKnobMode (Vst::KnobMode val);
// IPlugFrame
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) SMTG_OVERRIDE;
// FUnknown
tresult PLUGIN_API queryInterface (const char* _iid, void** obj) SMTG_OVERRIDE;
REFCOUNT_METHODS (FObject);
//------------------------------------------------------------------------
protected:
void createView ();
IPtr<IEditController> mController;
IPtr<IPlugView> mView;
ViewRect mViewRect;
};
//------------------------------------------------------------------------
const int32 kMaxEvents = 2048;
class ConnectionProxy;
//-------------------------------------------------------------------------------------------------------
class BaseWrapper : public IHostApplication,
public IComponentHandler,
public IUnitHandler,
public ITimerCallback,
public FObject
{
public:
struct SVST3Config
{
IPluginFactory* factory = nullptr;
IAudioProcessor* processor = nullptr;
IEditController* controller = nullptr;
FUID vst3ComponentID;
};
BaseWrapper (SVST3Config& config);
~BaseWrapper () override;
virtual bool init ();
virtual void _canDoubleReplacing (bool /*val*/) {}
virtual void _setInitialDelay (uint32 /*delay*/) {}
virtual void _noTail (bool /*val*/) {}
virtual void _ioChanged () {}
virtual void _updateDisplay () {}
virtual void _setNumInputs (uint32 inputs) { mNumInputs = inputs; }
virtual void _setNumOutputs (uint32 outputs) { mNumOutputs = outputs; }
virtual bool _sizeWindow (int32 width, int32 height) = 0;
virtual int32 _getChunk (void** data, bool isPreset);
virtual int32 _setChunk (void* data, int32 byteSize, bool isPreset);
virtual bool getEditorSize (int32& width, int32& height) const;
bool isActive () const { return mActive; }
uint32 getNumInputs () const { return mNumInputs; }
uint32 getNumOutputs () const { return mNumOutputs; }
BaseEditorWrapper* getEditor () const { return mEditor; }
//--- ---------------------------------------------------------------------
// VST 3 Interfaces ------------------------------------------------------
// FUnknown
tresult PLUGIN_API queryInterface (const char* iid, void** obj) SMTG_OVERRIDE;
REFCOUNT_METHODS (FObject);
// IHostApplication
tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) SMTG_OVERRIDE;
// IComponentHandler
tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE;
// IUnitHandler
tresult PLUGIN_API notifyUnitSelection (UnitID unitId) SMTG_OVERRIDE;
tresult PLUGIN_API notifyProgramListChange (ProgramListID listId,
int32 programIndex) SMTG_OVERRIDE;
// ITimer
void onTimer (Timer* timer) SMTG_OVERRIDE;
//-------------------------------------------------------------------------------------------------------
protected:
void term ();
virtual void setupParameters ();
virtual void setupProcessTimeInfo () = 0;
virtual void processOutputEvents () {}
virtual void processOutputParametersChanges () {}
void _setSampleRate (float newSamplerate);
bool setupProcessing (int32 processModeOverwrite = -1);
void _processReplacing (float** inputs, float** outputs, int32 sampleFrames);
void _processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames);
template <class T>
void setProcessingBuffers (T** inputs, T** outputs);
void doProcess (int32 sampleFrames);
void processMidiEvent (Event& toAdd, char* midiData, bool isLive = false, int32 noteLength = 0,
float noteOffVelocity = 1.f, float detune = 0.f);
void setEventPPQPositions ();
void _setEditor (BaseEditorWrapper* editor);
bool _setBlockSize (int32 newBlockSize);
float _getParameter (int32 index) const;
void _suspend ();
void _resume ();
void _startProcess ();
void _stopProcess ();
bool _setBypass (bool onOff);
virtual void setupBuses ();
void initMidiCtrlerAssignment ();
void getUnitPath (UnitID unitID, String& path) const;
uint32 countMainBusChannels (BusDirection dir, uint64& mainBusBitset);
/** Returns the last param change from guiTransfer queue. */
bool getLastParamChange (ParamID id, ParamValue& value);
void addParameterChange (ParamID id, ParamValue value, int32 sampleOffset);
void setVendorName (char* name);
void setEffectName (char* name);
void setEffectVersion (char* version);
void setSubCategories (char* string);
bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId);
bool getProgramListInfoByProgramListID (ProgramListID programListId, ProgramListInfo& info);
static const int32 kMaxProgramChangeParameters = 16;
ParamID mProgramChangeParameterIDs[kMaxProgramChangeParameters]; // for each MIDI channel
int32 mProgramChangeParameterIdxs[kMaxProgramChangeParameters]; // for each MIDI channel
FUID mVst3EffectClassID;
// vst3 data
IPtr<IAudioProcessor> mProcessor;
IPtr<IComponent> mComponent;
IPtr<IEditController> mController;
IPtr<IUnitInfo> mUnitInfo;
IPtr<IMidiMapping> mMidiMapping;
IPtr<BaseEditorWrapper> mEditor;
IPtr<PlugInterfaceSupport> mPlugInterfaceSupport;
IPtr<ConnectionProxy> mProcessorConnection;
IPtr<ConnectionProxy> mControllerConnection;
int32 mVst3SampleSize = kSample32;
int32 mVst3processMode = kRealtime;
char mName[PClassInfo::kNameSize];
char mVendor[PFactoryInfo::kNameSize];
char mSubCategories[PClassInfo2::kSubCategoriesSize];
int32 mVersion = 0;
struct ParamMapEntry
{
ParamID vst3ID;
int32 vst3Index;
};
std::vector<ParamMapEntry> mParameterMap;
std::map<ParamID, int32> mParamIndexMap;
ParamID mBypassParameterID = kNoParamId;
ParamID mProgramParameterID = kNoParamId;
int32 mProgramParameterIdx = -1;
HostProcessData mProcessData;
ProcessContext mProcessContext;
ParameterChanges mInputChanges;
ParameterChanges mOutputChanges;
IPtr<EventList> mInputEvents;
IPtr<EventList> mOutputEvents;
uint64 mMainAudioInputBuses = 0;
uint64 mMainAudioOutputBuses = 0;
ParameterChangeTransfer mInputTransfer;
ParameterChangeTransfer mOutputTransfer;
ParameterChangeTransfer mGuiTransfer;
MemoryStream mChunk;
IPtr<Timer> mTimer;
IPtr<IPluginFactory> mFactory;
int32 mNumPrograms {0};
float mSampleRate {44100};
int32 mBlockSize {256};
int32 mNumParams {0};
int32 mCurProgram {-1};
uint32 mNumInputs {0};
uint32 mNumOutputs {0};
enum
{
kMaxMidiMappingBusses = 4
};
ParamID* mMidiCCMapping[kMaxMidiMappingBusses][16];
bool mComponentInitialized = false;
bool mControllerInitialized = false;
bool mComponentsConnected = false;
bool mUseExportedBypass = true;
bool mActive = false;
bool mProcessing = false;
bool mHasEventInputBuses = false;
bool mHasEventOutputBuses = false;
bool mUseIncIndex = true;
};
const uint8 kNoteOff = 0x80; ///< note, off velocity
const uint8 kNoteOn = 0x90; ///< note, on velocity
const uint8 kPolyPressure = 0xA0; ///< note, pressure
const uint8 kController = 0xB0; ///< controller, value
const uint8 kProgramChangeStatus = 0xC0; ///< program change
const uint8 kAfterTouchStatus = 0xD0; ///< channel pressure
const uint8 kPitchBendStatus = 0xE0; ///< lsb, msb
const float kMidiScaler = 1.f / 127.f;
static const uint8 kChannelMask = 0x0F;
static const uint8 kStatusMask = 0xF0;
static const uint32 kDataMask = 0x7F;
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/basewrapper/basewrapper.sdk.cpp
// Created by : Steinberg, 05/2018
// Description : VST 3 -> XXX Wrapper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/common/memorystream.cpp"
#include "public.sdk/source/vst/basewrapper/basewrapper.cpp"
#include "public.sdk/source/vst/hosting/connectionproxy.cpp"
#include "public.sdk/source/vst/hosting/eventlist.cpp"
#include "public.sdk/source/vst/hosting/hostclasses.cpp"
#include "public.sdk/source/vst/hosting/parameterchanges.cpp"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.cpp"
#include "public.sdk/source/vst/hosting/processdata.cpp"
@@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/connectionproxy.cpp
// Created by : Steinberg, 04/2019
// Description : VST 3 Plug-in connection class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "connectionproxy.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (ConnectionProxy, IConnectionPoint, IConnectionPoint::iid)
//------------------------------------------------------------------------
ConnectionProxy::ConnectionProxy (IConnectionPoint* srcConnection)
: srcConnection (srcConnection) // share it
{
FUNKNOWN_CTOR
}
//------------------------------------------------------------------------
ConnectionProxy::~ConnectionProxy ()
{
FUNKNOWN_DTOR
}
//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::connect (IConnectionPoint* other)
{
if (other == nullptr)
return kInvalidArgument;
if (dstConnection)
return kResultFalse;
dstConnection = other; // share it
tresult res = srcConnection->connect (this);
if (res != kResultTrue)
dstConnection = nullptr;
return res;
}
//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::disconnect (IConnectionPoint* other)
{
if (!other)
return kInvalidArgument;
if (other == dstConnection)
{
if (srcConnection)
srcConnection->disconnect (this);
dstConnection = nullptr;
return kResultTrue;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
tresult PLUGIN_API ConnectionProxy::notify (IMessage* message)
{
if (dstConnection)
{
// We discard the message if we are not in the UI main thread
if (threadChecker && threadChecker->test ())
return dstConnection->notify (message);
}
return kResultFalse;
}
//------------------------------------------------------------------------
bool ConnectionProxy::disconnect ()
{
return disconnect (dstConnection) == kResultTrue;
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,51 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/connectionproxy.h
// Created by : Steinberg, 04/2020
// Description : VST 3 Plug-in connection class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstmessage.h"
#include "public.sdk/source/common/threadchecker.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Helper for creating and initializing component.
\ingroup Helper */
//------------------------------------------------------------------------
class ConnectionProxy : public IConnectionPoint
{
public:
ConnectionProxy (IConnectionPoint* srcConnection);
virtual ~ConnectionProxy ();
//--- from IConnectionPoint
tresult PLUGIN_API connect (IConnectionPoint* other) override;
tresult PLUGIN_API disconnect (IConnectionPoint* other) override;
tresult PLUGIN_API notify (IMessage* message) override;
bool disconnect ();
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
std::unique_ptr<ThreadChecker> threadChecker {ThreadChecker::create ()};
IPtr<IConnectionPoint> srcConnection;
IPtr<IConnectionPoint> dstConnection;
};
}
} // namespaces
@@ -0,0 +1,86 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/eventlist.cpp
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 event list implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "eventlist.h"
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (EventList, IEventList, IEventList::iid)
//-----------------------------------------------------------------------------
EventList::EventList (int32 inMaxSize)
{
FUNKNOWN_CTOR
setMaxSize (inMaxSize);
}
//-----------------------------------------------------------------------------
EventList::~EventList ()
{
setMaxSize (0);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
void EventList::setMaxSize (int32 newMaxSize)
{
if (events)
{
delete[] events;
events = nullptr;
fillCount = 0;
}
if (newMaxSize > 0)
events = new Event[newMaxSize];
maxSize = newMaxSize;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API EventList::getEvent (int32 index, Event& e)
{
if (auto event = getEventByIndex (index))
{
memcpy (&e, event, sizeof (Event));
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API EventList::addEvent (Event& e)
{
if (maxSize > fillCount)
{
memcpy (&events[fillCount], &e, sizeof (Event));
fillCount++;
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
Event* EventList::getEventByIndex (int32 index) const
{
if (index < fillCount)
return &events[index];
return nullptr;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,53 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/eventlist.h
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 event list implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstevents.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Example implementation of IEventList.
\ingroup sdkBase
*/
class EventList : public IEventList
{
public:
EventList (int32 maxSize = 50);
virtual ~EventList ();
int32 PLUGIN_API getEventCount () SMTG_OVERRIDE { return fillCount; }
tresult PLUGIN_API getEvent (int32 index, Event& e) SMTG_OVERRIDE;
tresult PLUGIN_API addEvent (Event& e) SMTG_OVERRIDE;
void setMaxSize (int32 maxSize);
void clear () { fillCount = 0; }
Event* getEventByIndex (int32 index) const;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
Event* events {nullptr};
int32 maxSize {0};
int32 fillCount {0};
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,319 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostclasses.cpp
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "hostclasses.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include <algorithm>
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
HostApplication::HostApplication ()
{
FUNKNOWN_CTOR
mPlugInterfaceSupport = owned (new PlugInterfaceSupport);
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::getName (String128 name)
{
return StringConvert::convert ("My VST3 HostApplication", name) ? kResultTrue : kInternalError;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::createInstance (TUID cid, TUID _iid, void** obj)
{
if (FUnknownPrivate::iidEqual (cid, IMessage::iid) &&
FUnknownPrivate::iidEqual (_iid, IMessage::iid))
{
*obj = new HostMessage;
return kResultTrue;
}
if (FUnknownPrivate::iidEqual (cid, IAttributeList::iid) &&
FUnknownPrivate::iidEqual (_iid, IAttributeList::iid))
{
if (auto al = HostAttributeList::make ())
{
*obj = al.take ();
return kResultTrue;
}
return kOutOfMemory;
}
*obj = nullptr;
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostApplication::queryInterface (const char* _iid, void** obj)
{
QUERY_INTERFACE (_iid, obj, FUnknown::iid, IHostApplication)
QUERY_INTERFACE (_iid, obj, IHostApplication::iid, IHostApplication)
if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (_iid, obj) == kResultTrue)
return kResultOk;
*obj = nullptr;
return kResultFalse;
}
//-----------------------------------------------------------------------------
uint32 PLUGIN_API HostApplication::addRef ()
{
return 1;
}
//-----------------------------------------------------------------------------
uint32 PLUGIN_API HostApplication::release ()
{
return 1;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (HostMessage, IMessage, IMessage::iid)
//-----------------------------------------------------------------------------
HostMessage::HostMessage () {FUNKNOWN_CTOR}
//-----------------------------------------------------------------------------
HostMessage::~HostMessage () noexcept
{
setMessageID (nullptr);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
const char* PLUGIN_API HostMessage::getMessageID ()
{
return messageId;
}
//-----------------------------------------------------------------------------
void PLUGIN_API HostMessage::setMessageID (const char* mid)
{
if (messageId)
delete[] messageId;
messageId = nullptr;
if (mid)
{
size_t len = strlen (mid) + 1;
messageId = new char[len];
strcpy (messageId, mid);
}
}
//-----------------------------------------------------------------------------
IAttributeList* PLUGIN_API HostMessage::getAttributes ()
{
if (!attributeList)
attributeList = HostAttributeList::make ();
return attributeList;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
struct HostAttributeList::Attribute
{
enum class Type
{
kUninitialized,
kInteger,
kFloat,
kString,
kBinary
};
Attribute () = default;
Attribute (int64 value) : type (Type::kInteger) { v.intValue = value; }
Attribute (double value) : type (Type::kFloat) { v.floatValue = value; }
/* size is in code unit (count of TChar) */
Attribute (const TChar* value, uint32 sizeInCodeUnit)
: size (sizeInCodeUnit), type (Type::kString)
{
v.stringValue = new TChar[sizeInCodeUnit];
memcpy (v.stringValue, value, sizeInCodeUnit * sizeof (TChar));
}
Attribute (const void* value, uint32 sizeInBytes) : size (sizeInBytes), type (Type::kBinary)
{
v.binaryValue = new char[sizeInBytes];
memcpy (v.binaryValue, value, sizeInBytes);
}
Attribute (Attribute&& o) SMTG_NOEXCEPT { *this = std::move (o); }
Attribute& operator= (Attribute&& o) SMTG_NOEXCEPT
{
v = o.v;
size = o.size;
type = o.type;
o.size = 0;
o.type = Type::kUninitialized;
o.v = {};
return *this;
}
~Attribute () noexcept
{
if (size)
delete[] v.binaryValue;
}
int64 intValue () const { return v.intValue; }
double floatValue () const { return v.floatValue; }
/* sizeInCodeUnit is in code unit (count of TChar) */
const TChar* stringValue (uint32& sizeInCodeUnit)
{
sizeInCodeUnit = size;
return v.stringValue;
}
const void* binaryValue (uint32& sizeInBytes)
{
sizeInBytes = size;
return v.binaryValue;
}
Type getType () const { return type; }
private:
union v
{
int64 intValue;
double floatValue;
TChar* stringValue;
char* binaryValue;
} v {};
uint32 size {0};
Type type {Type::kUninitialized};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (HostAttributeList, IAttributeList, IAttributeList::iid)
//-----------------------------------------------------------------------------
IPtr<IAttributeList> HostAttributeList::make ()
{
return owned (new HostAttributeList);
}
//-----------------------------------------------------------------------------
HostAttributeList::HostAttributeList () {FUNKNOWN_CTOR}
//-----------------------------------------------------------------------------
HostAttributeList::~HostAttributeList () noexcept {FUNKNOWN_DTOR}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setInt (AttrID aid, int64 value)
{
if (!aid)
return kInvalidArgument;
list[aid] = Attribute (value);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getInt (AttrID aid, int64& value)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kInteger)
{
value = it->second.intValue ();
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setFloat (AttrID aid, double value)
{
if (!aid)
return kInvalidArgument;
list[aid] = Attribute (value);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getFloat (AttrID aid, double& value)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kFloat)
{
value = it->second.floatValue ();
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setString (AttrID aid, const TChar* string)
{
if (!aid)
return kInvalidArgument;
// + 1 for the null-terminate
auto length = tstrlen (string) + 1;
list[aid] = Attribute (string, length);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getString (AttrID aid, TChar* string, uint32 sizeInBytes)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kString)
{
uint32 sizeInCodeUnit = 0;
const TChar* _string = it->second.stringValue (sizeInCodeUnit);
memcpy (string, _string, std::min<uint32> (sizeInCodeUnit * sizeof (TChar), sizeInBytes));
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::setBinary (AttrID aid, const void* data, uint32 sizeInBytes)
{
if (!aid)
return kInvalidArgument;
list[aid] = Attribute (data, sizeInBytes);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API HostAttributeList::getBinary (AttrID aid, const void*& data, uint32& sizeInBytes)
{
if (!aid)
return kInvalidArgument;
auto it = list.find (aid);
if (it != list.end () && it->second.getType () == Attribute::Type::kBinary)
{
data = it->second.binaryValue (sizeInBytes);
return kResultTrue;
}
sizeInBytes = 0;
return kResultFalse;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,100 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostclasses.h
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 hostclasses, example impl. for IHostApplication, IAttributeList and IMessage
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include <map>
#include <memory>
#include <string>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Implementation's example of IHostApplication.
\ingroup hostingBase
*/
class HostApplication : public IHostApplication
{
public:
HostApplication ();
virtual ~HostApplication () noexcept {FUNKNOWN_DTOR}
//--- IHostApplication ---------------
tresult PLUGIN_API getName (String128 name) override;
tresult PLUGIN_API createInstance (TUID cid, TUID _iid, void** obj) override;
DECLARE_FUNKNOWN_METHODS
PlugInterfaceSupport* getPlugInterfaceSupport () const { return mPlugInterfaceSupport; }
private:
IPtr<PlugInterfaceSupport> mPlugInterfaceSupport;
};
//------------------------------------------------------------------------
/** Example, ready to use implementation of IAttributeList.
\ingroup hostingBase
*/
class HostAttributeList final : public IAttributeList
{
public:
/** make a new attribute list instance */
static IPtr<IAttributeList> make ();
tresult PLUGIN_API setInt (AttrID aid, int64 value) override;
tresult PLUGIN_API getInt (AttrID aid, int64& value) override;
tresult PLUGIN_API setFloat (AttrID aid, double value) override;
tresult PLUGIN_API getFloat (AttrID aid, double& value) override;
tresult PLUGIN_API setString (AttrID aid, const TChar* string) override;
tresult PLUGIN_API getString (AttrID aid, TChar* string, uint32 sizeInBytes) override;
tresult PLUGIN_API setBinary (AttrID aid, const void* data, uint32 sizeInBytes) override;
tresult PLUGIN_API getBinary (AttrID aid, const void*& data, uint32& sizeInBytes) override;
virtual ~HostAttributeList () noexcept;
DECLARE_FUNKNOWN_METHODS
private:
HostAttributeList ();
struct Attribute;
std::map<std::string, Attribute> list;
};
//------------------------------------------------------------------------
/** Example implementation of IMessage.
\ingroup hostingBase
*/
class HostMessage final : public IMessage
{
public:
HostMessage ();
virtual ~HostMessage () noexcept;
const char* PLUGIN_API getMessageID () override;
void PLUGIN_API setMessageID (const char* messageID) override;
IAttributeList* PLUGIN_API getAttributes () override;
DECLARE_FUNKNOWN_METHODS
private:
char* messageId {nullptr};
IPtr<IAttributeList> attributeList;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,390 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostdataexchangehandler.cpp
// Created by : Steinberg, 06/2023
// Description : VST Data Exchange API Host Helper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "hostdataexchangehandler.h"
#include "../utility/alignedalloc.h"
#include "../utility/ringbuffer.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <algorithm>
#include <cassert>
#include <mutex>
#include <vector>
#ifdef _MSC_VER
#include <malloc.h>
#endif
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
struct HostDataExchangeHandler::Impl
: U::ImplementsNonDestroyable<U::Directly<IDataExchangeHandler>>
{
struct Block
{
Block () = default;
Block (uint32 blockSize, uint32 alignment, DataExchangeBlockID id)
: blockID (id), alignment (alignment)
{
data = aligned_alloc (blockSize, alignment);
}
Block (Block&& other) { *this = std::move (other); }
~Block () noexcept
{
if (data)
aligned_free (data, alignment);
}
Block& operator= (Block&& other)
{
data = other.data;
other.data = nullptr;
blockID = other.blockID;
other.blockID = InvalidDataExchangeBlockID;
alignment = other.alignment;
return *this;
}
void* data {nullptr};
DataExchangeBlockID blockID {InvalidDataExchangeBlockID};
uint32 alignment {0};
};
struct Queue
{
using BlockRingBuffer = OneReaderOneWriter::RingBuffer<Block>;
// we do the assumption that the std::vector does not allocate memory when we don't push
// more items than we reserve before
using BlockVector = std::vector<Block>;
Queue (IAudioProcessor* owner, IDataExchangeReceiver* receiver,
DataExchangeUserContextID userContext, uint32 blockSize, uint32 numBlocks,
uint32 alignment)
: owner (owner)
, receiver (receiver)
, userContext (userContext)
, blockSize (blockSize)
, numBlocks (numBlocks)
{
receiver->queueOpened (userContext, blockSize, wantBlocksOnBackgroundThread);
freeList.resize (numBlocks);
sendList.resize (numBlocks);
lockList.reserve (numBlocks);
freeListOnRTThread.reserve (numBlocks);
for (auto idx = 0u; idx < numBlocks; ++idx)
freeList.push (Block (blockSize, alignment, idx));
}
~Queue () noexcept
{
if (receiver)
receiver->queueClosed (userContext);
}
bool lock (DataExchangeBlock& block)
{
if (freeListOnRTThread.empty () == false)
{
auto& back = freeListOnRTThread.back ();
block.data = back.data;
block.size = blockSize;
block.blockID = back.blockID;
lockList.emplace_back (std::move (back));
freeListOnRTThread.pop_back ();
return true;
}
Block b;
if (freeList.pop (b))
{
block.data = b.data;
block.size = blockSize;
block.blockID = b.blockID;
lockList.emplace_back (std::move (b));
return true;
}
return false;
}
bool free (DataExchangeBlockID blockID)
{
if (blockID >= numBlocks)
return false;
auto it = std::find_if (lockList.begin (), lockList.end (),
[&] (const auto& el) { return el.blockID == blockID; });
if (it == lockList.end ())
return false;
Block b = std::move (*it);
freeListOnRTThread.emplace_back (std::move (b));
lockList.erase (it);
return true;
}
bool readyToSend (DataExchangeBlockID blockID)
{
if (blockID >= numBlocks)
return false;
auto it = std::find_if (lockList.begin (), lockList.end (),
[&] (const auto& el) { return el.blockID == blockID; });
if (it == lockList.end ())
return false;
Block b = std::move (*it);
sendList.push (std::move (b));
lockList.erase (it);
return true;
}
uint32 sendBlocks (DataExchangeQueueID queueID)
{
BlockVector blocks;
Block b;
while (sendList.pop (b))
{
blocks.emplace_back (std::move (b));
}
if (blocks.empty ())
return 0;
std::vector<DataExchangeBlock> debs;
std::for_each (blocks.begin (), blocks.end (), [&] (const auto& el) {
DataExchangeBlock block;
block.data = el.data;
block.size = blockSize;
block.blockID = el.blockID;
debs.push_back (block);
});
receiver->onDataExchangeBlocksReceived (userContext, static_cast<uint32> (debs.size ()),
debs.data (), wantBlocksOnBackgroundThread);
std::for_each (blocks.begin (), blocks.end (),
[&] (auto&& el) { freeList.push (std::move (el)); });
return static_cast<uint32> (debs.size ());
}
IAudioProcessor* owner;
IPtr<IDataExchangeReceiver> receiver;
DataExchangeUserContextID userContext {};
TBool wantBlocksOnBackgroundThread {false};
BlockRingBuffer freeList;
BlockVector freeListOnRTThread;
BlockVector lockList;
BlockRingBuffer sendList;
uint32 blockSize {0};
uint32 numBlocks {0};
};
using QueuePtr = std::unique_ptr<Queue>;
using QueueList = std::vector<QueuePtr>;
Impl (IDataExchangeHandlerHost& host, uint32 maxQueues) : host (host)
{
queues.resize (maxQueues);
}
void setQueue (DataExchangeQueueID queueID, QueuePtr&& queue)
{
queuesLock.lock ();
queues[queueID] = std::move (queue);
if (queues[queueID]->wantBlocksOnBackgroundThread)
++numOpenBackgroundQueues;
else
++numOpenMainThreadQueues;
queuesLock.unlock ();
host.onQueueOpened (queues[queueID]->owner, queueID,
queues[queueID]->wantBlocksOnBackgroundThread);
host.numberOfQueuesChanged (numOpenMainThreadQueues, numOpenBackgroundQueues);
}
tresult PLUGIN_API openQueue (IAudioProcessor* owner, uint32 blockSize, uint32 numBlocks,
uint32 alignment, DataExchangeUserContextID userContext,
DataExchangeQueueID* outID) override
{
if (!host.isMainThread ())
return kResultFalse;
if (outID == nullptr)
return kInvalidArgument;
if (!host.isProcessorInactive (owner))
return kResultFalse;
auto receiver = host.findDataExchangeReceiver (owner);
if (!receiver)
return kInvalidArgument;
if (!host.allowAllocateSize (blockSize, numBlocks, alignment))
return kOutOfMemory;
for (auto queueID = 0; queueID < queues.size (); ++queueID)
{
if (queues[queueID] == nullptr)
{
auto newQueue = std::make_unique<Queue> (owner, receiver, userContext, blockSize,
numBlocks, alignment);
setQueue (queueID, std::move (newQueue));
*outID = queueID;
return kResultTrue;
}
}
auto queueSize = queues.size ();
if (host.allowQueueListResize (static_cast<uint32> (queueSize + 1)))
{
queues.resize (queueSize + 1);
assert (queues.size () == queueSize + 1);
DataExchangeQueueID queueID = static_cast<DataExchangeQueueID> (queueSize);
auto newQueue = std::make_unique<Queue> (owner, receiver, userContext, blockSize,
numBlocks, alignment);
setQueue (queueID, std::move (newQueue));
*outID = queueID;
return kResultTrue;
}
return kOutOfMemory;
}
tresult PLUGIN_API closeQueue (DataExchangeQueueID queueID) override
{
if (!host.isMainThread ())
return kResultFalse;
if (queues[queueID])
{
if (!host.isProcessorInactive (queues[queueID]->owner))
return kResultFalse;
QueuePtr q;
queuesLock.lock ();
std::swap (q, queues[queueID]);
if (q->wantBlocksOnBackgroundThread)
--numOpenBackgroundQueues;
else
--numOpenMainThreadQueues;
queuesLock.unlock ();
host.onQueueClosed (q->owner, queueID, q->wantBlocksOnBackgroundThread);
host.numberOfQueuesChanged (numOpenMainThreadQueues, numOpenBackgroundQueues);
q.reset ();
return kResultTrue;
}
return kResultFalse;
}
tresult PLUGIN_API lockBlock (DataExchangeQueueID queueId, DataExchangeBlock* block) override
{
if (!block || queueId >= queues.size () || queues[queueId] == nullptr)
return kInvalidArgument;
if (queues[queueId]->lock (*block))
return kResultTrue;
return kOutOfMemory;
}
tresult PLUGIN_API freeBlock (DataExchangeQueueID queueId, DataExchangeBlockID blockID,
TBool sendToController) override
{
if (queueId >= queues.size () || queues[queueId] == nullptr)
return kInvalidArgument;
if (sendToController)
{
if (queues[queueId]->readyToSend (blockID))
{
++numReadyToSendBlocks;
host.newBlockReadyToBeSend (queueId);
return kResultTrue;
}
return kResultFalse;
}
return queues[queueId]->free (blockID) ? kResultTrue : kResultFalse;
}
bool sendBlocks (bool isMainThread, size_t queueID, uint32& numSendBlocks)
{
LockGuard guard (queuesLock);
if (auto& queue = queues[queueID])
{
if (queue->wantBlocksOnBackgroundThread != static_cast<TBool> (isMainThread))
{
numSendBlocks = queue->sendBlocks (static_cast<DataExchangeQueueID> (queueID));
}
return true;
}
return false;
}
uint32 sendBlocks (bool isMainThread, DataExchangeQueueID queueFilter)
{
if (queueFilter != InvalidDataExchangeQueueID)
{
if (queueFilter < queues.size ())
{
uint32 numSendBlocks;
if (sendBlocks (isMainThread, queueFilter, numSendBlocks))
return numSendBlocks;
}
return 0;
}
uint32 totalSendBlocks = 0;
uint32 openQueues = numOpenBackgroundQueues + numOpenMainThreadQueues;
for (auto queueID = 0u; queueID < queues.size (); ++queueID)
{
uint32 numSendBlocks;
if (sendBlocks (isMainThread, queueID, numSendBlocks))
{
numReadyToSendBlocks -= numSendBlocks;
totalSendBlocks += numSendBlocks;
if ((--openQueues) == 0)
break;
}
}
return totalSendBlocks;
}
IDataExchangeHandlerHost& host;
QueueList queues;
std::atomic<uint32> numReadyToSendBlocks {0};
std::atomic<uint32> numOpenMainThreadQueues {0};
std::atomic<uint32> numOpenBackgroundQueues {0};
using Mutex = std::recursive_mutex;
using LockGuard = std::lock_guard<Mutex>;
Mutex queuesLock;
};
//------------------------------------------------------------------------
HostDataExchangeHandler::HostDataExchangeHandler (IDataExchangeHandlerHost& host, uint32 maxQueues)
{
impl = std::make_unique<Impl> (host, maxQueues);
}
//------------------------------------------------------------------------
HostDataExchangeHandler::~HostDataExchangeHandler () noexcept = default;
//------------------------------------------------------------------------
IDataExchangeHandler* HostDataExchangeHandler::getInterface () const
{
return impl.get ();
}
//------------------------------------------------------------------------
uint32 HostDataExchangeHandler::sendMainThreadBlocks ()
{
return impl->sendBlocks (true, InvalidDataExchangeQueueID);
}
//------------------------------------------------------------------------
uint32 HostDataExchangeHandler::sendBackgroundBlocks (DataExchangeQueueID queueId)
{
return impl->sendBlocks (false, queueId);
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/hostdataexchangehandler.h
// Created by : Steinberg, 06/2023
// Description : VST Data Exchange API Host Helper
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstdataexchange.h"
#include <memory>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
struct IDataExchangeHandlerHost
{
virtual ~IDataExchangeHandlerHost () noexcept = default;
/** return if the audioprocessor is in an inactive state
* [main thread]
*/
virtual bool isProcessorInactive (IAudioProcessor* processor) = 0;
/** return the data exchange receiver (most likely the edit controller) for the processor
* [main thread]
*/
virtual IPtr<IDataExchangeReceiver> findDataExchangeReceiver (IAudioProcessor* processor) = 0;
/** check if the requested queue size should be allowed
* [main thread]
*/
virtual bool allowAllocateSize (uint32 blockSize, uint32 numBlocks, uint32 alignment) = 0;
/** check if this call is made on the main thread
* [any thread]
*/
virtual bool isMainThread () = 0;
/** check if the number of queues can be changed in this moment.
*
* this is only allowed if no other thread can access the IDataExchangeManagerHost in this
* moment
* [main thread]
*/
virtual bool allowQueueListResize (uint32 newNumQueues) = 0;
/** notification that the number of open queues changed
* [main thread]
*/
virtual void numberOfQueuesChanged (uint32 openMainThreadQueues,
uint32 openBackgroundThreadQueues) = 0;
/** notification that a new queue was opened */
virtual void onQueueOpened (IAudioProcessor* processor, DataExchangeQueueID queueID,
bool dispatchOnMainThread) = 0;
/** notification that a queue was closed */
virtual void onQueueClosed (IAudioProcessor* processor, DataExchangeQueueID queueID,
bool dispatchOnMainThread) = 0;
/** notification that a new block is ready to be send
* [process thread]
*/
virtual void newBlockReadyToBeSend (DataExchangeQueueID queueID) = 0;
};
//------------------------------------------------------------------------
struct HostDataExchangeHandler
{
/** Constructor
*
* allocate and deallocate this object on the main thread
*
* the number of queues is constant
*
* @param host the managing host
* @param maxQueues number of maximal allowed open queues
*/
HostDataExchangeHandler (IDataExchangeHandlerHost& host, uint32 maxQueues = 64);
~HostDataExchangeHandler () noexcept;
/** get the IHostDataExchangeManager interface
*
* the interface you must provide to the IAudioProcessor
*/
IDataExchangeHandler* getInterface () const;
/** send blocks
*
* the host should periodically call this method on the main thread to send all queued blocks
* which should be send on the main thread
*/
uint32 sendMainThreadBlocks ();
/** send blocks
*
* the host should call this on a dedicated background thread
* inside a mutex is used, so don't delete this object while calling this
*
* @param queueId only send blocks from the specified queue. If queueId is equal to
* InvalidDataExchangeQueueID all blocks from all queues are send.
*/
uint32 sendBackgroundBlocks (DataExchangeQueueID queueId = InvalidDataExchangeQueueID);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,327 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "module.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/utility/optional.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <sstream>
#include <utility>
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
//------------------------------------------------------------------------
FactoryInfo::FactoryInfo (PFactoryInfo&& other) noexcept
{
*this = std::move (other);
}
//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (FactoryInfo&& other) noexcept
{
info = std::move (other.info);
other.info = {};
return *this;
}
//------------------------------------------------------------------------
FactoryInfo& FactoryInfo::operator= (PFactoryInfo&& other) noexcept
{
info = std::move (other);
other = {};
return *this;
}
//------------------------------------------------------------------------
std::string FactoryInfo::vendor () const noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
return StringConvert::convert (info.vendor, PFactoryInfo::kNameSize);
}
//------------------------------------------------------------------------
std::string FactoryInfo::url () const noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
return StringConvert::convert (info.url, PFactoryInfo::kURLSize);
}
//------------------------------------------------------------------------
std::string FactoryInfo::email () const noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
return StringConvert::convert (info.email, PFactoryInfo::kEmailSize);
}
//------------------------------------------------------------------------
Steinberg::int32 FactoryInfo::flags () const noexcept
{
return info.flags;
}
//------------------------------------------------------------------------
bool FactoryInfo::classesDiscardable () const noexcept
{
return (info.flags & PFactoryInfo::kClassesDiscardable) != 0;
}
//------------------------------------------------------------------------
bool FactoryInfo::licenseCheck () const noexcept
{
return (info.flags & PFactoryInfo::kLicenseCheck) != 0;
}
//------------------------------------------------------------------------
bool FactoryInfo::componentNonDiscardable () const noexcept
{
return (info.flags & PFactoryInfo::kComponentNonDiscardable) != 0;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
PluginFactory::PluginFactory (const PluginFactoryPtr& factory) noexcept : factory (factory)
{
}
//------------------------------------------------------------------------
void PluginFactory::setHostContext (Steinberg::FUnknown* context) const noexcept
{
if (auto f = Steinberg::FUnknownPtr<Steinberg::IPluginFactory3> (factory))
f->setHostContext (context);
}
//------------------------------------------------------------------------
FactoryInfo PluginFactory::info () const noexcept
{
Steinberg::PFactoryInfo i;
factory->getFactoryInfo (&i);
return FactoryInfo (std::move (i));
}
//------------------------------------------------------------------------
uint32_t PluginFactory::classCount () const noexcept
{
auto count = factory->countClasses ();
assert (count >= 0);
return static_cast<uint32_t> (count);
}
//------------------------------------------------------------------------
PluginFactory::ClassInfos PluginFactory::classInfos () const noexcept
{
auto count = classCount ();
Optional<FactoryInfo> factoryInfo;
ClassInfos classes;
classes.reserve (count);
auto f3 = Steinberg::U::cast<Steinberg::IPluginFactory3> (factory);
auto f2 = Steinberg::U::cast<Steinberg::IPluginFactory2> (factory);
Steinberg::PClassInfo ci;
Steinberg::PClassInfo2 ci2;
Steinberg::PClassInfoW ci3;
for (uint32_t i = 0; i < count; ++i)
{
if (f3 && f3->getClassInfoUnicode (i, &ci3) == Steinberg::kResultTrue)
classes.emplace_back (ci3);
else if (f2 && f2->getClassInfo2 (i, &ci2) == Steinberg::kResultTrue)
classes.emplace_back (ci2);
else if (factory->getClassInfo (i, &ci) == Steinberg::kResultTrue)
classes.emplace_back (ci);
auto& classInfo = classes.back ();
if (classInfo.vendor ().empty ())
{
if (!factoryInfo)
factoryInfo = Optional<FactoryInfo> (info ());
classInfo.get ().vendor = factoryInfo->vendor ();
}
}
return classes;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
const UID& ClassInfo::ID () const noexcept
{
return data.classID;
}
//------------------------------------------------------------------------
int32_t ClassInfo::cardinality () const noexcept
{
return data.cardinality;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::category () const noexcept
{
return data.category;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::name () const noexcept
{
return data.name;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::vendor () const noexcept
{
return data.vendor;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::version () const noexcept
{
return data.version;
}
//------------------------------------------------------------------------
const std::string& ClassInfo::sdkVersion () const noexcept
{
return data.sdkVersion;
}
//------------------------------------------------------------------------
const ClassInfo::SubCategories& ClassInfo::subCategories () const noexcept
{
return data.subCategories;
}
//------------------------------------------------------------------------
Steinberg::uint32 ClassInfo::classFlags () const noexcept
{
return data.classFlags;
}
//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo& info) noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
}
//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfo2& info) noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
parseSubCategories (
StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
data.classFlags = info.classFlags;
}
//------------------------------------------------------------------------
ClassInfo::ClassInfo (const PClassInfoW& info) noexcept
{
namespace StringConvert = Steinberg::Vst::StringConvert;
data.classID = info.cid;
data.cardinality = info.cardinality;
data.category = StringConvert::convert (info.category, PClassInfo::kCategorySize);
data.name = StringConvert::convert (info.name, PClassInfo::kNameSize);
data.vendor = StringConvert::convert (info.vendor, PClassInfo2::kVendorSize);
data.version = StringConvert::convert (info.version, PClassInfo2::kVersionSize);
data.sdkVersion = StringConvert::convert (info.sdkVersion, PClassInfo2::kVersionSize);
parseSubCategories (
StringConvert::convert (info.subCategories, PClassInfo2::kSubCategoriesSize));
data.classFlags = info.classFlags;
}
//------------------------------------------------------------------------
void ClassInfo::parseSubCategories (const std::string& str) noexcept
{
std::stringstream stream (str);
std::string item;
while (std::getline (stream, item, '|'))
data.subCategories.emplace_back (std::move (item));
}
//------------------------------------------------------------------------
std::string ClassInfo::subCategoriesString () const noexcept
{
std::string result;
if (data.subCategories.empty ())
return result;
result = data.subCategories[0];
for (auto index = 1u; index < data.subCategories.size (); ++index)
result += "|" + data.subCategories[index];
return result;
}
//------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------
std::pair<size_t, size_t> rangeOfScaleFactor (const std::string& name)
{
auto result = std::make_pair (std::string::npos, std::string::npos);
size_t xIndex = name.find_last_of ('x');
if (xIndex == std::string::npos)
return result;
size_t indicatorIndex = name.find_last_of ('_');
if (indicatorIndex == std::string::npos)
return result;
if (xIndex < indicatorIndex)
return result;
result.first = indicatorIndex + 1;
result.second = xIndex;
return result;
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Optional<double> Module::Snapshot::decodeScaleFactor (const std::string& name)
{
auto range = rangeOfScaleFactor (name);
if (range.first == std::string::npos || range.second == std::string::npos)
return {};
std::string tmp (name.data () + range.first, range.second - range.first);
std::istringstream sstream (tmp);
sstream.imbue (std::locale::classic ());
sstream.precision (static_cast<std::streamsize> (3));
double result;
sstream >> result;
return Optional<double> (result);
}
//------------------------------------------------------------------------
Optional<UID> Module::Snapshot::decodeUID (const std::string& filename)
{
if (filename.size () < 45)
return {};
if (filename.find ("_snapshot") != 32)
return {};
auto uidStr = filename.substr (0, 32);
return UID::fromString (uidStr);
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,196 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module.h
// Created by : Steinberg, 08/2016
// Description : hosting module classes
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "../utility/uid.h"
#include "pluginterfaces/base/ipluginbase.h"
#include <utility>
#include <vector>
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
//------------------------------------------------------------------------
class FactoryInfo
{
public:
//------------------------------------------------------------------------
using PFactoryInfo = Steinberg::PFactoryInfo;
FactoryInfo () noexcept {}
~FactoryInfo () noexcept {}
FactoryInfo (const FactoryInfo&) noexcept = default;
FactoryInfo (PFactoryInfo&&) noexcept;
FactoryInfo (FactoryInfo&&) noexcept = default;
FactoryInfo& operator= (const FactoryInfo&) noexcept = default;
FactoryInfo& operator= (FactoryInfo&&) noexcept;
FactoryInfo& operator= (PFactoryInfo&&) noexcept;
std::string vendor () const noexcept;
std::string url () const noexcept;
std::string email () const noexcept;
Steinberg::int32 flags () const noexcept;
bool classesDiscardable () const noexcept;
bool licenseCheck () const noexcept;
bool componentNonDiscardable () const noexcept;
PFactoryInfo& get () noexcept { return info; }
//------------------------------------------------------------------------
private:
PFactoryInfo info {};
};
//------------------------------------------------------------------------
class ClassInfo
{
public:
//------------------------------------------------------------------------
using SubCategories = std::vector<std::string>;
using PClassInfo = Steinberg::PClassInfo;
using PClassInfo2 = Steinberg::PClassInfo2;
using PClassInfoW = Steinberg::PClassInfoW;
//------------------------------------------------------------------------
ClassInfo () noexcept {}
explicit ClassInfo (const PClassInfo& info) noexcept;
explicit ClassInfo (const PClassInfo2& info) noexcept;
explicit ClassInfo (const PClassInfoW& info) noexcept;
ClassInfo (const ClassInfo&) = default;
ClassInfo& operator= (const ClassInfo&) = default;
ClassInfo (ClassInfo&&) = default;
ClassInfo& operator= (ClassInfo&&) = default;
const UID& ID () const noexcept;
int32_t cardinality () const noexcept;
const std::string& category () const noexcept;
const std::string& name () const noexcept;
const std::string& vendor () const noexcept;
const std::string& version () const noexcept;
const std::string& sdkVersion () const noexcept;
const SubCategories& subCategories () const noexcept;
std::string subCategoriesString () const noexcept;
Steinberg::uint32 classFlags () const noexcept;
struct Data
{
UID classID;
int32_t cardinality;
std::string category;
std::string name;
std::string vendor;
std::string version;
std::string sdkVersion;
SubCategories subCategories;
Steinberg::uint32 classFlags = 0;
};
Data& get () noexcept { return data; }
//------------------------------------------------------------------------
private:
void parseSubCategories (const std::string& str) noexcept;
Data data {};
};
//------------------------------------------------------------------------
class PluginFactory
{
public:
//------------------------------------------------------------------------
using ClassInfos = std::vector<ClassInfo>;
using PluginFactoryPtr = Steinberg::IPtr<Steinberg::IPluginFactory>;
//------------------------------------------------------------------------
explicit PluginFactory (const PluginFactoryPtr& factory) noexcept;
void setHostContext (Steinberg::FUnknown* context) const noexcept;
FactoryInfo info () const noexcept;
uint32_t classCount () const noexcept;
ClassInfos classInfos () const noexcept;
template <typename T>
Steinberg::IPtr<T> createInstance (const UID& classID) const noexcept;
const PluginFactoryPtr& get () const noexcept { return factory; }
//------------------------------------------------------------------------
private:
PluginFactoryPtr factory;
};
//------------------------------------------------------------------------
//------------------------------------------------------------------------
class Module
{
public:
//------------------------------------------------------------------------
struct Snapshot
{
struct ImageDesc
{
double scaleFactor {1.};
std::string path;
};
UID uid;
std::vector<ImageDesc> images;
static Optional<double> decodeScaleFactor (const std::string& path);
static Optional<UID> decodeUID (const std::string& filename);
};
using Ptr = std::shared_ptr<Module>;
using PathList = std::vector<std::string>;
using SnapshotList = std::vector<Snapshot>;
//------------------------------------------------------------------------
static Ptr create (const std::string& path, std::string& errorDescription);
static PathList getModulePaths ();
static SnapshotList getSnapshots (const std::string& modulePath);
/** get the path to the module info json file if it exists */
static Optional<std::string> getModuleInfoPath (const std::string& modulePath);
/** validate the bundle structure */
static bool validateBundleStructure (const std::string& path, std::string& errorDescription);
const std::string& getName () const noexcept { return name; }
const std::string& getPath () const noexcept { return path; }
const PluginFactory& getFactory () const noexcept { return factory; }
bool isBundle () const noexcept { return hasBundleStructure; }
//------------------------------------------------------------------------
protected:
virtual ~Module () noexcept = default;
virtual bool load (const std::string& path, std::string& errorDescription) = 0;
PluginFactory factory {nullptr};
std::string name;
std::string path;
bool hasBundleStructure {true};
};
//------------------------------------------------------------------------
template <typename T>
inline Steinberg::IPtr<T> PluginFactory::createInstance (const UID& classID) const noexcept
{
T* obj = nullptr;
if (factory->createInstance (classID.data (), T::iid, reinterpret_cast<void**> (&obj)) ==
Steinberg::kResultTrue)
return Steinberg::owned (obj);
return nullptr;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,391 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_linux.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes (linux implementation)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "module.h"
#include "public.sdk/source/vst/utility/optional.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <algorithm>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#if SMTG_CPP17
#if __has_include(<filesystem>)
#define USE_EXPERIMENTAL_FS 0
#elif __has_include(<experimental/filesystem>)
#define USE_EXPERIMENTAL_FS 1
#endif
#else // !SMTG_CPP17
#define USE_EXPERIMENTAL_FS 1
#endif // SMTG_CPP17
#if USE_EXPERIMENTAL_FS == 1
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#else // USE_EXPERIMENTAL_FS == 0
#include <filesystem>
namespace filesystem = std::filesystem;
#endif // USE_EXPERIMENTAL_FS
//------------------------------------------------------------------------
extern "C" {
using ModuleEntryFunc = bool (PLUGIN_API*) (void*);
using ModuleExitFunc = bool (PLUGIN_API*) ();
}
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
using Path = filesystem::path;
//------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------
Optional<std::string> getCurrentMachineName ()
{
struct utsname unameData;
int res = uname (&unameData);
if (res != 0)
return {};
return {unameData.machine};
}
//------------------------------------------------------------------------
Optional<Path> getApplicationPath ()
{
std::string appPath = "";
pid_t pid = getpid ();
char buf[10];
sprintf (buf, "%d", pid);
std::string _link = "/proc/";
_link.append (buf);
_link.append ("/exe");
char proc[1024];
int ch = readlink (_link.c_str (), proc, 1024);
if (ch == -1)
return {};
proc[ch] = 0;
appPath = proc;
std::string::size_type t = appPath.find_last_of ("/");
appPath = appPath.substr (0, t);
return Path {appPath};
}
//------------------------------------------------------------------------
class LinuxModule : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
return reinterpret_cast<T> (dlsym (mModule, name));
}
~LinuxModule () override
{
factory = PluginFactory (nullptr);
if (mModule)
{
if (auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit"))
moduleExit ();
dlclose (mModule);
}
}
static Optional<Path> getSOPath (const std::string& inPath)
{
Path modulePath {inPath};
if (!filesystem::is_directory (modulePath))
return {};
auto stem = modulePath.stem ();
modulePath /= "Contents";
if (!filesystem::is_directory (modulePath))
return {};
// use the Machine Hardware Name (from uname cmd-line) as prefix for "-linux"
auto machine = getCurrentMachineName ();
if (!machine)
return {};
modulePath /= *machine + "-linux";
if (!filesystem::is_directory (modulePath))
return {};
modulePath /= stem;
modulePath += ".so";
return Optional<Path> (std::move (modulePath));
}
bool load (const std::string& inPath, std::string& errorDescription) override
{
auto modulePath = getSOPath (inPath);
if (!modulePath)
{
errorDescription = inPath + " is not a module directory.";
return false;
}
mModule = dlopen (reinterpret_cast<const char*> (modulePath->generic_string ().data ()),
RTLD_LAZY);
if (!mModule)
{
errorDescription = "dlopen failed.\n";
errorDescription += dlerror ();
return false;
}
// ModuleEntry is mandatory
auto moduleEntry = getFunctionPointer<ModuleEntryFunc> ("ModuleEntry");
if (!moduleEntry)
{
errorDescription =
"The shared library does not export the required 'ModuleEntry' function";
return false;
}
// ModuleExit is mandatory
auto moduleExit = getFunctionPointer<ModuleExitFunc> ("ModuleExit");
if (!moduleExit)
{
errorDescription =
"The shared library does not export the required 'ModuleExit' function";
return false;
}
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription =
"The shared library does not export the required 'GetPluginFactory' function";
return false;
}
if (!moduleEntry (mModule))
{
errorDescription = "Calling 'ModuleEntry' failed";
return false;
}
auto f = Steinberg::U::cast<Steinberg::IPluginFactory> (owned (factoryProc ()));
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}
void* mModule {nullptr};
};
//------------------------------------------------------------------------
void findFilesWithExt (const std::string& path, const std::string& ext, Module::PathList& pathList,
bool recursive = true)
{
try
{
for (auto& p : filesystem::directory_iterator (path))
{
if (p.path ().extension () == ext)
{
pathList.push_back (p.path ().generic_string ());
}
else if (recursive && p.status ().type () == filesystem::file_type::directory)
{
findFilesWithExt (p.path (), ext, pathList);
}
}
}
catch (...)
{
}
}
//------------------------------------------------------------------------
void findModules (const std::string& path, Module::PathList& pathList)
{
findFilesWithExt (path, ".vst3", pathList);
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto _module = std::make_shared<LinuxModule> ();
if (_module->load (path, errorDescription))
{
_module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
_module->name = {it.base (), path.end ()};
return _module;
}
return nullptr;
}
//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
/* VST3 component locations on linux :
* User privately installed : $HOME/.vst3/
* Distribution installed : /usr/lib/vst3/
* Locally installed : /usr/local/lib/vst3/
* Application : /$APPFOLDER/vst3/
*/
const auto systemPaths = {"/usr/lib/vst3/", "/usr/local/lib/vst3/"};
PathList list;
if (auto homeDir = getenv ("HOME"))
{
filesystem::path homePath (homeDir);
homePath /= ".vst3";
findModules (homePath.generic_string (), list);
}
for (auto path : systemPaths)
findModules (path, list);
// application level
auto appPath = getApplicationPath ();
if (appPath)
{
*appPath /= "vst3";
findModules (appPath->generic_string (), list);
}
return list;
}
//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList result;
filesystem::path path (modulePath);
path /= "Contents";
path /= "Resources";
path /= "Snapshots";
PathList pngList;
findFilesWithExt (path, ".png", pngList, false);
for (auto& png : pngList)
{
filesystem::path p (png);
auto filename = p.filename ().generic_string ();
auto uid = Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;
Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (png);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
return result;
}
//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
filesystem::path path (modulePath);
path /= "Contents";
path /= "Resources";
path /= "moduleinfo.json";
if (filesystem::exists (path))
return {path.generic_string ()};
return {};
}
//------------------------------------------------------------------------
bool Module::validateBundleStructure (const std::string& modulePath, std::string& errorDescription)
{
filesystem::path path (modulePath);
auto moduleName = path.filename ();
path /= "Contents";
if (filesystem::exists (path) == false)
{
errorDescription = "Expecting 'Contents' as first subfolder.";
return false;
}
auto machine = getCurrentMachineName ();
if (!machine)
{
errorDescription = "Could not get the current machine name.";
return false;
}
path /= *machine + "-linux";
if (filesystem::exists (path) == false)
{
errorDescription = "Expecting '" + *machine + "-linux' as architecture subfolder.";
return false;
}
moduleName.replace_extension (".so");
path /= moduleName;
if (filesystem::exists (path) == false)
{
errorDescription = "Shared library name is not equal to bundle folder name. Must be '" +
moduleName.string () + "'.";
return false;
}
return true;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,383 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_mac.mm
// Created by : Steinberg, 08/2016
// Description : hosting module classes (macOS implementation)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "module.h"
#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#if !__has_feature(objc_arc)
#error this file needs to be compiled with automatic reference counting enabled
#endif
//------------------------------------------------------------------------
extern "C" {
typedef bool (*BundleEntryFunc) (CFBundleRef);
typedef bool (*BundleExitFunc) ();
}
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
//------------------------------------------------------------------------
namespace {
//------------------------------------------------------------------------
template <typename T>
class CFPtr
{
public:
inline CFPtr (const T& obj = nullptr) : obj (obj) {}
inline CFPtr (CFPtr&& other) { *this = other; }
inline ~CFPtr ()
{
if (obj)
CFRelease (obj);
}
inline CFPtr& operator= (CFPtr&& other)
{
obj = other.obj;
other.obj = nullptr;
return *this;
}
inline CFPtr& operator= (const T& o)
{
if (obj)
CFRelease (obj);
obj = o;
return *this;
}
inline operator T () const { return obj; } // act as T
private:
CFPtr (const CFPtr& other) = delete;
CFPtr& operator= (const CFPtr& other) = delete;
T obj = nullptr;
};
//------------------------------------------------------------------------
class MacModule : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
assert (bundle);
CFPtr<CFStringRef> functionName (
CFStringCreateWithCString (kCFAllocatorDefault, name, kCFStringEncodingASCII));
return reinterpret_cast<T> (CFBundleGetFunctionPointerForName (bundle, functionName));
}
bool loadInternal (const std::string& path, std::string& errorDescription)
{
CFPtr<CFURLRef> url (CFURLCreateFromFileSystemRepresentation (
kCFAllocatorDefault, reinterpret_cast<const UInt8*> (path.data ()), path.length (),
true));
if (!url)
return false;
bundle = CFBundleCreate (kCFAllocatorDefault, url);
CFErrorRef error = nullptr;
if (!bundle || !CFBundleLoadExecutableAndReturnError (bundle, &error))
{
if (error)
{
CFPtr<CFStringRef> errorString (CFErrorCopyDescription (error));
if (errorString)
{
auto stringLength = CFStringGetLength (errorString);
auto maxSize =
CFStringGetMaximumSizeForEncoding (stringLength, kCFStringEncodingUTF8);
auto buffer = std::make_unique<char[]> (maxSize);
if (CFStringGetCString (errorString, buffer.get (), maxSize,
kCFStringEncodingUTF8))
errorDescription = buffer.get ();
CFRelease (error);
}
}
else
{
errorDescription = "Could not create Bundle for path: " + path;
}
return false;
}
// bundleEntry is mandatory
auto bundleEntry = getFunctionPointer<BundleEntryFunc> ("bundleEntry");
if (!bundleEntry)
{
errorDescription = "Bundle does not export the required 'bundleEntry' function";
return false;
}
// bundleExit is mandatory
auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit");
if (!bundleExit)
{
errorDescription = "Bundle does not export the required 'bundleExit' function";
return false;
}
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription = "Bundle does not export the required 'GetPluginFactory' function";
return false;
}
if (!bundleEntry (bundle))
{
errorDescription = "Calling 'bundleEntry' failed";
return false;
}
auto f = owned (factoryProc ());
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}
bool load (const std::string& path, std::string& errorDescription) override
{
if (!path.empty () && path[0] != '/')
{
auto buffer = std::make_unique<char[]> (PATH_MAX);
auto workDir = getcwd (buffer.get (), PATH_MAX);
if (workDir)
{
std::string wd (workDir);
wd += "/";
if (loadInternal (wd + path, errorDescription))
{
name = path;
return true;
}
return false;
}
}
return loadInternal (path, errorDescription);
}
~MacModule () override
{
factory = PluginFactory (nullptr);
if (bundle)
{
if (auto bundleExit = getFunctionPointer<BundleExitFunc> ("bundleExit"))
bundleExit ();
}
}
CFPtr<CFBundleRef> bundle;
};
//------------------------------------------------------------------------
void findModulesInDirectory (NSURL* dirUrl, Module::PathList& result)
{
dirUrl = [dirUrl URLByResolvingSymlinksInPath];
if (!dirUrl)
return;
NSDirectoryEnumerator* enumerator = [[NSFileManager defaultManager]
enumeratorAtURL: dirUrl
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsPackageDescendants
errorHandler:nil];
for (NSURL* url in enumerator)
{
if ([[[url lastPathComponent] pathExtension] isEqualToString:@"vst3"])
{
CFPtr<CFArrayRef> archs (
CFBundleCopyExecutableArchitecturesForURL (static_cast<CFURLRef> (url)));
if (archs)
result.emplace_back ([url.path UTF8String]);
}
else
{
id resValue;
if (![url getResourceValue:&resValue forKey:NSURLIsSymbolicLinkKey error:nil])
continue;
if (!static_cast<NSNumber*> (resValue).boolValue)
continue;
auto resolvedUrl = [url URLByResolvingSymlinksInPath];
if (![resolvedUrl getResourceValue:&resValue forKey:NSURLIsDirectoryKey error:nil])
continue;
if (!static_cast<NSNumber*> (resValue).boolValue)
continue;
findModulesInDirectory (resolvedUrl, result);
}
}
}
//------------------------------------------------------------------------
void getModules (NSSearchPathDomainMask domain, Module::PathList& result)
{
NSURL* libraryUrl = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory
inDomain:domain
appropriateForURL:nil
create:NO
error:nil];
if (libraryUrl == nil)
return;
NSURL* audioUrl = [libraryUrl URLByAppendingPathComponent:@"Audio"];
if (audioUrl == nil)
return;
NSURL* plugInsUrl = [audioUrl URLByAppendingPathComponent:@"Plug-Ins"];
if (plugInsUrl == nil)
return;
NSURL* vst3Url =
[[plugInsUrl URLByAppendingPathComponent:@"VST3"] URLByResolvingSymlinksInPath];
if (vst3Url == nil)
return;
findModulesInDirectory (vst3Url, result);
}
//------------------------------------------------------------------------
void getApplicationModules (Module::PathList& result)
{
auto bundle = CFBundleGetMainBundle ();
if (!bundle)
return;
auto bundleUrl = static_cast<NSURL*> (CFBridgingRelease (CFBundleCopyBundleURL (bundle)));
if (!bundleUrl)
return;
auto resUrl = [bundleUrl URLByAppendingPathComponent:@"Contents"];
if (!resUrl)
return;
auto vst3Url = [resUrl URLByAppendingPathComponent:@"VST3"];
if (!vst3Url)
return;
findModulesInDirectory (vst3Url, result);
}
//------------------------------------------------------------------------
void getModuleSnapshots (const std::string& path, Module::SnapshotList& result)
{
auto* nsString = [NSString stringWithUTF8String:path.data ()];
if (!nsString)
return;
auto bundleUrl = [NSURL fileURLWithPath:nsString];
if (!bundleUrl)
return;
auto urls = [NSBundle URLsForResourcesWithExtension:@"png"
subdirectory:@"Snapshots"
inBundleWithURL:bundleUrl];
if (!urls || [urls count] == 0)
return;
for (NSURL* url in urls)
{
std::string fullpath ([[url path] UTF8String]);
std::string filename ([[[url path] lastPathComponent] UTF8String]);
auto uid = Module::Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Module::Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;
Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (fullpath);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto module = std::make_shared<MacModule> ();
if (module->load (path, errorDescription))
{
module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
module->name = {it.base (), path.end ()};
return std::move (module);
}
return nullptr;
}
//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
PathList list;
getModules (NSUserDomainMask, list);
getModules (NSLocalDomainMask, list);
// TODO getModules (NSNetworkDomainMask, list);
getApplicationModules (list);
return list;
}
//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList list;
getModuleSnapshots (modulePath, list);
return list;
}
//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
auto* nsString = [NSString stringWithUTF8String:modulePath.data ()];
if (!nsString)
return {};
auto bundleUrl = [NSURL fileURLWithPath:nsString];
if (!bundleUrl)
return {};
auto moduleInfoUrl = [NSBundle URLForResource:@"moduleinfo"
withExtension:@"json"
subdirectory:nullptr
inBundleWithURL:bundleUrl];
if (!moduleInfoUrl)
return {};
NSError* error = nil;
if ([moduleInfoUrl checkResourceIsReachableAndReturnError:&error])
return {std::string (moduleInfoUrl.fileSystemRepresentation)};
return {};
}
//------------------------------------------------------------------------
bool Module::validateBundleStructure (const std::string& path, std::string& errorDescription)
{
auto* nsString = [NSString stringWithUTF8String:path.data ()];
if (!nsString)
return false;
return [NSBundle bundleWithPath:nsString] != nil;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,746 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/module_win32.cpp
// Created by : Steinberg, 08/2016
// Description : hosting module classes (win32 implementation)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "module.h"
#include "public.sdk/source/vst/utility/optional.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <shlobj.h>
#include <windows.h>
#include <algorithm>
#include <iostream>
#if SMTG_CPP17
#if __has_include(<filesystem>)
#define USE_FILESYSTEM 1
#elif __has_include(<experimental/filesystem>)
#define USE_FILESYSTEM 0
#endif
#else // !SMTG_CPP17
#define USE_FILESYSTEM 0
#endif // SMTG_CPP17
#if USE_FILESYSTEM == 1
#include <filesystem>
namespace filesystem = std::filesystem;
#else // USE_FILESYSTEM == 0
// The <experimental/filesystem> header is deprecated. It is superseded by the C++17 <filesystem>
// header. You can define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING to silence the
// warning, otherwise the build will fail in VS2019 16.3.0
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
namespace filesystem = std::experimental::filesystem;
#endif // USE_FILESYSTEM
#pragma comment(lib, "Shell32")
//------------------------------------------------------------------------
extern "C" {
using InitModuleFunc = bool (PLUGIN_API*) ();
using ExitModuleFunc = bool (PLUGIN_API*) ();
}
//------------------------------------------------------------------------
namespace VST3 {
namespace Hosting {
constexpr unsigned long kIPPathNameMax = 1024;
//------------------------------------------------------------------------
namespace {
#define USE_OLE !USE_FILESYSTEM
// for testing only
#if 0 // DEVELOPMENT
#define LOG_ENABLE 1
#else
#define LOG_ENABLE 0
#endif
#if SMTG_PLATFORM_64
#if SMTG_OS_WINDOWS_ARM
#if SMTG_CPU_ARM_64EC
constexpr auto architectureString = "arm64ec-win";
constexpr auto architectureX64String = "x86_64-win";
#else // !SMTG_CPU_ARM_64EC
constexpr auto architectureString = "arm64-win";
#endif // SMTG_CPU_ARM_64EC
constexpr auto architectureArm64XString = "arm64x-win";
#else // !SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "x86_64-win";
#endif // SMTG_OS_WINDOWS_ARM
#else // !SMTG_PLATFORM_64
#if SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "arm-win";
#else // !SMTG_OS_WINDOWS_ARM
constexpr auto architectureString = "x86-win";
#endif // SMTG_OS_WINDOWS_ARM
#endif // SMTG_PLATFORM_64
#if USE_OLE
//------------------------------------------------------------------------
struct Ole
{
static Ole& instance ()
{
static Ole gInstance;
return gInstance;
}
private:
Ole () { OleInitialize (nullptr); }
~Ole () { OleUninitialize (); }
};
#endif // USE_OLE
//------------------------------------------------------------------------
class Win32Module : public Module
{
public:
template <typename T>
T getFunctionPointer (const char* name)
{
return reinterpret_cast<T> (GetProcAddress (mModule, name));
}
~Win32Module () override
{
factory = PluginFactory (nullptr);
if (mModule)
{
// ExitDll is optional
if (auto dllExit = getFunctionPointer<ExitModuleFunc> ("ExitDll"))
dllExit ();
FreeLibrary ((HMODULE)mModule);
}
}
//--- -----------------------------------------------------------------------
HINSTANCE loadAsPackage (const std::string& inPath, std::string& errorDescription,
const char* archString = architectureString)
{
namespace StringConvert = Steinberg::Vst::StringConvert;
filesystem::path p (inPath);
auto filename = p.filename ();
p /= "Contents";
p /= archString;
p /= filename;
const std::wstring wString = p.generic_wstring ();
HINSTANCE instance = LoadLibraryW (reinterpret_cast<LPCWSTR> (wString.data ()));
#if SMTG_CPU_ARM_64EC
if (instance == nullptr)
instance = loadAsPackage (inPath, errorDescription, architectureArm64XString);
if (instance == nullptr)
instance = loadAsPackage (inPath, errorDescription, architectureX64String);
#endif // SMTG_CPU_ARM_64EC
if (instance == nullptr)
getLastError (p.string (), errorDescription);
return instance;
}
//--- -----------------------------------------------------------------------
HINSTANCE loadAsDll (const std::string& inPath, std::string& errorDescription)
{
namespace StringConvert = Steinberg::Vst::StringConvert;
auto wideStr = StringConvert::convert (inPath);
HINSTANCE instance = LoadLibraryW (reinterpret_cast<LPCWSTR> (wideStr.data ()));
if (instance == nullptr)
{
getLastError (inPath, errorDescription);
}
else
{
hasBundleStructure = false;
}
return instance;
}
//--- -----------------------------------------------------------------------
bool load (const std::string& inPath, std::string& errorDescription) override
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
const filesystem::path tmp (inPath);
#else
const filesystem::path tmp = filesystem::u8path (inPath);
#endif // SMTG_CPP20
std::error_code ec;
if (filesystem::is_directory (tmp, ec))
{
// try as package (bundle)
mModule = loadAsPackage (inPath, errorDescription);
}
else
{
// try old definition without package
mModule = loadAsDll (inPath, errorDescription);
}
if (mModule == nullptr)
return false;
auto factoryProc = getFunctionPointer<GetFactoryProc> ("GetPluginFactory");
if (!factoryProc)
{
errorDescription = "The dll does not export the required 'GetPluginFactory' function";
return false;
}
// InitDll is optional
auto dllEntry = getFunctionPointer<InitModuleFunc> ("InitDll");
if (dllEntry && !dllEntry ())
{
errorDescription = "Calling 'InitDll' failed";
return false;
}
auto f = Steinberg::U::cast<Steinberg::IPluginFactory> (owned (factoryProc ()));
if (!f)
{
errorDescription = "Calling 'GetPluginFactory' returned nullptr";
return false;
}
factory = PluginFactory (f);
return true;
}
HINSTANCE mModule {nullptr};
private:
//--- -----------------------------------------------------------------------
void getLastError (const std::string& inPath, std::string& errorDescription)
{
auto lastError = GetLastError ();
LPVOID lpMessageBuffer {nullptr};
if (FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, nullptr,
lastError, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMessageBuffer, 0, nullptr) > 0)
{
errorDescription = "LoadLibraryW failed for path " + inPath + ": " +
std::string ((char*)lpMessageBuffer);
LocalFree (lpMessageBuffer);
}
else
{
errorDescription = "LoadLibraryW failed with error number: " +
std::to_string (lastError) + " for path " + inPath;
}
}
};
//------------------------------------------------------------------------
bool openVST3Package (const filesystem::path& p, const char* archString,
filesystem::path* result = nullptr)
{
auto path = p;
path /= "Contents";
path /= archString;
path /= p.filename ();
const std::wstring wString = path.generic_wstring ();
auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.data ()), GENERIC_READ,
FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle (hFile);
if (result)
*result = path;
return true;
}
return false;
}
//------------------------------------------------------------------------
bool checkVST3Package (const filesystem::path& p, filesystem::path* result = nullptr,
const char* archString = architectureString)
{
if (openVST3Package (p, archString, result))
return true;
#if SMTG_CPU_ARM_64EC
if (openVST3Package (p, architectureArm64XString, result))
return true;
if (openVST3Package (p, architectureX64String, result))
return true;
#endif // SMTG_CPU_ARM_64EC
return false;
}
//------------------------------------------------------------------------
bool isFolderSymbolicLink (const filesystem::path& p)
{
#if USE_FILESYSTEM
std::error_code ec;
if (filesystem::is_symlink (p, ec))
return true;
#else
const std::wstring wString = p.generic_wstring ();
auto attrib = GetFileAttributesW (reinterpret_cast<LPCWSTR> (wString.data ()));
if (attrib & FILE_ATTRIBUTE_REPARSE_POINT)
{
auto hFile = CreateFileW (reinterpret_cast<LPCWSTR> (wString.data ()), GENERIC_READ,
FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return true;
CloseHandle (hFile);
}
#endif // USE_FILESYSTEM
return false;
}
//------------------------------------------------------------------------
Optional<std::string> getKnownFolder (REFKNOWNFOLDERID folderID)
{
namespace StringConvert = Steinberg::Vst::StringConvert;
PWSTR wideStr {};
if (FAILED (SHGetKnownFolderPath (folderID, 0, nullptr, &wideStr)))
return {};
return StringConvert::convert (Steinberg::wscast (wideStr));
}
//------------------------------------------------------------------------
VST3::Optional<filesystem::path> resolveShellLink (const filesystem::path& p)
{
#if USE_FILESYSTEM
std::error_code ec;
auto target = filesystem::read_symlink (p, ec);
if (ec)
return {};
else
return { target.lexically_normal () };
#elif USE_OLE
Ole::instance ();
IShellLink* shellLink = nullptr;
if (!SUCCEEDED (CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLink, reinterpret_cast<LPVOID*> (&shellLink))))
return {};
IPersistFile* persistFile = nullptr;
if (!SUCCEEDED (
shellLink->QueryInterface (IID_IPersistFile, reinterpret_cast<void**> (&persistFile))))
return {};
if (!SUCCEEDED (persistFile->Load (p.wstring ().data (), STGM_READ)))
return {};
if (!SUCCEEDED (shellLink->Resolve (nullptr, MAKELONG (SLR_NO_UI, 500))))
return {};
WCHAR resolvedPath[kIPPathNameMax];
if (!SUCCEEDED (shellLink->GetPath (resolvedPath, kIPPathNameMax, nullptr, SLGP_SHORTPATH)))
return {};
std::wstring longPath;
longPath.resize (kIPPathNameMax);
auto numChars =
GetLongPathNameW (resolvedPath, const_cast<wchar_t*> (longPath.data ()), kIPPathNameMax);
if (!numChars)
return {};
longPath.resize (numChars);
persistFile->Release ();
shellLink->Release ();
return {filesystem::path (longPath)};
#else
return {};
#endif // USE_FILESYSTEM
}
//------------------------------------------------------------------------
void addToPathList (Module::PathList& pathList, const std::string& toAdd)
{
#if LOG_ENABLE
std::cout << "=> add: " << toAdd << "\n";
#endif
pathList.push_back (toAdd);
}
//------------------------------------------------------------------------
void findFilesWithExt (const filesystem::path& path, const std::string& ext,
Module::PathList& pathList, bool recursive = true)
{
for (auto& p : filesystem::directory_iterator (path))
{
#if USE_FILESYSTEM
filesystem::path finalPath (p);
if (isFolderSymbolicLink (p))
{
if (auto res = resolveShellLink (p))
{
finalPath = *res;
std::error_code ec;
if (!filesystem::exists (finalPath, ec))
continue;
}
else
continue;
}
const auto& cpExt = finalPath.extension ();
if (cpExt == ext)
{
filesystem::path result;
if (checkVST3Package (finalPath, &result))
{
#if SMTG_CPP20
std::u8string u8str = result.generic_u8string ();
std::string str;
str.assign (std::begin (u8str), std::end (u8str));
addToPathList (pathList, str);
#else
addToPathList (pathList, result.generic_u8string ());
#endif // SMTG_CPP20
continue;
}
}
std::error_code ec;
if (filesystem::is_directory (finalPath, ec))
{
if (recursive)
findFilesWithExt (finalPath, ext, pathList, recursive);
}
else if (cpExt == ext)
{
#if SMTG_CPP20
std::u8string u8str = finalPath.generic_u8string ();
std::string str;
str.assign (std::begin (u8str), std::end (u8str));
addToPathList (pathList, str);
#else
addToPathList (pathList, finalPath.generic_u8string ());
#endif // SMTG_CPP20
}
#else // !USE_FILESYSTEM
const auto& cp = p.path ();
const auto& cpExt = cp.extension ();
if (cpExt == ext)
{
if ((p.status ().type () == filesystem::file_type::directory) ||
isFolderSymbolicLink (p))
{
filesystem::path result;
if (checkVST3Package (p, &result))
{
addToPathList (pathList, result.generic_u8string ());
continue;
}
findFilesWithExt (cp, ext, pathList, recursive);
}
else
addToPathList (pathList, cp.generic_u8string ());
}
else if (recursive)
{
if (p.status ().type () == filesystem::file_type::directory)
{
findFilesWithExt (cp, ext, pathList, recursive);
}
else if (cpExt == ".lnk")
{
if (auto resolvedLink = resolveShellLink (cp))
{
if (resolvedLink->extension () == ext)
{
if (filesystem::is_directory (*resolvedLink) ||
isFolderSymbolicLink (*resolvedLink))
{
filesystem::path result;
if (checkVST3Package (*resolvedLink, &result))
{
addToPathList (pathList, result.generic_u8string ());
continue;
}
findFilesWithExt (*resolvedLink, ext, pathList, recursive);
}
else
addToPathList (pathList, resolvedLink->generic_u8string ());
}
else if (filesystem::is_directory (*resolvedLink))
{
const auto& str = resolvedLink->generic_u8string ();
if (cp.generic_u8string ().compare (0, str.size (), str.data (),
str.size ()) != 0)
findFilesWithExt (*resolvedLink, ext, pathList, recursive);
}
}
}
}
#endif // USE_FILESYSTEM
}
}
//------------------------------------------------------------------------
void findModules (const filesystem::path& path, Module::PathList& pathList)
{
std::error_code ec;
if (filesystem::exists (path, ec))
findFilesWithExt (path, ".vst3", pathList);
}
//------------------------------------------------------------------------
Optional<filesystem::path> getContentsDirectoryFromModuleExecutablePath (
const std::string& modulePath)
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (modulePath);
#else
filesystem::path path = filesystem::u8path (modulePath);
#endif // SMTG_CPP20
path = path.parent_path ();
if (path.filename () != architectureString)
return {};
path = path.parent_path ();
if (path.filename () != "Contents")
return {};
return Optional<filesystem::path> {std::move (path)};
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
Module::Ptr Module::create (const std::string& path, std::string& errorDescription)
{
auto _module = std::make_shared<Win32Module> ();
if (_module->load (path, errorDescription))
{
_module->path = path;
auto it = std::find_if (path.rbegin (), path.rend (),
[] (const std::string::value_type& c) { return c == '/'; });
if (it != path.rend ())
_module->name = {it.base (), path.end ()};
return _module;
}
return nullptr;
}
//------------------------------------------------------------------------
Module::PathList Module::getModulePaths ()
{
namespace StringConvert = Steinberg::Vst::StringConvert;
// find plug-ins located in common/VST3
PathList list;
if (auto knownFolder = getKnownFolder (FOLDERID_UserProgramFilesCommon))
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (*knownFolder);
#else
filesystem::path path = filesystem::u8path (*knownFolder);
#endif // SMTG_CPP20
path.append ("VST3");
#if LOG_ENABLE
std::cout << "Check folder: " << path << "\n";
#endif
findModules (path, list);
}
if (auto knownFolder = getKnownFolder (FOLDERID_ProgramFilesCommon))
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (*knownFolder);
#else
filesystem::path path = filesystem::u8path (*knownFolder);
#endif // SMTG_CPP20
path.append ("VST3");
#if LOG_ENABLE
std::cout << "Check folder: " << path << "\n";
#endif
findModules (path, list);
}
// find plug-ins located in VST3 (application folder)
WCHAR modulePath[kIPPathNameMax];
GetModuleFileNameW (nullptr, modulePath, kIPPathNameMax);
auto appPath = StringConvert::convert (Steinberg::wscast (modulePath));
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
filesystem::path path (appPath);
#else
filesystem::path path = filesystem::u8path (appPath);
#endif // SMTG_CPP20
path = path.parent_path ();
path = path.append ("VST3");
#if LOG_ENABLE
std::cout << "Check folder: " << path << "\n";
#endif
findModules (path, list);
return list;
}
//------------------------------------------------------------------------
Optional<std::string> Module::getModuleInfoPath (const std::string& modulePath)
{
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
return {};
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> {p};
}
*path /= "Resources";
*path /= "moduleinfo.json";
std::error_code ec;
if (filesystem::exists (*path, ec))
{
return {path->generic_string ()};
}
return {};
}
//------------------------------------------------------------------------
bool Module::validateBundleStructure (const std::string& modulePath, std::string& errorDescription)
{
try
{
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
{
errorDescription = "Not a bundle: '" + modulePath + "'.";
return false;
}
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> {p};
}
if (path->filename () != "Contents")
{
errorDescription = "Unexpected directory name, should be 'Contents' but is '" +
path->filename ().string () + "'.";
return false;
}
auto bundlePath = path->parent_path ();
*path /= architectureString;
*path /= bundlePath.filename ();
std::error_code ec;
if (filesystem::exists (*path, ec) == false)
{
errorDescription = "Shared library name is not equal to bundle folder name. Must be '" +
bundlePath.filename ().string () + "'.";
return false;
}
return true;
}
catch (const std::exception& exc)
{
errorDescription = exc.what ();
return false;
}
}
//------------------------------------------------------------------------
Module::SnapshotList Module::getSnapshots (const std::string& modulePath)
{
SnapshotList result;
auto path = getContentsDirectoryFromModuleExecutablePath (modulePath);
if (!path)
{
filesystem::path p;
if (!checkVST3Package ({modulePath}, &p))
return result;
p = p.parent_path ();
p = p.parent_path ();
path = Optional<filesystem::path> (p);
}
*path /= "Resources";
*path /= "Snapshots";
std::error_code ec;
if (filesystem::exists (*path, ec) == false)
return result;
PathList pngList;
findFilesWithExt (*path, ".png", pngList, false);
for (auto& png : pngList)
{
// filesystem::u8path is deprecated in C++20
#if SMTG_CPP20
const filesystem::path p (png);
#else
const filesystem::path p = filesystem::u8path (png);
#endif // SMTG_CPP20
auto filename = p.filename ().generic_string ();
auto uid = Snapshot::decodeUID (filename);
if (!uid)
continue;
auto scaleFactor = 1.;
if (auto decodedScaleFactor = Snapshot::decodeScaleFactor (filename))
scaleFactor = *decodedScaleFactor;
Module::Snapshot::ImageDesc desc;
desc.scaleFactor = scaleFactor;
desc.path = std::move (png);
bool found = false;
for (auto& entry : result)
{
if (entry.uid != *uid)
continue;
found = true;
entry.images.emplace_back (std::move (desc));
break;
}
if (found)
continue;
Module::Snapshot snapshot;
snapshot.uid = *uid;
snapshot.images.emplace_back (std::move (desc));
result.emplace_back (std::move (snapshot));
}
return result;
}
//------------------------------------------------------------------------
} // Hosting
} // VST3
@@ -0,0 +1,298 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/parameterchanges.cpp
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 parameter changes implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "parameterchanges.h"
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (ParameterChanges, IParameterChanges, IParameterChanges::iid)
IMPLEMENT_FUNKNOWN_METHODS (ParameterValueQueue, IParamValueQueue, IParamValueQueue::iid)
constexpr int32 kQueueReservedPoints = 5;
//-----------------------------------------------------------------------------
ParameterValueQueue::ParameterValueQueue (ParamID paramID)
: paramID (paramID)
{
values.reserve (kQueueReservedPoints);
FUNKNOWN_CTOR
}
//-----------------------------------------------------------------------------
ParameterValueQueue::~ParameterValueQueue ()
{
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
void ParameterValueQueue::clear ()
{
values.clear ();
}
//-----------------------------------------------------------------------------
int32 PLUGIN_API ParameterValueQueue::getPointCount ()
{
return static_cast<int32> (values.size ());
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API ParameterValueQueue::getPoint (int32 index, int32& sampleOffset, ParamValue& value)
{
if (index >= 0 && index < static_cast<int32> (values.size ()))
{
const ParameterQueueValue& queueValue = values[index];
sampleOffset = queueValue.sampleOffset;
value = queueValue.value;
return kResultTrue;
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API ParameterValueQueue::addPoint (int32 sampleOffset, ParamValue value, int32& index)
{
auto destIndex = static_cast<int32>(values.size ());
for (uint32 i = 0; i < values.size (); i++)
{
if (values[i].sampleOffset == sampleOffset)
{
values[i].value = value;
index = i;
return kResultTrue;
}
if (values[i].sampleOffset > sampleOffset)
{
destIndex = i;
break;
}
}
// need new point
ParameterQueueValue queueValue (value, sampleOffset);
if (destIndex == static_cast<int32> (values.size ()))
values.emplace_back (queueValue);
else
values.insert (values.begin () + destIndex, queueValue);
index = destIndex;
return kResultTrue;
}
//-----------------------------------------------------------------------------
// ParameterChanges
//-----------------------------------------------------------------------------
ParameterChanges::ParameterChanges (int32 maxParameters)
{
FUNKNOWN_CTOR
setMaxParameters (maxParameters);
}
//-----------------------------------------------------------------------------
ParameterChanges::~ParameterChanges ()
{
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
void ParameterChanges::setMaxParameters (int32 maxParameters)
{
if (maxParameters < 0)
return;
while (static_cast<int32> (queues.size ()) < maxParameters)
{
queues.emplace_back (owned (new ParameterValueQueue (kNoParamId)));
}
while (static_cast<int32> (queues.size ()) > maxParameters)
{
queues.pop_back ();
}
if (usedQueueCount > maxParameters)
usedQueueCount = maxParameters;
}
//-----------------------------------------------------------------------------
void ParameterChanges::clearQueue ()
{
usedQueueCount = 0;
}
//-----------------------------------------------------------------------------
int32 PLUGIN_API ParameterChanges::getParameterCount ()
{
return usedQueueCount;
}
//-----------------------------------------------------------------------------
IParamValueQueue* PLUGIN_API ParameterChanges::getParameterData (int32 index)
{
if (index >= 0 && index < usedQueueCount)
return queues[index];
return nullptr;
}
//-----------------------------------------------------------------------------
IParamValueQueue* PLUGIN_API ParameterChanges::addParameterData (const ParamID& pid, int32& index)
{
for (int32 i = 0; i < usedQueueCount; i++)
{
if (queues[i]->getParameterId () == pid)
{
index = i;
return queues[i];
}
}
ParameterValueQueue* valueQueue = nullptr;
if (usedQueueCount < static_cast<int32> (queues.size ()))
{
valueQueue = queues[usedQueueCount];
valueQueue->setParamID (pid);
valueQueue->clear ();
}
else
{
queues.emplace_back (owned (new ParameterValueQueue (pid)));
valueQueue = queues.back ();
}
index = usedQueueCount;
usedQueueCount++;
return valueQueue;
}
//-----------------------------------------------------------------------------
// ParameterChangeTransfer
//-----------------------------------------------------------------------------
ParameterChangeTransfer::ParameterChangeTransfer (int32 maxParameters)
: size (0)
, changes (nullptr)
, readIndex (0)
, writeIndex (0)
{
setMaxParameters (maxParameters);
}
//-----------------------------------------------------------------------------
ParameterChangeTransfer::~ParameterChangeTransfer ()
{
setMaxParameters (0);
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::setMaxParameters (int32 maxParameters)
{
// reserve memory for twice the amount of all parameters
int32 newSize = maxParameters * 2;
if (size != newSize)
{
if (changes)
delete [] changes;
changes = nullptr;
size = newSize;
if (size > 0)
changes = new ParameterChange [size];
}
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::addChange (ParamID pid, ParamValue value, int32 sampleOffset)
{
if (changes)
{
changes[writeIndex].id = pid;
changes[writeIndex].value = value;
changes[writeIndex].sampleOffset = sampleOffset;
int32 newWriteIndex = writeIndex + 1;
if (newWriteIndex >= size)
newWriteIndex = 0;
if (readIndex != newWriteIndex)
writeIndex = newWriteIndex;
}
}
//-----------------------------------------------------------------------------
bool ParameterChangeTransfer::getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset)
{
if (!changes)
return false;
int32 currentWriteIndex = writeIndex;
if (readIndex != currentWriteIndex)
{
pid = changes [readIndex].id;
value = changes [readIndex].value;
sampleOffset = changes [readIndex].sampleOffset;
int32 newReadIndex = readIndex + 1;
if (newReadIndex >= size)
newReadIndex = 0;
readIndex = newReadIndex;
return true;
}
return false;
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::transferChangesTo (ParameterChanges& dest)
{
ParamID pid;
ParamValue value;
int32 sampleOffset;
int32 index;
while (getNextChange (pid, value, sampleOffset))
{
IParamValueQueue* queue = dest.addParameterData (pid, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
}
//-----------------------------------------------------------------------------
void ParameterChangeTransfer::transferChangesFrom (ParameterChanges& source)
{
ParamValue value;
int32 sampleOffset;
for (int32 i = 0; i < source.getParameterCount (); i++)
{
IParamValueQueue* queue = source.getParameterData (i);
if (queue)
{
for (int32 j = 0; j < queue->getPointCount (); j++)
{
if (queue->getPoint (j, sampleOffset, value) == kResultTrue)
{
addChange (queue->getParameterId (), value, sampleOffset);
}
}
}
}
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,122 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/parameterchanges.h
// Created by : Steinberg, 03/05/2008.
// Description : VST 3 parameter changes implementation
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include <vector>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Implementation's example of IParamValueQueue - not threadsave!.
\ingroup hostingBase
*/
class ParameterValueQueue : public IParamValueQueue
{
public:
//------------------------------------------------------------------------
ParameterValueQueue (ParamID paramID);
virtual ~ParameterValueQueue ();
ParamID PLUGIN_API getParameterId () SMTG_OVERRIDE { return paramID; }
int32 PLUGIN_API getPointCount () SMTG_OVERRIDE;
tresult PLUGIN_API getPoint (int32 index, int32& sampleOffset, ParamValue& value) SMTG_OVERRIDE;
tresult PLUGIN_API addPoint (int32 sampleOffset, ParamValue value, int32& index) SMTG_OVERRIDE;
void setParamID (ParamID pID) {paramID = pID;}
void clear ();
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
ParamID paramID;
struct ParameterQueueValue
{
ParameterQueueValue (ParamValue value, int32 sampleOffset) : value (value), sampleOffset (sampleOffset) {}
ParamValue value;
int32 sampleOffset;
};
std::vector<ParameterQueueValue> values;
};
//------------------------------------------------------------------------
/** Implementation's example of IParameterChanges - not threadsave!.
\ingroup hostingBase
*/
class ParameterChanges : public IParameterChanges
{
public:
//------------------------------------------------------------------------
ParameterChanges (int32 maxParameters = 0);
virtual ~ParameterChanges ();
void clearQueue ();
void setMaxParameters (int32 maxParameters);
//---IParameterChanges-----------------------------
int32 PLUGIN_API getParameterCount () SMTG_OVERRIDE;
IParamValueQueue* PLUGIN_API getParameterData (int32 index) SMTG_OVERRIDE;
IParamValueQueue* PLUGIN_API addParameterData (const ParamID& pid, int32& index) SMTG_OVERRIDE;
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
std::vector<IPtr<ParameterValueQueue>> queues;
int32 usedQueueCount {0};
};
//------------------------------------------------------------------------
/** Ring buffer for transferring parameter changes from a writer to a read thread .
\ingroup hostingBase
*/
class ParameterChangeTransfer
{
public:
//------------------------------------------------------------------------
ParameterChangeTransfer (int32 maxParameters = 0);
virtual ~ParameterChangeTransfer ();
void setMaxParameters (int32 maxParameters);
void addChange (ParamID pid, ParamValue value, int32 sampleOffset);
bool getNextChange (ParamID& pid, ParamValue& value, int32& sampleOffset);
void transferChangesTo (ParameterChanges& dest);
void transferChangesFrom (ParameterChanges& source);
void removeChanges () { writeIndex = readIndex; }
//------------------------------------------------------------------------
protected:
struct ParameterChange
{
ParamID id;
ParamValue value;
int32 sampleOffset;
};
int32 size;
ParameterChange* changes;
volatile int32 readIndex;
volatile int32 writeIndex;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,118 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/pluginterfacesupport.cpp
// Created by : Steinberg, 11/2018.
// Description : VST 3 hostclasses, example implementations for IPlugInterfaceSupport
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "pluginterfacesupport.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include <algorithm>
//-----------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//-----------------------------------------------------------------------------
PlugInterfaceSupport::PlugInterfaceSupport ()
{
FUNKNOWN_CTOR
// add minimum set
//---VST 3.0.0--------------------------------
addPlugInterfaceSupported (IComponent::iid);
addPlugInterfaceSupported (IAudioProcessor::iid);
addPlugInterfaceSupported (IEditController::iid);
addPlugInterfaceSupported (IConnectionPoint::iid);
addPlugInterfaceSupported (IUnitInfo::iid);
addPlugInterfaceSupported (IUnitData::iid);
addPlugInterfaceSupported (IProgramListData::iid);
//---VST 3.0.1--------------------------------
addPlugInterfaceSupported (IMidiMapping::iid);
//---VST 3.1----------------------------------
addPlugInterfaceSupported (IEditController2::iid);
/*
//---VST 3.0.2--------------------------------
addPlugInterfaceSupported (IParameterFinder::iid);
//---VST 3.1----------------------------------
addPlugInterfaceSupported (IAudioPresentationLatency::iid);
//---VST 3.5----------------------------------
addPlugInterfaceSupported (IKeyswitchController::iid);
addPlugInterfaceSupported (IContextMenuTarget::iid);
addPlugInterfaceSupported (IEditControllerHostEditing::iid);
addPlugInterfaceSupported (IXmlRepresentationController::iid);
addPlugInterfaceSupported (INoteExpressionController::iid);
//---VST 3.6.5--------------------------------
addPlugInterfaceSupported (ChannelContext::IInfoListener::iid);
addPlugInterfaceSupported (IPrefetchableSupport::iid);
addPlugInterfaceSupported (IAutomationState::iid);
//---VST 3.6.11--------------------------------
addPlugInterfaceSupported (INoteExpressionPhysicalUIMapping::iid);
//---VST 3.6.12--------------------------------
addPlugInterfaceSupported (IMidiLearn::iid);
//---VST 3.7-----------------------------------
addPlugInterfaceSupported (IProcessContextRequirements::iid);
addPlugInterfaceSupported (IParameterFunctionName::iid);
addPlugInterfaceSupported (IProgress::iid);
//----VST 3.8------------------------------------
addPlugInterfaceSupported (IMidiMapping2::iid)
addPlugInterfaceSupported (IMidiLearn2::iid)
*/
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PlugInterfaceSupport::isPlugInterfaceSupported (const TUID _iid)
{
auto uid = FUID::fromTUID (_iid);
if (std::find (mFUIDArray.begin (), mFUIDArray.end (), uid) != mFUIDArray.end ())
return kResultTrue;
return kResultFalse;
}
//-----------------------------------------------------------------------------
void PlugInterfaceSupport::addPlugInterfaceSupported (const TUID _iid)
{
mFUIDArray.push_back (FUID::fromTUID (_iid));
}
//-----------------------------------------------------------------------------
bool PlugInterfaceSupport::removePlugInterfaceSupported (const TUID _iid)
{
auto uid = FUID::fromTUID (_iid);
auto it = std::find (mFUIDArray.begin (), mFUIDArray.end (), uid);
if (it == mFUIDArray.end ())
return false;
mFUIDArray.erase (it);
return true;
}
IMPLEMENT_FUNKNOWN_METHODS (PlugInterfaceSupport, IPlugInterfaceSupport, IPlugInterfaceSupport::iid)
//-----------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,50 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/pluginterfacesupport.h
// Created by : Steinberg, 11/20018.
// Description : VST 3 hostclasses, example implementations for IPlugInterfaceSupport
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstpluginterfacesupport.h"
#include <vector>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Example implementation of IPlugInterfaceSupport.
\ingroup hostingBase
*/
class PlugInterfaceSupport : public IPlugInterfaceSupport
{
public:
PlugInterfaceSupport ();
virtual ~PlugInterfaceSupport () = default;
//--- IPlugInterfaceSupport ---------
tresult PLUGIN_API isPlugInterfaceSupported (const TUID _iid) SMTG_OVERRIDE;
void addPlugInterfaceSupported (const TUID _iid);
bool removePlugInterfaceSupported (const TUID _iid);
DECLARE_FUNKNOWN_METHODS
private:
std::vector<FUID> mFUIDArray;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,320 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/plugprovider.cpp
// Created by : Steinberg, 08/2016
// Description : VST 3 Plug-in Provider class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "plugprovider.h"
#include "connectionproxy.h"
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include <cstdio>
#include <iostream>
static std::ostream* errorStream = &std::cout;
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// PlugProvider
//------------------------------------------------------------------------
PlugProvider::PlugProvider (const PluginFactory& factory, ClassInfo classInfo, bool plugIsGlobal)
: factory (factory)
, component (nullptr)
, controller (nullptr)
, classInfo (classInfo)
, plugIsGlobal (plugIsGlobal)
{
}
//------------------------------------------------------------------------
PlugProvider::~PlugProvider ()
{
terminatePlugin ();
}
//------------------------------------------------------------------------
template <typename Proc>
void PlugProvider::printError (Proc p) const
{
if (errorStream)
{
p (*errorStream);
}
}
//------------------------------------------------------------------------
bool PlugProvider::initialize ()
{
if (plugIsGlobal)
{
return setupPlugin (PluginContextFactory::instance ().getPluginContext ());
}
return true;
}
//------------------------------------------------------------------------
IComponent* PLUGIN_API PlugProvider::getComponent ()
{
if (!component)
setupPlugin (PluginContextFactory::instance ().getPluginContext ());
if (component)
component->addRef ();
return component;
}
//------------------------------------------------------------------------
IEditController* PLUGIN_API PlugProvider::getController ()
{
if (controller)
controller->addRef ();
// 'iController == 0' is allowed! In this case the plug has no controller
return controller;
}
//------------------------------------------------------------------------
IPluginFactory* PLUGIN_API PlugProvider::getPluginFactory ()
{
if (auto f = factory.get ())
return f.get ();
return nullptr;
}
//------------------------------------------------------------------------
tresult PLUGIN_API PlugProvider::getComponentUID (FUID& uid) const
{
uid = FUID::fromTUID (classInfo.ID ().data ());
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API PlugProvider::releasePlugIn (IComponent* iComponent,
IEditController* iController)
{
if (iComponent)
iComponent->release ();
if (iController)
iController->release ();
if (!plugIsGlobal)
{
terminatePlugin ();
}
return kResultOk;
}
//------------------------------------------------------------------------
bool PlugProvider::setupPlugin (FUnknown* hostContext)
{
bool res = false;
bool isSingleComponent = false;
//---create Plug-in here!--------------
// create its component part
component = factory.createInstance<IComponent> (classInfo.ID ());
if (component)
{
// initialize the component with our context
if (auto plugBase = U::cast<IPluginBase> (component))
{
res = (plugBase->initialize (hostContext) == kResultOk);
if (res == false)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to initialize component of " << classInfo.name () << "!\n";
});
return false;
}
}
else
{
printError ([&] (std::ostream& stream) {
stream << "Failed to get IPluginBase from component of " << classInfo.name ()
<< "!\n";
});
return false;
}
// try to create the controller part from the component
// (for Plug-ins which did not succeed to separate component from controller)
if (component->queryInterface (IEditController::iid, (void**)&controller) == kResultTrue)
{
isSingleComponent = true;
}
else
{
TUID controllerCID;
// ask for the associated controller class ID
if (component->getControllerClassId (controllerCID) == kResultTrue)
{
// create its controller part created from the factory
controller = factory.createInstance<IEditController> (VST3::UID (controllerCID));
if (controller)
{
// initialize the component with our context
if (auto plugCtrlBase = U::cast<IPluginBase> (controller))
{
res = (plugCtrlBase->initialize (hostContext) == kResultOk);
if (res == false)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to initialize controller of " << classInfo.name ()
<< "!\n";
});
}
}
else
{
printError ([&] (std::ostream& stream) {
stream << "Failed to get IPluginBase from controller of "
<< classInfo.name () << "!\n";
});
return false;
}
}
}
else
{
printError ([&] (std::ostream& stream) {
stream << "Component does not provide a required controller class ID ["
<< classInfo.name () << "]!\n";
});
}
}
if (!res)
{
component.reset ();
controller.reset ();
}
}
else if (errorStream)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to create component instance of " << classInfo.name () << "!\n";
});
}
if (res && !isSingleComponent)
return connectComponents ();
return res;
}
//------------------------------------------------------------------------
bool PlugProvider::connectComponents ()
{
if (!component || !controller)
return false;
auto compICP = U::cast<IConnectionPoint> (component);
auto contrICP = U::cast<IConnectionPoint> (controller);
if (!compICP || !contrICP)
return false;
componentCP = owned (new ConnectionProxy (compICP));
controllerCP = owned (new ConnectionProxy (contrICP));
tresult tres = componentCP->connect (contrICP);
if (tres != kResultTrue)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to connect the component with the controller with result code '"
<< tres << "'!\n";
});
return false;
}
tres = controllerCP->connect (compICP);
if (tres != kResultTrue)
{
printError ([&] (std::ostream& stream) {
stream << "Failed to connect the controller with the component with result code '"
<< tres << "'!\n";
});
return false;
}
return true;
}
//------------------------------------------------------------------------
bool PlugProvider::disconnectComponents ()
{
if (!componentCP || !controllerCP)
return false;
bool res = componentCP->disconnect ();
res &= controllerCP->disconnect ();
componentCP.reset ();
controllerCP.reset ();
return res;
}
//------------------------------------------------------------------------
void PlugProvider::terminatePlugin ()
{
disconnectComponents ();
bool controllerIsComponent = false;
if (component)
{
controllerIsComponent = FUnknownPtr<IEditController> (component).getInterface () != nullptr;
if (auto plugBase = U::cast<IPluginBase> (component))
plugBase->terminate ();
else
{
printError ([&](std::ostream& stream) {
stream << "Failed to get IPluginBase from component of " << classInfo.name ()
<< "!\n";
});
}
}
if (controller && controllerIsComponent == false)
{
if (auto plugCtrlBase = U::cast<IPluginBase> (controller))
plugCtrlBase->terminate ();
else
{
printError ([&](std::ostream& stream) {
stream << "Failed to get IPluginBase from controller of " << classInfo.name ()
<< "!\n";
});
}
}
component.reset ();
controller.reset ();
}
//------------------------------------------------------------------------
void PlugProvider::setErrorStream (std::ostream* stream)
{
errorStream = stream;
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,107 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/plugprovider.h
// Created by : Steinberg, 04/2005
// Description : VST 3 Plug-in Provider class
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/hosting/module.h"
#include "pluginterfaces/vst/ivsttestplugprovider.h"
#include "pluginterfaces/base/funknownimpl.h"
#include <ostream>
namespace Steinberg {
namespace Vst {
class IComponent;
class IEditController;
class ConnectionProxy;
//------------------------------------------------------------------------
/** Helper for creating and initializing component.
\ingroup Validator */
//------------------------------------------------------------------------
class PlugProvider
: public U::Implements<U::Directly<ITestPlugProvider2>, U::Indirectly<ITestPlugProvider>>
{
public:
using ClassInfo = VST3::Hosting::ClassInfo;
using PluginFactory = VST3::Hosting::PluginFactory;
//--- ---------------------------------------------------------------------
PlugProvider (const PluginFactory& factory, ClassInfo info, bool plugIsGlobal = true);
~PlugProvider () override;
bool initialize ();
IPtr<IComponent> getComponentPtr () const { return component; }
IPtr<IEditController> getControllerPtr () const { return controller; }
const ClassInfo& getClassInfo () const { return classInfo; }
//--- from ITestPlugProvider ------------------
IComponent* PLUGIN_API getComponent () SMTG_OVERRIDE;
IEditController* PLUGIN_API getController () SMTG_OVERRIDE;
tresult PLUGIN_API releasePlugIn (IComponent* component, IEditController* controller) SMTG_OVERRIDE;
tresult PLUGIN_API getSubCategories (IStringResult& result) const SMTG_OVERRIDE
{
result.setText (classInfo.subCategoriesString ().data ());
return kResultTrue;
}
tresult PLUGIN_API getComponentUID (FUID& uid) const SMTG_OVERRIDE;
//--- from ITestPlugProvider2 ------------------
IPluginFactory* PLUGIN_API getPluginFactory () SMTG_OVERRIDE;
static void setErrorStream (std::ostream* stream);
//------------------------------------------------------------------------
protected:
bool setupPlugin (FUnknown* hostContext);
bool connectComponents ();
bool disconnectComponents ();
void terminatePlugin ();
template<typename Proc>
void printError (Proc p) const;
PluginFactory factory;
IPtr<IComponent> component;
IPtr<IEditController> controller;
ClassInfo classInfo;
IPtr<ConnectionProxy> componentCP;
IPtr<ConnectionProxy> controllerCP;
bool plugIsGlobal;
};
//------------------------------------------------------------------------
class PluginContextFactory
{
public:
static PluginContextFactory& instance ()
{
static PluginContextFactory factory;
return factory;
}
void setPluginContext (FUnknown* obj) { context = obj; }
FUnknown* getPluginContext () const { return context; }
private:
FUnknown* context;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,204 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/processdata.cpp
// Created by : Steinberg, 10/2005
// Description : VST Hosting Utilities
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "processdata.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// HostProcessData
//------------------------------------------------------------------------
HostProcessData::~HostProcessData () noexcept
{
unprepare ();
}
//------------------------------------------------------------------------
bool HostProcessData::prepare (IComponent& component, int32 bufferSamples,
int32 _symbolicSampleSize)
{
if (checkIfReallocationNeeded (component, bufferSamples, _symbolicSampleSize))
{
unprepare ();
symbolicSampleSize = _symbolicSampleSize;
channelBufferOwner = bufferSamples > 0;
numInputs = createBuffers (component, inputs, kInput, bufferSamples);
numOutputs = createBuffers (component, outputs, kOutput, bufferSamples);
}
else
{
// reset silence flags
for (int32 i = 0; i < numInputs; i++)
{
inputs[i].silenceFlags = 0;
}
for (int32 i = 0; i < numOutputs; i++)
{
outputs[i].silenceFlags = 0;
}
}
symbolicSampleSize = _symbolicSampleSize;
return true;
}
//------------------------------------------------------------------------
void HostProcessData::unprepare ()
{
destroyBuffers (inputs, numInputs);
destroyBuffers (outputs, numOutputs);
channelBufferOwner = false;
}
//------------------------------------------------------------------------
bool HostProcessData::checkIfReallocationNeeded (IComponent& component, int32 bufferSamples,
int32 _symbolicSampleSize) const
{
if (channelBufferOwner != (bufferSamples > 0))
return true;
if (symbolicSampleSize != _symbolicSampleSize)
return true;
int32 inBusCount = component.getBusCount (kAudio, kInput);
if (inBusCount != numInputs)
return true;
int32 outBusCount = component.getBusCount (kAudio, kOutput);
if (outBusCount != numOutputs)
return true;
for (int32 i = 0; i < inBusCount; i++)
{
BusInfo busInfo = {};
if (component.getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue)
{
if (inputs[i].numChannels != busInfo.channelCount)
return true;
}
}
for (int32 i = 0; i < outBusCount; i++)
{
BusInfo busInfo = {};
if (component.getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue)
{
if (outputs[i].numChannels != busInfo.channelCount)
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
int32 HostProcessData::createBuffers (IComponent& component, AudioBusBuffers*& buffers,
BusDirection dir, int32 bufferSamples)
{
int32 busCount = component.getBusCount (kAudio, dir);
if (busCount > 0)
{
buffers = new AudioBusBuffers[busCount];
for (int32 i = 0; i < busCount; i++)
{
BusInfo busInfo = {};
if (component.getBusInfo (kAudio, dir, i, busInfo) == kResultTrue)
{
buffers[i].numChannels = busInfo.channelCount;
// allocate for each channel
if (busInfo.channelCount > 0)
{
if (symbolicSampleSize == kSample64)
buffers[i].channelBuffers64 = new Sample64*[busInfo.channelCount];
else
buffers[i].channelBuffers32 = new Sample32*[busInfo.channelCount];
for (int32 j = 0; j < busInfo.channelCount; j++)
{
if (symbolicSampleSize == kSample64)
{
if (bufferSamples > 0)
buffers[i].channelBuffers64[j] = new Sample64[bufferSamples];
else
buffers[i].channelBuffers64[j] = nullptr;
}
else
{
if (bufferSamples > 0)
buffers[i].channelBuffers32[j] = new Sample32[bufferSamples];
else
buffers[i].channelBuffers32[j] = nullptr;
}
}
}
}
}
}
return busCount;
}
//-----------------------------------------------------------------------------
void HostProcessData::destroyBuffers (AudioBusBuffers*& buffers, int32& busCount)
{
if (buffers)
{
for (int32 i = 0; i < busCount; i++)
{
if (channelBufferOwner)
{
for (int32 j = 0; j < buffers[i].numChannels; j++)
{
if (symbolicSampleSize == kSample64)
{
if (buffers[i].channelBuffers64 && buffers[i].channelBuffers64[j])
delete[] buffers[i].channelBuffers64[j];
}
else
{
if (buffers[i].channelBuffers32 && buffers[i].channelBuffers32[j])
delete[] buffers[i].channelBuffers32[j];
}
}
}
if (symbolicSampleSize == kSample64)
{
if (buffers[i].channelBuffers64)
delete[] buffers[i].channelBuffers64;
}
else
{
if (buffers[i].channelBuffers32)
delete[] buffers[i].channelBuffers32;
}
}
delete[] buffers;
buffers = nullptr;
}
busCount = 0;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,192 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/processdata.h
// Created by : Steinberg, 10/2005
// Description : VST Hosting Utilities
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
/** Extension of ProcessData.
Helps setting up the buffers for the process data structure for a component.
When the prepare method is called with bufferSamples != 0 the buffer management is handled by this class.
Otherwise the buffers need to be setup explicitly.
\ingroup hostingBase
*/
class HostProcessData : public ProcessData
{
public:
//------------------------------------------------------------------------
HostProcessData () = default;
virtual ~HostProcessData () noexcept;
/** Prepare buffer containers for all busses. If bufferSamples is not null buffers will be
* created. */
bool prepare (IComponent& component, int32 bufferSamples, int32 _symbolicSampleSize);
/** Remove bus buffers. */
void unprepare ();
/** Sets one sample buffer for all channels inside a bus. */
bool setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffer);
bool setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffer);
/** Sets individual sample buffers per channel inside a bus. */
bool setChannelBuffers (BusDirection dir, int32 busIndex, Sample32* sampleBuffers[],
int32 bufferCount);
bool setChannelBuffers64 (BusDirection dir, int32 busIndex, Sample64* sampleBuffers[],
int32 bufferCount);
/** Sets one sample buffer for a given channel inside a bus. */
bool setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex,
Sample32* sampleBuffer);
bool setChannelBuffer64 (BusDirection dir, int32 busIndex, int32 channelIndex,
Sample64* sampleBuffer);
static constexpr uint64 kAllChannelsSilent =
#if SMTG_OS_MACOS
0xffffffffffffffffULL;
#else
0xffffffffffffffffUL;
#endif
//------------------------------------------------------------------------
protected:
int32 createBuffers (IComponent& component, AudioBusBuffers*& buffers, BusDirection dir,
int32 bufferSamples);
void destroyBuffers (AudioBusBuffers*& buffers, int32& busCount);
bool checkIfReallocationNeeded (IComponent& component, int32 bufferSamples,
int32 _symbolicSampleSize) const;
bool isValidBus (BusDirection dir, int32 busIndex) const;
bool channelBufferOwner {false};
};
//------------------------------------------------------------------------
// inline
//------------------------------------------------------------------------
inline bool HostProcessData::isValidBus (BusDirection dir, int32 busIndex) const
{
if (dir == kInput && (!inputs || busIndex >= numInputs))
return false;
if (dir == kOutput && (!outputs || busIndex >= numOutputs))
return false;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex,
Sample32* sampleBuffer)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample32)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
for (int32 i = 0; i < busBuffers.numChannels; i++)
busBuffers.channelBuffers32[i] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex,
Sample64* sampleBuffer)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample64)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
for (int32 i = 0; i < busBuffers.numChannels; i++)
busBuffers.channelBuffers64[i] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers (BusDirection dir, int32 busIndex,
Sample32* sampleBuffers[], int32 bufferCount)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample32)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels;
for (int32 i = 0; i < count; i++)
busBuffers.channelBuffers32[i] = sampleBuffers ? sampleBuffers[i] : nullptr;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffers64 (BusDirection dir, int32 busIndex,
Sample64* sampleBuffers[], int32 bufferCount)
{
if (channelBufferOwner || symbolicSampleSize != SymbolicSampleSizes::kSample64)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
int32 count = bufferCount < busBuffers.numChannels ? bufferCount : busBuffers.numChannels;
for (int32 i = 0; i < count; i++)
busBuffers.channelBuffers64[i] = sampleBuffers ? sampleBuffers[i] : nullptr;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffer (BusDirection dir, int32 busIndex, int32 channelIndex,
Sample32* sampleBuffer)
{
if (symbolicSampleSize != SymbolicSampleSizes::kSample32)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
if (channelIndex >= busBuffers.numChannels)
return false;
busBuffers.channelBuffers32[channelIndex] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
inline bool HostProcessData::setChannelBuffer64 (BusDirection dir, int32 busIndex,
int32 channelIndex, Sample64* sampleBuffer)
{
if (symbolicSampleSize != SymbolicSampleSizes::kSample64)
return false;
if (!isValidBus (dir, busIndex))
return false;
AudioBusBuffers& busBuffers = dir == kInput ? inputs[busIndex] : outputs[busIndex];
if (channelIndex >= busBuffers.numChannels)
return false;
busBuffers.channelBuffers64[channelIndex] = sampleBuffer;
return true;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,130 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/connectionproxytest.cpp
// Created by : Steinberg, 08/2021
// Description : Test connection proxy
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/connectionproxy.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/utility/testing.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <thread>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
class ConnectionPoint : public IConnectionPoint
{
public:
tresult PLUGIN_API connect (IConnectionPoint* inOther) override
{
other = inOther;
return kResultTrue;
}
tresult PLUGIN_API disconnect (IConnectionPoint* inOther) override
{
if (inOther != other)
return kResultFalse;
return kResultTrue;
}
tresult PLUGIN_API notify (IMessage*) override
{
messageReceived = true;
return kResultTrue;
}
tresult PLUGIN_API queryInterface (const TUID, void**) override { return kNotImplemented; }
uint32 PLUGIN_API addRef () override { return 100; }
uint32 PLUGIN_API release () override { return 100; }
IConnectionPoint* other {nullptr};
bool messageReceived {false};
};
//------------------------------------------------------------------------
ModuleInitializer ConnectionProxyTests ([] () {
constexpr auto TestSuiteName = "ConnectionProxy";
registerTest (TestSuiteName, STR ("Connect and disconnect"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_EQ (proxy.disconnect (&cp2), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Disconnect wrong object"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionPoint cp3;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_NE (proxy.disconnect (&cp3), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Send message on UI thread"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_FALSE (cp2.messageReceived);
HostMessage msg;
EXPECT_EQ (proxy.notify (&msg), kResultTrue);
EXPECT_TRUE (cp2.messageReceived);
return true;
});
registerTest (TestSuiteName, STR ("Send message on 2nd thread"), [] (ITestResult* testResult) {
ConnectionPoint cp1;
ConnectionPoint cp2;
ConnectionProxy proxy (&cp1);
EXPECT_EQ (proxy.connect (&cp2), kResultTrue);
EXPECT_FALSE (cp2.messageReceived);
std::condition_variable cv;
std::mutex m;
std::optional<tresult> notifyResult;
std::thread thread ([&] () {
HostMessage msg;
{
const std::scoped_lock sl (m);
notifyResult = proxy.notify (&msg);
}
cv.notify_one ();
});
std::unique_lock ul (m);
cv.wait (ul, [&] { return notifyResult.has_value (); });
EXPECT_NE (*notifyResult, kResultTrue);
EXPECT_FALSE (cp2.messageReceived);
thread.join ();
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/eventlisttest.cpp
// Created by : Steinberg, 08/2021
// Description : Test event list
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/utility/testing.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
ModuleInitializer EventListTests ([] () {
constexpr auto TestSuiteName = "EventList";
registerTest (TestSuiteName, STR ("Set and get single event"), [] (ITestResult* testResult) {
EventList eventList;
Event event1 = {};
event1.type = Event::kNoteOnEvent;
event1.noteOn.noteId = 10;
EXPECT_EQ (eventList.addEvent (event1), kResultTrue);
Event event2;
EXPECT_EQ (eventList.getEvent (0, event2), kResultTrue);
EXPECT_EQ (memcmp (&event1, &event2, sizeof (Event)), 0);
return true;
});
registerTest (TestSuiteName, STR ("Count events"), [] (ITestResult* testResult) {
EventList eventList;
Event event = {};
for (auto i = 0; i < 20; ++i)
{
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
}
EXPECT_EQ (eventList.getEventCount (), 20);
return true;
});
registerTest (TestSuiteName, STR ("Overflow"), [] (ITestResult* testResult) {
EventList eventList (20);
Event event = {};
for (auto i = 0; i < 20; ++i)
{
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
}
EXPECT_EQ (eventList.getEventCount (), 20);
EXPECT_NE (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 20);
return true;
});
registerTest (TestSuiteName, STR ("Get unknown event"), [] (ITestResult* testResult) {
EventList eventList;
Event event {};
EXPECT_NE (eventList.getEvent (0, event), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Resize"), [] (ITestResult* testResult) {
EventList eventList (1);
Event event {};
EXPECT_NE (eventList.getEvent (0, event), kResultTrue);
event = {};
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 1);
EXPECT_NE (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 1);
eventList.setMaxSize (2);
EXPECT_EQ (eventList.getEventCount (), 0);
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 2);
EXPECT_NE (eventList.addEvent (event), kResultTrue);
EXPECT_EQ (eventList.getEventCount (), 2);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/hostclassestest.cpp
// Created by : Steinberg, 08/2021
// Description : Test host classes
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "pluginterfaces/base/fstrdefs.h"
#include <array>
#include <vector>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
ModuleInitializer HostApplicationTests ([] () {
constexpr auto TestSuiteName = "HostApplication";
registerTest (
TestSuiteName, STR ("Create instance of IAttributeList"), [] (ITestResult* testResult) {
HostApplication hostApp;
FUnknown* instance {nullptr};
TUID iid;
IAttributeList::iid.toTUID (iid);
EXPECT_EQ (hostApp.createInstance (iid, iid, reinterpret_cast<void**> (&instance)),
kResultTrue);
EXPECT_NE (instance, nullptr);
instance->release ();
return true;
});
registerTest (TestSuiteName, STR ("Create instance of IMessage"), [] (ITestResult* testResult) {
HostApplication hostApp;
FUnknown* instance {nullptr};
TUID iid;
IMessage::iid.toTUID (iid);
EXPECT_EQ (hostApp.createInstance (iid, iid, reinterpret_cast<void**> (&instance)),
kResultTrue);
EXPECT_NE (instance, nullptr);
instance->release ();
return true;
});
});
//------------------------------------------------------------------------
ModuleInitializer HostAttributeListTests ([] () {
constexpr auto TestSuiteName = "HostAttributeList";
registerTest (TestSuiteName, STR ("Int"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr int64 testValue = 5;
EXPECT_EQ (attrList->setInt ("Int", testValue), kResultTrue);
int64 value = 0;
EXPECT_EQ (attrList->getInt ("Int", value), kResultTrue);
EXPECT_EQ (value, testValue);
return true;
});
registerTest (TestSuiteName, STR ("Float"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr double testValue = 2.636;
EXPECT_EQ (attrList->setFloat ("Float", testValue), kResultTrue);
double value = 0;
EXPECT_EQ (attrList->getFloat ("Float", value), kResultTrue);
EXPECT_EQ (value, testValue);
return true;
});
registerTest (TestSuiteName, STR ("String"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr const TChar* testValue = STR ("TestValue");
EXPECT_EQ (attrList->setString ("Str", testValue), kResultTrue);
TChar value[10];
EXPECT_EQ (attrList->getString ("Str", value, 10 * sizeof (TChar)), kResultTrue);
EXPECT_EQ (tstrcmp (testValue, value), 0);
return true;
});
registerTest (TestSuiteName, STR ("Binary"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
std::array<int32, 20> testData {};
int32 val = 0;
for (auto item : testData)
{
item = val++;
}
uint32 testDataSize = static_cast<uint32>(testData.size ()) * sizeof (int32);
EXPECT_EQ (attrList->setBinary ("Binary", testData.data (), testDataSize), kResultTrue);
const void* data;
uint32 dataSize {0};
EXPECT_EQ (attrList->getBinary ("Binary", data, dataSize), kResultTrue);
EXPECT_EQ (dataSize, testDataSize);
auto s = reinterpret_cast<const int32*> (data);
for (auto i : testData)
{
EXPECT_EQ (i, *s);
s++;
}
return true;
});
registerTest (TestSuiteName, STR ("Multiple Set"), [] (ITestResult* testResult) {
auto attrList = HostAttributeList::make ();
constexpr int64 testValue1 = 5;
constexpr int64 testValue2 = 6;
constexpr int64 testValue3 = 7;
EXPECT_EQ (attrList->setInt ("Int", testValue1), kResultTrue);
EXPECT_EQ (attrList->setInt ("Int", testValue2), kResultTrue);
EXPECT_EQ (attrList->setInt ("Int", testValue3), kResultTrue);
int64 value = 0;
EXPECT_EQ (attrList->getInt ("Int", value), kResultTrue);
EXPECT_EQ (value, testValue3);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,273 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/parameterchangestest.cpp
// Created by : Steinberg, 08/2021
// Description : Test parameter changes
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/utility/testing.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
struct ValuePoint
{
int32 sampleOffset {};
ParamValue value {};
};
//------------------------------------------------------------------------
ModuleInitializer ParameterValueQueueTests ([] () {
constexpr auto TestSuiteName = "ParameterValueQueue";
registerTest (TestSuiteName, STR ("Set paramID"), [] (ITestResult* testResult) {
ParameterValueQueue queue (10);
EXPECT_EQ (queue.getParameterId (), 10);
queue.setParamID (5);
EXPECT_EQ (queue.getParameterId (), 5);
return true;
});
registerTest (TestSuiteName, STR ("Set/get point"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp {100, 0.5};
int32 index {};
EXPECT_EQ (queue.addPoint (vp.sampleOffset, vp.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 1);
EXPECT_EQ (index, 0);
ValuePoint test;
EXPECT_EQ (queue.getPoint (index, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp.value, test.value);
return true;
});
registerTest (TestSuiteName, STR ("Set/get multiple points"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp1 {10, 0.1};
ValuePoint vp2 {30, 0.3};
ValuePoint vp3 {50, 0.6};
ValuePoint vp4 {70, 0.8};
int32 index {};
EXPECT_EQ (queue.addPoint (vp1.sampleOffset, vp1.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp2.sampleOffset, vp2.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp3.sampleOffset, vp3.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp4.sampleOffset, vp4.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 4);
EXPECT_EQ (index, 3);
ValuePoint test;
EXPECT_EQ (queue.getPoint (0, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp1.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp1.value, test.value);
EXPECT_EQ (queue.getPoint (1, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp2.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp2.value, test.value);
EXPECT_EQ (queue.getPoint (2, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp3.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp3.value, test.value);
EXPECT_EQ (queue.getPoint (3, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp4.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp4.value, test.value);
return true;
});
registerTest (TestSuiteName, STR ("Ordered points"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp1 {70, 0.1};
ValuePoint vp2 {50, 0.3};
ValuePoint vp3 {30, 0.6};
ValuePoint vp4 {10, 0.8};
int32 index {};
EXPECT_EQ (queue.addPoint (vp1.sampleOffset, vp1.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp2.sampleOffset, vp2.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp3.sampleOffset, vp3.value, index), kResultTrue);
EXPECT_EQ (queue.addPoint (vp4.sampleOffset, vp4.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 4);
ValuePoint test;
EXPECT_EQ (queue.getPoint (0, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp4.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp4.value, test.value);
EXPECT_EQ (queue.getPoint (1, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp3.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp3.value, test.value);
EXPECT_EQ (queue.getPoint (2, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp2.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp2.value, test.value);
EXPECT_EQ (queue.getPoint (3, test.sampleOffset, test.value), kResultTrue);
EXPECT_EQ (vp1.sampleOffset, test.sampleOffset);
EXPECT_EQ (vp1.value, test.value);
return true;
});
registerTest (TestSuiteName, STR ("Clear"), [] (ITestResult* testResult) {
ParameterValueQueue queue (0);
ValuePoint vp {100, 0.5};
int32 index {};
EXPECT_EQ (queue.addPoint (vp.sampleOffset, vp.value, index), kResultTrue);
EXPECT_EQ (queue.getPointCount (), 1);
EXPECT_EQ (index, 0);
queue.clear ();
EXPECT_EQ (queue.getPointCount (), 0);
ValuePoint test;
EXPECT_NE (queue.getPoint (index, test.sampleOffset, test.value), kResultTrue);
return true;
});
});
//------------------------------------------------------------------------
ModuleInitializer ParameterChangesTests ([] () {
constexpr auto TestSuiteName = "ParameterChanges";
registerTest (TestSuiteName, STR ("Parameter count"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
EXPECT_EQ (changes.getParameterCount (), 0);
int32 index {};
auto queue = changes.addParameterData (0, index);
EXPECT_NE (queue, nullptr);
EXPECT_EQ (index, 0);
EXPECT_EQ (changes.getParameterCount (), 1);
return true;
});
registerTest (TestSuiteName, STR ("Clear queue"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
int32 index {};
EXPECT_EQ (changes.getParameterCount (), 0);
changes.addParameterData (0, index);
EXPECT_EQ (changes.getParameterCount (), 1);
changes.clearQueue ();
EXPECT_EQ (changes.getParameterCount (), 0);
return true;
});
registerTest (TestSuiteName, STR ("Increase max parameters"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
int32 index {};
EXPECT_EQ (changes.getParameterCount (), 0);
changes.addParameterData (0, index);
EXPECT_EQ (changes.getParameterCount (), 1);
EXPECT_NE (changes.addParameterData (1, index), nullptr);
EXPECT_EQ (changes.getParameterCount (), 2);
changes.setMaxParameters (4);
EXPECT_EQ (changes.getParameterCount (), 2);
return true;
});
registerTest (TestSuiteName, STR ("Get parameter data"), [] (ITestResult* testResult) {
ParameterChanges changes (1);
int32 index {};
auto queue1 = changes.addParameterData (0, index);
auto queue2 = changes.getParameterData (index);
EXPECT_EQ (queue1, queue2);
return true;
});
});
//------------------------------------------------------------------------
struct ParamChange
{
ParamID id {};
ParamValue value {};
int32 sampleOffset {};
bool operator== (const ParamChange& o) const
{
return id == o.id && value == o.value && sampleOffset == o.sampleOffset;
}
bool operator!= (const ParamChange& o) const
{
return id != o.id || value != o.value || sampleOffset != o.sampleOffset;
}
};
//------------------------------------------------------------------------
ModuleInitializer ParameterChangeTransferTests ([] () {
constexpr auto TestSuiteName = "ParameterChangeTransfer";
registerTest (TestSuiteName, STR ("Add/get change"), [] (ITestResult* testResult) {
ParameterChangeTransfer transfer (1);
ParamChange change {1, 0.8, 2};
transfer.addChange (change.id, change.value, change.sampleOffset);
ParamChange test {};
EXPECT_NE (change, test);
EXPECT_TRUE (transfer.getNextChange (test.id, test.value, test.sampleOffset));
EXPECT_EQ (change, test);
return true;
});
registerTest (TestSuiteName, STR ("Remove changes"), [] (ITestResult* testResult) {
ParameterChangeTransfer transfer (1);
ParamChange change {1, 0.8, 2};
transfer.addChange (change.id, change.value, change.sampleOffset);
transfer.removeChanges ();
ParamChange test {};
EXPECT_FALSE (transfer.getNextChange (test.id, test.value, test.sampleOffset));
return true;
});
registerTest (TestSuiteName, STR ("Transfer changes to"), [] (ITestResult* testResult) {
ParameterChangeTransfer transfer (10);
ParamChange ch1 {1, 0.8, 2};
ParamChange ch2 {2, 0.4, 8};
transfer.addChange (ch1.id, ch1.value, ch1.sampleOffset);
transfer.addChange (ch2.id, ch2.value, ch2.sampleOffset);
ParameterChanges changes (2);
transfer.transferChangesTo (changes);
EXPECT_EQ (changes.getParameterCount (), 2);
auto valueQueue1 = changes.getParameterData (0);
EXPECT_NE (valueQueue1, nullptr);
auto valueQueue2 = changes.getParameterData (1);
EXPECT_NE (valueQueue2, nullptr);
auto pid1 = valueQueue1->getParameterId ();
auto pid2 = valueQueue2->getParameterId ();
EXPECT (pid1 == ch1.id || pid1 == ch2.id);
EXPECT (pid2 == ch1.id || pid2 == ch2.id);
EXPECT_NE (pid1, pid2);
ValuePoint vp1;
ValuePoint vp2;
if (pid1 == ch1.id)
{
EXPECT_EQ (valueQueue1->getPoint (0, vp1.sampleOffset, vp1.value), kResultTrue);
EXPECT_EQ (valueQueue2->getPoint (0, vp2.sampleOffset, vp2.value), kResultTrue);
}
else
{
EXPECT_EQ (valueQueue2->getPoint (0, vp1.sampleOffset, vp1.value), kResultTrue);
EXPECT_EQ (valueQueue1->getPoint (0, vp2.sampleOffset, vp2.value), kResultTrue);
}
return true;
});
registerTest (TestSuiteName, STR ("Transfer changes from"), [] (ITestResult* testResult) {
ParamChange ch1 {1, 0.8, 2};
ParamChange ch2 {2, 0.4, 8};
ParameterChangeTransfer transfer (2);
ParameterChanges changes;
int32 index {};
auto valueQueue = changes.addParameterData (ch1.id, index);
EXPECT_NE (valueQueue, nullptr);
EXPECT_EQ (valueQueue->addPoint (ch1.sampleOffset, ch1.value, index), kResultTrue);
valueQueue = changes.addParameterData (ch2.id, index);
EXPECT_NE (valueQueue, nullptr);
EXPECT_EQ (valueQueue->addPoint (ch2.sampleOffset, ch2.value, index), kResultTrue);
transfer.transferChangesFrom (changes);
ParamChange test1 {};
ParamChange test2 {};
ParamChange test3 {};
EXPECT_TRUE (transfer.getNextChange (test1.id, test1.value, test1.sampleOffset));
EXPECT_TRUE (transfer.getNextChange (test2.id, test2.value, test2.sampleOffset));
EXPECT_FALSE (transfer.getNextChange (test3.id, test3.value, test3.sampleOffset));
EXPECT (test1 == ch1 || test1 == ch2);
EXPECT (test2 == ch1 || test2 == ch2);
EXPECT_NE (test1, test2);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/pluginterfacesupporttest.cpp
// Created by : Steinberg, 08/2021
// Description : Test pluginterface support helper
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/pluginterfacesupport.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include "pluginterfaces/vst/ivstunits.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
ModuleInitializer PlugInterfaceSupportTests ([] () {
constexpr auto TestSuiteName = "PlugInterfaceSupport";
registerTest (TestSuiteName, STR ("Initial interfaces"), [] (ITestResult* testResult) {
PlugInterfaceSupport pis;
//---VST 3.0.0--------------------------------
EXPECT_EQ (pis.isPlugInterfaceSupported (IComponent::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IAudioProcessor::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditController::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IConnectionPoint::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IUnitInfo::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IUnitData::iid), kResultTrue);
EXPECT_EQ (pis.isPlugInterfaceSupported (IProgramListData::iid), kResultTrue);
//---VST 3.0.1--------------------------------
EXPECT_EQ (pis.isPlugInterfaceSupported (IMidiMapping::iid), kResultTrue);
//---VST 3.1----------------------------------
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditController2::iid), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Add interface"), [] (ITestResult* testResult) {
PlugInterfaceSupport pis;
EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
pis.addPlugInterfaceSupported (IEditControllerHostEditing::iid);
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
return true;
});
registerTest (TestSuiteName, STR ("Remove interface"), [] (ITestResult* testResult) {
PlugInterfaceSupport pis;
EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
pis.addPlugInterfaceSupported (IEditControllerHostEditing::iid);
EXPECT_EQ (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
EXPECT_TRUE (pis.removePlugInterfaceSupported (IEditControllerHostEditing::iid));
EXPECT_NE (pis.isPlugInterfaceSupported (IEditControllerHostEditing::iid), kResultTrue);
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,351 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/hosting/test/processdatatest.cpp
// Created by : Steinberg, 08/2021
// Description : Test process data helper
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/source/main/moduleinit.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "public.sdk/source/vst/utility/testing.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include "pluginterfaces/vst/ivstunits.h"
#include <functional>
#include <memory>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace {
//------------------------------------------------------------------------
struct TestComponent : public IComponent
{
using GetBusCountFunc = std::function<int32 (BusDirection dir)>;
using GetBusInfoFunc = std::function<tresult (BusDirection dir, int32 index, BusInfo& bus)>;
tresult PLUGIN_API queryInterface (const TUID /*_iid*/, void** /*obj*/) override
{
return kNoInterface;
}
uint32 PLUGIN_API addRef () override { return 100; }
uint32 PLUGIN_API release () override { return 100; }
tresult PLUGIN_API initialize (FUnknown* /*context*/) override { return kResultTrue; }
tresult PLUGIN_API terminate () override { return kResultTrue; }
tresult PLUGIN_API getControllerClassId (TUID /*classId*/) override { return kNotImplemented; }
tresult PLUGIN_API setIoMode (IoMode /*mode*/) override { return kNotImplemented; }
int32 PLUGIN_API getBusCount (MediaType type, BusDirection dir) override
{
if (type != MediaTypes::kAudio)
return 0;
return getBusCountFunc (dir);
}
tresult PLUGIN_API getBusInfo (MediaType type, BusDirection dir, int32 index,
BusInfo& bus) override
{
if (type != MediaTypes::kAudio)
return kResultFalse;
return getBusInfoFunc (dir, index, bus);
}
tresult PLUGIN_API getRoutingInfo (RoutingInfo& /*inInfo*/, RoutingInfo& /*outInfo*/) override
{
return kNotImplemented;
}
tresult PLUGIN_API activateBus (MediaType /*type*/, BusDirection /*dir*/, int32 /*index*/,
TBool /*state*/) override
{
return kNotImplemented;
}
tresult PLUGIN_API setActive (TBool /*state*/) override { return kNotImplemented; }
tresult PLUGIN_API setState (IBStream* /*state*/) override { return kNotImplemented; }
tresult PLUGIN_API getState (IBStream* /*state*/) override { return kNotImplemented; }
GetBusCountFunc getBusCountFunc = [] (BusDirection /*dir*/) { return 0; };
GetBusInfoFunc getBusInfoFunc = [] (BusDirection /*dir*/, int32 /*index*/, BusInfo& /*bus*/) {
return kNotImplemented;
};
};
//------------------------------------------------------------------------
ModuleInitializer HostProcessDataTests ([] () {
constexpr auto TestSuiteName = "HostProcessData";
registerTest (TestSuiteName, STR ("No bus"), [] (ITestResult* testResult) {
TestComponent tc;
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numInputs, 0);
EXPECT_EQ (processData.numOutputs, 0);
return true;
});
registerTest (TestSuiteName, STR ("1 out bus no channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection dir) {
return dir == BusDirections::kOutput ? 1 : 0;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numInputs, 0);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_EQ (processData.outputs[0].numChannels, 0);
return true;
});
registerTest (TestSuiteName, STR ("1 out bus 2 channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection dir) {
return dir == BusDirections::kOutput ? 1 : 0;
};
tc.getBusInfoFunc = [] (BusDirection dir, int32 index, BusInfo& bus) {
if (dir == BusDirections::kInput || index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numInputs, 0);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_EQ (processData.outputs[0].numChannels, 2);
EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr);
return true;
});
registerTest (TestSuiteName, STR ("1 in & out bus 2 channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_EQ (processData.outputs[0].numChannels, 2);
EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr);
EXPECT_EQ (processData.numInputs, 1);
EXPECT_EQ (processData.inputs[0].numChannels, 2);
EXPECT_NE (processData.inputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[1], nullptr);
return true;
});
registerTest (TestSuiteName, STR ("2 in & out bus dif channels"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 2; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index < 0 || index > 1)
return kResultFalse;
bus.channelCount = index == 0 ? 4 : 1;
return kResultTrue;
};
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 1024, kSample32));
EXPECT_EQ (processData.numOutputs, 2);
EXPECT_EQ (processData.outputs[0].numChannels, 4);
EXPECT_NE (processData.outputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[1], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[2], nullptr);
EXPECT_NE (processData.outputs[0].channelBuffers32[3], nullptr);
EXPECT_EQ (processData.outputs[1].numChannels, 1);
EXPECT_NE (processData.outputs[1].channelBuffers32[0], nullptr);
EXPECT_EQ (processData.numInputs, 2);
EXPECT_EQ (processData.inputs[0].numChannels, 4);
EXPECT_NE (processData.inputs[0].channelBuffers32[0], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[1], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[2], nullptr);
EXPECT_NE (processData.inputs[0].channelBuffers32[3], nullptr);
EXPECT_EQ (processData.inputs[1].numChannels, 1);
EXPECT_NE (processData.inputs[1].channelBuffers32[0], nullptr);
return true;
});
registerTest (TestSuiteName, STR ("Set all channel buffers 32"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto buffer = std::unique_ptr<float[]> (new float[10]);
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 0, kSample32));
EXPECT_EQ (processData.numInputs, 1);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 1, nullptr));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 1, nullptr));
EXPECT_TRUE (processData.setChannelBuffers (BusDirections::kInput, 0, buffer.get ()));
EXPECT_TRUE (processData.setChannelBuffers (BusDirections::kOutput, 0, buffer.get ()));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 0, nullptr));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kOutput, 0, nullptr));
EXPECT_EQ (processData.inputs[0].channelBuffers32[0], buffer.get ());
EXPECT_EQ (processData.inputs[0].channelBuffers32[1], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers32[0], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers32[1], buffer.get ());
return true;
});
registerTest (TestSuiteName, STR ("Set all channel buffers 64"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto buffer = std::unique_ptr<double[]> (new double[10]);
HostProcessData processData;
EXPECT_TRUE (processData.prepare (tc, 0, kSample64));
EXPECT_EQ (processData.numInputs, 1);
EXPECT_EQ (processData.numOutputs, 1);
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 1, nullptr));
EXPECT_FALSE (processData.setChannelBuffers64 (BusDirections::kInput, 1, nullptr));
EXPECT_TRUE (processData.setChannelBuffers64 (BusDirections::kInput, 0, buffer.get ()));
EXPECT_TRUE (processData.setChannelBuffers64 (BusDirections::kOutput, 0, buffer.get ()));
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kInput, 0, nullptr));
EXPECT_FALSE (processData.setChannelBuffers (BusDirections::kOutput, 0, nullptr));
EXPECT_EQ (processData.inputs[0].channelBuffers64[0], buffer.get ());
EXPECT_EQ (processData.inputs[0].channelBuffers64[1], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers64[0], buffer.get ());
EXPECT_EQ (processData.outputs[0].channelBuffers64[1], buffer.get ());
return true;
});
registerTest (
TestSuiteName, STR ("Set individual channel buffers 32"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<float[]> (new float[10]);
auto bufferR = std::unique_ptr<float[]> (new float[10]);
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample32));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 1, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 1, 0, nullptr));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kInput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kInput, 0, 1, bufferR.get ()));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kOutput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer (BusDirections::kOutput, 0, 1, bufferR.get ()));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 1, nullptr));
EXPECT_EQ (pd.inputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers32[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[1], bufferR.get ());
return true;
});
registerTest (TestSuiteName, STR ("Set individual channel buffers 32 combined"),
[] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<float[]> (new float[10]);
auto bufferR = std::unique_ptr<float[]> (new float[10]);
float* buffers[2] = {bufferL.get (), bufferR.get ()};
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample32));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 1, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 1, nullptr, 0));
EXPECT_TRUE (pd.setChannelBuffers (BusDirections::kInput, 0, buffers, 2));
EXPECT_TRUE (pd.setChannelBuffers (BusDirections::kOutput, 0, buffers, 2));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 0, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kOutput, 0, nullptr, 0));
EXPECT_EQ (pd.inputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers32[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers32[1], bufferR.get ());
return true;
});
registerTest (
TestSuiteName, STR ("Set individual channel buffers 64"), [] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<double[]> (new double[10]);
auto bufferR = std::unique_ptr<double[]> (new double[10]);
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample64));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 1, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer64 (BusDirections::kInput, 1, 0, nullptr));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kInput, 0, 1, bufferR.get ()));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 0, bufferL.get ()));
EXPECT_TRUE (pd.setChannelBuffer64 (BusDirections::kOutput, 0, 1, bufferR.get ()));
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kInput, 0, 0, nullptr));
EXPECT_FALSE (pd.setChannelBuffer (BusDirections::kOutput, 0, 1, nullptr));
EXPECT_EQ (pd.inputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers64[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[1], bufferR.get ());
return true;
});
registerTest (TestSuiteName, STR ("Set individual channel buffers 64 combined"),
[] (ITestResult* testResult) {
TestComponent tc;
tc.getBusCountFunc = [] (BusDirection /*dir*/) { return 1; };
tc.getBusInfoFunc = [] (BusDirection /*dir*/, int32 index, BusInfo& bus) {
if (index != 0)
return kResultFalse;
bus.channelCount = 2;
return kResultTrue;
};
auto bufferL = std::unique_ptr<double[]> (new double[10]);
auto bufferR = std::unique_ptr<double[]> (new double[10]);
double* buffers[2] = {bufferL.get (), bufferR.get ()};
HostProcessData pd;
EXPECT_TRUE (pd.prepare (tc, 0, kSample64));
EXPECT_EQ (pd.numInputs, 1);
EXPECT_EQ (pd.numOutputs, 1);
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 1, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers64 (BusDirections::kInput, 1, nullptr, 0));
EXPECT_TRUE (pd.setChannelBuffers64 (BusDirections::kInput, 0, buffers, 2));
EXPECT_TRUE (pd.setChannelBuffers64 (BusDirections::kOutput, 0, buffers, 2));
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kInput, 0, nullptr, 0));
EXPECT_FALSE (pd.setChannelBuffers (BusDirections::kOutput, 0, nullptr, 0));
EXPECT_EQ (pd.inputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.inputs[0].channelBuffers64[1], bufferR.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[0], bufferL.get ());
EXPECT_EQ (pd.outputs[0].channelBuffers64[1], bufferR.get ());
return true;
});
});
//------------------------------------------------------------------------
} // anonymous
} // Vst
} // Steinberg
@@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/AudioIO.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/vst/vsttypes.h"
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AUComponent.h>
#include <vector>
#ifndef __OBJC__
struct UIImage;
struct NSString;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class AudioIO;
//------------------------------------------------------------------------
class IMidiProcessor
{
public:
virtual void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread) = 0;
};
//------------------------------------------------------------------------
class IAudioIOProcessor : public IMidiProcessor
{
public:
virtual void willStartAudio (AudioIO* audioIO) = 0;
virtual void didStopAudio (AudioIO* audioIO) = 0;
virtual void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) = 0;
};
//------------------------------------------------------------------------
class AudioIO
{
public:
static AudioIO* instance ();
tresult init (OSType type, OSType subType, OSType manufacturer, CFStringRef name);
bool switchToHost ();
bool sendRemoteControlEvent (AudioUnitRemoteControlEvent event);
UIImage* getHostIcon ();
tresult start ();
tresult stop ();
tresult addProcessor (IAudioIOProcessor* processor);
tresult removeProcessor (IAudioIOProcessor* processor);
// accessors
AudioUnit getRemoteIO () const { return remoteIO; }
SampleRate getSampleRate () const { return sampleRate; }
bool getInterAppAudioConnected () const { return interAppAudioConnected; }
// host context information
bool getBeatAndTempo (Float64& beat, Float64& tempo);
bool getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator,
UInt32& timeSigDenominator, Float64& downBeat);
bool getTransportState (Boolean& isPlaying, Boolean& isRecording,
Boolean& transportStateChanged, Float64& sampleInTimeLine,
Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat);
void setStaticFallbackTempo (Float64 tempo) { staticTempo = tempo; }
Float64 getStaticFallbackTempo () const { return staticTempo; }
static NSString* kConnectionStateChange;
//------------------------------------------------------------------------
protected:
AudioIO ();
~AudioIO ();
static OSStatus inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
static OSStatus renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
static void propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope, AudioUnitElement inElement);
static void midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1,
UInt32 inData2, UInt32 inOffsetSampleFrame);
OSStatus inputCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
OSStatus renderCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
void midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2,
UInt32 inOffsetSampleFrame);
void remoteIOPropertyChanged (AudioUnitPropertyID inID, AudioUnitScope inScope,
AudioUnitElement inElement);
void setAudioSessionActive (bool state);
tresult setupRemoteIO (OSType type);
tresult setupAUGraph (OSType type);
void updateInterAppAudioConnectionState ();
AudioUnit remoteIO {nullptr};
AUGraph graph {nullptr};
AudioBufferList* ioBufferList {nullptr};
HostCallbackInfo hostCallback {};
UInt32 maxFrames {4096};
Float64 staticTempo {120.};
SampleRate sampleRate;
bool interAppAudioConnected {false};
std::vector<IAudioIOProcessor*> audioProcessors;
enum InternalState
{
kUninitialized,
kInitialized,
kStarted,
};
InternalState internalState {kUninitialized};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,554 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/AudioIO.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "AudioIO.h"
#import "MidiIO.h"
#import "pluginterfaces/base/fstrdefs.h"
#import <AVFoundation/AVAudioSession.h>
#import <AudioUnit/AudioUnit.h>
#import <UIKit/UIKit.h>
#define FORCE_INLINE __attribute__ ((always_inline))
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
static AudioBufferList* createBuffers (uint32 numChannels, uint32 maxFrames, uint32 frameSize)
{
AudioBufferList* result =
(AudioBufferList*)malloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * numChannels);
result->mNumberBuffers = numChannels;
for (int32 i = 0; i < numChannels; i++)
{
result->mBuffers[i].mDataByteSize = maxFrames * sizeof (float);
result->mBuffers[i].mData = calloc (1, result->mBuffers[i].mDataByteSize);
result->mBuffers[i].mNumberChannels = 1;
}
return result;
}
//------------------------------------------------------------------------
static void freeAudioBufferList (AudioBufferList* audioBufferList)
{
for (uint32 i = 0; i < audioBufferList->mNumberBuffers; i++)
{
free (audioBufferList->mBuffers[i].mData);
}
free (audioBufferList);
}
//------------------------------------------------------------------------
NSString* AudioIO::kConnectionStateChange = @"AudioIO::kConnectionStateChange";
//------------------------------------------------------------------------
AudioIO::AudioIO ()
{
sampleRate = [[AVAudioSession sharedInstance] sampleRate];
MidiIO::instance ();
}
//------------------------------------------------------------------------
AudioIO::~AudioIO ()
{
if (ioBufferList)
freeAudioBufferList (ioBufferList);
}
//------------------------------------------------------------------------
AudioIO* AudioIO::instance ()
{
static AudioIO gInstance;
return &gInstance;
}
//------------------------------------------------------------------------
tresult AudioIO::setupRemoteIO (OSType type)
{
if (remoteIO != nullptr)
{
AudioStreamBasicDescription streamFormat = {};
streamFormat.mChannelsPerFrame = 2;
streamFormat.mSampleRate = sampleRate;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags =
kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
streamFormat.mBytesPerFrame = streamFormat.mBytesPerPacket = sizeof (Float32);
streamFormat.mBitsPerChannel = 32;
streamFormat.mFramesPerPacket = 1;
OSStatus status =
AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
1, &streamFormat, sizeof (streamFormat));
if (status != noErr)
return kInternalError;
status =
AudioUnitSetProperty (remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
0, &streamFormat, sizeof (streamFormat));
if (status != noErr)
return kInternalError;
status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 1, &maxFrames, sizeof (maxFrames));
if (status != noErr)
return kInternalError;
bool needInput = (type == kAudioUnitType_RemoteGenerator ||
type == kAudioUnitType_RemoteInstrument) == false;
UInt32 flag = 1;
if (needInput)
{
// enable IO Input
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input, 1, &flag, sizeof (flag));
if (status != noErr)
return kInternalError;
}
// enable IO Output
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output, 0, &flag, sizeof (flag));
if (status != noErr)
return kInternalError;
AURenderCallbackStruct renderCallback = {};
if (needInput)
{
renderCallback.inputProc = inputCallbackStatic;
renderCallback.inputProcRefCon = this;
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, 1, &renderCallback,
sizeof (renderCallback));
if (status != noErr)
return kInternalError;
}
renderCallback.inputProc = renderCallbackStatic;
renderCallback.inputProcRefCon = this;
status = AudioUnitSetProperty (remoteIO, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global, 0, &renderCallback,
sizeof (renderCallback));
if (status != noErr)
return kInternalError;
if (type == kAudioUnitType_RemoteInstrument || type == kAudioUnitType_RemoteMusicEffect)
{
AudioOutputUnitMIDICallbacks callBackStruct = {};
callBackStruct.userData = this;
callBackStruct.MIDIEventProc = midiEventCallbackStatic;
status = AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_MIDICallbacks,
kAudioUnitScope_Global, 0, &callBackStruct,
sizeof (callBackStruct));
if (status != noErr)
{
NSLog (@"Setting MIDICallback on OutputUnit failed");
}
}
if (ioBufferList)
freeAudioBufferList (ioBufferList);
ioBufferList =
createBuffers (streamFormat.mChannelsPerFrame, maxFrames, streamFormat.mBytesPerFrame);
if (ioBufferList == nullptr)
return kOutOfMemory;
status = AudioUnitAddPropertyListener (remoteIO, kAudioUnitProperty_IsInterAppConnected,
propertyChangeStatic, this);
status = AudioUnitAddPropertyListener (
remoteIO, kAudioOutputUnitProperty_HostTransportState, propertyChangeStatic, this);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::setupAUGraph (OSType type)
{
if (graph == nullptr)
{
OSStatus status = NewAUGraph (&graph);
if (status != noErr)
return kInternalError;
AudioComponentDescription iOUnitDescription;
iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
iOUnitDescription.componentFlags = 0;
iOUnitDescription.componentFlagsMask = 0;
iOUnitDescription.componentType = kAudioUnitType_Output;
iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
AUNode remoteIONode;
status = AUGraphAddNode (graph, &iOUnitDescription, &remoteIONode);
if (status != noErr)
return kInternalError;
status = AUGraphOpen (graph);
if (status != noErr)
return kInternalError;
status = AUGraphNodeInfo (graph, remoteIONode, nullptr, &remoteIO);
if (status != noErr)
return kInternalError;
return setupRemoteIO (type);
}
return kResultFalse;
}
//------------------------------------------------------------------------
void AudioIO::updateInterAppAudioConnectionState ()
{
if (remoteIO)
{
UInt32 connected;
UInt32 dataSize = sizeof (connected);
OSStatus status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_IsInterAppConnected,
kAudioUnitScope_Global, 0, &connected, &dataSize);
if (status == noErr)
{
if (interAppAudioConnected != connected)
{
if (connected)
{
UInt32 size = sizeof (HostCallbackInfo);
status = AudioUnitGetProperty (remoteIO, kAudioUnitProperty_HostCallbacks,
kAudioUnitScope_Global, 0, &hostCallback, &size);
}
else
{
memset (&hostCallback, 0, sizeof (HostCallbackInfo));
}
interAppAudioConnected = connected > 0 ? true : false;
[[NSNotificationCenter defaultCenter] postNotificationName:kConnectionStateChange
object:nil];
}
}
}
}
//------------------------------------------------------------------------
tresult AudioIO::init (OSType type, OSType subType, OSType manufacturer, CFStringRef name)
{
tresult result = setupAUGraph (type);
if (result != kResultTrue)
return result;
AudioComponentDescription desc = {type, subType, manufacturer, 0, 0};
OSStatus status = AudioOutputUnitPublish (&desc, name, 0, remoteIO);
if (status != noErr)
{
NSLog (@"AudioOutputUnitPublish failed with status:%d", (int)status);
}
internalState = kInitialized;
return result;
}
//------------------------------------------------------------------------
void AudioIO::setAudioSessionActive (bool state)
{
NSError* error;
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setPreferredSampleRate:sampleRate error:&error];
[session setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:&error];
[session setActive:(state ? YES : NO)error:&error];
}
//------------------------------------------------------------------------
tresult AudioIO::start ()
{
if (internalState == kInitialized)
{
bool appIsActive =
[UIApplication sharedApplication].applicationState == UIApplicationStateActive;
if (!(appIsActive || interAppAudioConnected))
{
return kResultFalse;
}
setAudioSessionActive (true);
Boolean graphInitialized = true;
OSStatus status = AUGraphIsInitialized (graph, &graphInitialized);
if (status != noErr)
return kInternalError;
if (graphInitialized == false)
{
status = AUGraphInitialize (graph);
if (status != noErr)
return kInternalError;
updateInterAppAudioConnectionState ();
}
for (auto processor : audioProcessors)
{
processor->willStartAudio (this);
}
status = AUGraphStart (graph);
if (status == noErr)
{
internalState = kStarted;
updateInterAppAudioConnectionState ();
return kResultTrue;
}
return kInternalError;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::stop ()
{
if (internalState == kStarted)
{
if (AUGraphStop (graph) == noErr)
{
for (auto processor : audioProcessors)
{
processor->didStopAudio (this);
}
internalState = kInitialized;
if (interAppAudioConnected == false)
setAudioSessionActive (false);
return kResultTrue;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::addProcessor (IAudioIOProcessor* processor)
{
if (internalState == kInitialized)
{
audioProcessors.push_back (processor);
MidiIO::instance ().addProcessor (processor);
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult AudioIO::removeProcessor (IAudioIOProcessor* processor)
{
if (internalState == kInitialized)
{
auto it = std::find (audioProcessors.begin (), audioProcessors.end (), processor);
if (it != audioProcessors.end ())
{
audioProcessors.erase (it);
MidiIO::instance ().removeProcessor (processor);
return kResultTrue;
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
bool AudioIO::switchToHost ()
{
if (remoteIO && interAppAudioConnected)
{
CFURLRef instrumentUrl;
UInt32 dataSize = sizeof (instrumentUrl);
OSStatus result =
AudioUnitGetProperty (remoteIO, kAudioUnitProperty_PeerURL, kAudioUnitScope_Global, 0,
&instrumentUrl, &dataSize);
if (result == noErr)
{
[[UIApplication sharedApplication] openURL:(__bridge NSURL*)instrumentUrl];
return true;
}
}
return false;
}
//------------------------------------------------------------------------
bool AudioIO::sendRemoteControlEvent (AudioUnitRemoteControlEvent event)
{
if (remoteIO && interAppAudioConnected)
{
UInt32 controlEvent = event;
UInt32 dataSize = sizeof (controlEvent);
OSStatus status =
AudioUnitSetProperty (remoteIO, kAudioOutputUnitProperty_RemoteControlToHost,
kAudioUnitScope_Global, 0, &controlEvent, dataSize);
return status == noErr;
}
return false;
}
//------------------------------------------------------------------------
UIImage* AudioIO::getHostIcon ()
{
if (remoteIO && interAppAudioConnected)
{
return AudioOutputUnitGetHostIcon (remoteIO, 128);
}
return nil;
}
//------------------------------------------------------------------------
bool AudioIO::getBeatAndTempo (Float64& beat, Float64& tempo)
{
if (hostCallback.beatAndTempoProc)
{
if (hostCallback.beatAndTempoProc (hostCallback.hostUserData, &beat, &tempo) == noErr)
return true;
}
tempo = staticTempo;
beat = 0;
return true;
}
//------------------------------------------------------------------------
bool AudioIO::getMusicalTimeLocation (UInt32& deltaSampleOffset, Float32& timeSigNumerator,
UInt32& timeSigDenominator, Float64& downBeat)
{
if (hostCallback.musicalTimeLocationProc)
{
if (hostCallback.musicalTimeLocationProc (hostCallback.hostUserData, &deltaSampleOffset,
&timeSigNumerator, &timeSigDenominator,
&downBeat) == noErr)
return true;
}
return false;
}
//------------------------------------------------------------------------
bool AudioIO::getTransportState (Boolean& isPlaying, Boolean& isRecording,
Boolean& transportStateChanged, Float64& sampleInTimeLine,
Boolean& isCycling, Float64& cycleStartBeat, Float64& cycleEndBeat)
{
if (hostCallback.transportStateProc2)
{
if (hostCallback.transportStateProc2 (hostCallback.hostUserData, &isPlaying, &isRecording,
&transportStateChanged, &sampleInTimeLine, &isCycling,
&cycleStartBeat, &cycleEndBeat) == noErr)
return true;
}
return false;
}
//------------------------------------------------------------------------
FORCE_INLINE void AudioIO::remoteIOPropertyChanged (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement)
{
if (inID == kAudioUnitProperty_IsInterAppConnected)
{
bool wasConnected = interAppAudioConnected;
updateInterAppAudioConnectionState ();
if (wasConnected != interAppAudioConnected)
{
stop ();
start ();
}
}
}
//------------------------------------------------------------------------
FORCE_INLINE OSStatus AudioIO::renderCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList* ioData)
{
if (ioData->mNumberBuffers == ioBufferList->mNumberBuffers)
{
for (uint32 i = 0; i < ioData->mNumberBuffers; i++)
{
memcpy (ioData->mBuffers[i].mData, ioBufferList->mBuffers[i].mData,
ioData->mBuffers[i].mDataByteSize);
}
bool outputIsSilence =
ioActionFlags ? *ioActionFlags & kAudioUnitRenderAction_OutputIsSilence : false;
for (auto processor : audioProcessors)
{
outputIsSilence = false;
processor->process (inTimeStamp, inBusNumber, inNumberFrames, ioData, outputIsSilence,
this);
}
if (ioActionFlags)
{
*ioActionFlags = outputIsSilence ? kAudioUnitRenderAction_OutputIsSilence : 0;
}
}
return noErr;
}
//------------------------------------------------------------------------
FORCE_INLINE OSStatus AudioIO::inputCallback (AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
OSStatus status = AudioUnitRender (remoteIO, ioActionFlags, inTimeStamp, inBusNumber,
inNumberFrames, ioBufferList);
return status;
}
//------------------------------------------------------------------------
FORCE_INLINE void AudioIO::midiEventCallback (UInt32 inStatus, UInt32 inData1, UInt32 inData2,
UInt32 inOffsetSampleFrame)
{
for (auto processor : audioProcessors)
{
processor->onMIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame, true);
}
}
//------------------------------------------------------------------------
OSStatus AudioIO::inputCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
AudioIO* io = (AudioIO*)inRefCon;
return io->inputCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
//------------------------------------------------------------------------
OSStatus AudioIO::renderCallbackStatic (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
AudioIO* io = (AudioIO*)inRefCon;
return io->renderCallback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
}
//------------------------------------------------------------------------
void AudioIO::propertyChangeStatic (void* inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID,
AudioUnitScope inScope, AudioUnitElement inElement)
{
AudioIO* audioIO = (AudioIO*)inRefCon;
audioIO->remoteIOPropertyChanged (inID, inScope, inElement);
}
//------------------------------------------------------------------------
void AudioIO::midiEventCallbackStatic (void* inRefCon, UInt32 inStatus, UInt32 inData1,
UInt32 inData2, UInt32 inOffsetSampleFrame)
{
AudioIO* audioIO = (AudioIO*)inRefCon;
audioIO->midiEventCallback (inStatus, inData1, inData2, inOffsetSampleFrame);
}
}
}
}
@@ -0,0 +1,51 @@
if(SMTG_MAC)
option(SMTG_BUILD_INTERAPPAUDIO "Enable building the iOS InterAppAudio examples (deprecated)" OFF)
if(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO)
message("[SMTG] ********************************************************************************************************************************")
message("[SMTG] * The iOS InterAppAudio wrapper is deprecated and may be removed in the next SDK update. Please switch to AudioUnit V3 on iOS. *")
message("[SMTG] ********************************************************************************************************************************")
set(target interappaudio)
set(${target}_sources
AudioIO.mm
AudioIO.h
HostApp.mm
HostApp.h
MidiIO.mm
MidiIO.h
PresetBrowserViewController.mm
PresetBrowserViewController.h
PresetManager.mm
PresetManager.h
PresetSaveViewController.mm
PresetSaveViewController.h
SettingsViewController.mm
SettingsViewController.h
VST3Editor.mm
VST3Editor.h
VST3Plugin.mm
VST3Plugin.h
VSTInterAppAudioAppDelegateBase.mm
VSTInterAppAudioAppDelegateBase.h
)
add_library(${target} STATIC ${${target}_sources})
smtg_set_platform_ios(${target})
set_target_properties(${target}
PROPERTIES
${SDK_IDE_LIBS_FOLDER}
)
target_link_libraries(${target}
PRIVATE
sdk_ios
"-framework CoreGraphics"
"-framework UIKit"
"-framework CoreMIDI"
"-framework AudioToolbox"
"-framework AVFoundation"
)
endif(XCODE AND SMTG_ENABLE_IOS_TARGETS AND SMTG_BUILD_INTERAPPAUDIO)
endif(SMTG_MAC)
@@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/HostApp.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#import "public.sdk/source/vst/hosting/hostclasses.h"
#import "base/source/fobject.h"
#import "pluginterfaces/vst/ivstinterappaudio.h"
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class VST3Plugin;
//-----------------------------------------------------------------------------
class InterAppAudioHostApp : public FObject, public HostApplication, public IInterAppAudioHost
{
public:
//-----------------------------------------------------------------------------
static InterAppAudioHostApp* instance ();
void setPlugin (VST3Plugin* plugin);
VST3Plugin* getPlugin () const { return plugin; }
//-----------------------------------------------------------------------------
// IInterAppAudioHost
tresult PLUGIN_API getScreenSize (ViewRect* size, float* scale) override;
tresult PLUGIN_API connectedToHost () override;
tresult PLUGIN_API switchToHost () override;
tresult PLUGIN_API sendRemoteControlEvent (uint32 event) override;
tresult PLUGIN_API getHostIcon (void** icon) override;
tresult PLUGIN_API scheduleEventFromUI (Event& event) override;
IInterAppAudioPresetManager* PLUGIN_API createPresetManager (const TUID& cid) override;
tresult PLUGIN_API showSettingsView () override;
//-----------------------------------------------------------------------------
// HostApplication
tresult PLUGIN_API getName (String128 name) override;
OBJ_METHODS (InterAppAudioHostApp, FObject)
REFCOUNT_METHODS (FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IHostApplication)
DEF_INTERFACE (IInterAppAudioHost)
END_DEFINE_INTERFACES (FObject)
protected:
InterAppAudioHostApp ();
VST3Plugin* plugin {nullptr};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,149 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/HostApp.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "HostApp.h"
#import "AudioIO.h"
#import "PresetManager.h"
#import "SettingsViewController.h"
#import "VST3Plugin.h"
#import "base/source/updatehandler.h"
#import "pluginterfaces/gui/iplugview.h"
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
InterAppAudioHostApp* InterAppAudioHostApp::instance ()
{
static InterAppAudioHostApp gInstance;
return &gInstance;
}
//-----------------------------------------------------------------------------
InterAppAudioHostApp::InterAppAudioHostApp () = default;
//-----------------------------------------------------------------------------
void InterAppAudioHostApp::setPlugin (VST3Plugin* plugin)
{
this->plugin = plugin;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::getName (String128 name)
{
String str ("InterAppAudioHost");
str.copyTo (name, 0, 127);
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::getScreenSize (ViewRect* size, float* scale)
{
if (size)
{
UIScreen* screen = [UIScreen mainScreen];
CGSize s = [screen currentMode].size;
UIWindow* window = [[[UIApplication sharedApplication] windows] objectAtIndex:0];
if (window)
{
NSArray* subViews = [window subviews];
if ([subViews count] == 1)
{
s = [[subViews objectAtIndex:0] bounds].size;
}
}
size->left = 0;
size->top = 0;
size->right = s.width;
size->bottom = s.height;
if (scale)
{
*scale = screen.scale;
}
return kResultTrue;
}
return kInvalidArgument;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::connectedToHost ()
{
return AudioIO::instance ()->getInterAppAudioConnected () ? kResultTrue : kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::switchToHost ()
{
return AudioIO::instance ()->switchToHost () ? kResultTrue : kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::sendRemoteControlEvent (uint32 event)
{
return AudioIO::instance ()->sendRemoteControlEvent (
static_cast<AudioUnitRemoteControlEvent> (event)) ?
kResultTrue :
kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::getHostIcon (void** icon)
{
if (icon)
{
UIImage* hostIcon = AudioIO::instance ()->getHostIcon ();
if (hostIcon)
{
CGImageRef cgImage = [hostIcon CGImage];
if (cgImage)
{
*icon = cgImage;
return kResultTrue;
}
}
return kNotImplemented;
}
return kInvalidArgument;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::scheduleEventFromUI (Event& event)
{
if (plugin)
{
return plugin->scheduleEventFromUI (event);
}
return kNotInitialized;
}
//-----------------------------------------------------------------------------
IInterAppAudioPresetManager* PLUGIN_API InterAppAudioHostApp::createPresetManager (const TUID& cid)
{
return plugin ? new PresetManager (plugin, cid) : nullptr;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API InterAppAudioHostApp::showSettingsView ()
{
showIOSettings ();
return kResultTrue;
}
}
}
}
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="16B2555" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
@@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/MidiIO.h
// Created by : Steinberg, 09/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "AudioIO.h"
#include <CoreMIDI/CoreMIDI.h>
#include <vector>
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//-----------------------------------------------------------------------------
class MidiIO
{
public:
static MidiIO& instance ();
bool setEnabled (bool state);
bool isEnabled () const;
// MIDI Network is experimental, do not use yet
void setMidiNetworkEnabled (bool state);
bool isMidiNetworkEnabled () const;
void setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy);
MIDINetworkConnectionPolicy getMidiNetworkPolicy () const;
void addProcessor (IMidiProcessor* processor);
void removeProcessor (IMidiProcessor* processor);
//-----------------------------------------------------------------------------
private:
MidiIO ();
~MidiIO ();
void onInput (const MIDIPacketList* pktlist);
void onSourceAdded (MIDIObjectRef source);
void onSetupChanged ();
void disconnectSources ();
MIDIClientRef client {0};
MIDIPortRef inputPort {0};
MIDIEndpointRef destPort {0};
using MidiProcessors = std::vector<IMidiProcessor*>;
MidiProcessors midiProcessors;
using ConnectionList = std::vector<MIDIEndpointRef>;
ConnectionList connectedSources;
static void readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon);
static void notifyProc (const MIDINotification* message, void* refCon);
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,206 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/MidiIO.mm
// Created by : Steinberg, 09/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "MidiIO.h"
#import <CoreMIDI/MIDINetworkSession.h>
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//-----------------------------------------------------------------------------
MidiIO& MidiIO::instance ()
{
static MidiIO gInstance;
return gInstance;
}
//-----------------------------------------------------------------------------
MidiIO::MidiIO () = default;
//-----------------------------------------------------------------------------
MidiIO::~MidiIO ()
{
setEnabled (false);
}
//-----------------------------------------------------------------------------
void MidiIO::addProcessor (IMidiProcessor* processor)
{
midiProcessors.push_back (processor);
}
//-----------------------------------------------------------------------------
void MidiIO::removeProcessor (IMidiProcessor* processor)
{
auto it = std::find (midiProcessors.begin (), midiProcessors.end (), processor);
if (it != midiProcessors.end ())
{
midiProcessors.erase (it);
}
}
//-----------------------------------------------------------------------------
bool MidiIO::isEnabled () const
{
return client != 0;
}
//-----------------------------------------------------------------------------
bool MidiIO::setEnabled (bool state)
{
if (state)
{
if (client)
return true;
OSStatus err;
NSString* name = [[NSBundle mainBundle] bundleIdentifier];
if ((err =
MIDIClientCreate ((__bridge CFStringRef)name, notifyProc, this, &client) != noErr))
return false;
if ((err = MIDIInputPortCreate (client, CFSTR ("Input"), readProc, this, &inputPort) !=
noErr))
{
MIDIClientDispose (client);
client = 0;
return false;
}
name = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleDisplayName"];
if ((err = MIDIDestinationCreate (client, (__bridge CFStringRef)name, readProc, this,
&destPort) != noErr))
{
MIDIPortDispose (inputPort);
inputPort = 0;
MIDIClientDispose (client);
client = 0;
return false;
}
}
else
{
if (client == 0)
return true;
disconnectSources ();
MIDIEndpointDispose (destPort);
destPort = 0;
MIDIPortDispose (inputPort);
inputPort = 0;
MIDIClientDispose (client);
client = 0;
}
return true;
}
//-----------------------------------------------------------------------------
void MidiIO::setMidiNetworkEnabled (bool state)
{
if (inputPort && isMidiNetworkEnabled () != state)
{
if (!state)
{
MIDIPortDisconnectSource (inputPort,
[MIDINetworkSession defaultSession].sourceEndpoint);
}
[MIDINetworkSession defaultSession].enabled = state;
if (state)
{
MIDIPortConnectSource (inputPort, [MIDINetworkSession defaultSession].sourceEndpoint,
0);
}
}
}
//-----------------------------------------------------------------------------
bool MidiIO::isMidiNetworkEnabled () const
{
return [MIDINetworkSession defaultSession].isEnabled;
}
//-----------------------------------------------------------------------------
void MidiIO::setMidiNetworkPolicy (MIDINetworkConnectionPolicy policy)
{
[MIDINetworkSession defaultSession].connectionPolicy = policy;
}
//-----------------------------------------------------------------------------
MIDINetworkConnectionPolicy MidiIO::getMidiNetworkPolicy () const
{
return [MIDINetworkSession defaultSession].connectionPolicy;
}
//-----------------------------------------------------------------------------
void MidiIO::onInput (const MIDIPacketList* pktlist)
{
const MIDIPacket* packet = &pktlist->packet[0];
for (UInt32 i = 0; i < pktlist->numPackets; i++)
{
for (auto processor : midiProcessors)
{
processor->onMIDIEvent (packet->data[0], packet->data[1], packet->data[2], 0, false);
}
packet = MIDIPacketNext (packet);
}
}
//-----------------------------------------------------------------------------
void MidiIO::onSourceAdded (MIDIObjectRef source)
{
connectedSources.push_back ((MIDIEndpointRef)source);
MIDIPortConnectSource (inputPort, (MIDIEndpointRef)source, NULL);
}
//-----------------------------------------------------------------------------
void MidiIO::disconnectSources ()
{
for (auto source : connectedSources)
MIDIPortDisconnectSource (inputPort, source);
connectedSources.clear ();
}
//-----------------------------------------------------------------------------
void MidiIO::onSetupChanged ()
{
disconnectSources ();
ItemCount numSources = MIDIGetNumberOfSources ();
for (ItemCount i = 0; i < numSources; i++)
{
onSourceAdded (MIDIGetSource (i));
}
}
//-----------------------------------------------------------------------------
void MidiIO::readProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* srcConnRefCon)
{
MidiIO* io = static_cast<MidiIO*> (readProcRefCon);
io->onInput (pktlist);
}
//-----------------------------------------------------------------------------
void MidiIO::notifyProc (const MIDINotification* message, void* refCon)
{
if (message->messageID == kMIDIMsgSetupChanged)
{
MidiIO* mio = (MidiIO*)refCon;
mio->onSetupChanged ();
}
}
}
}
} // namespaces
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="ipad9_7" orientation="landscape">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PresetBrowserViewController">
<connections>
<outlet property="containerView" destination="klJ-ou-M84" id="oc3-yW-Wj1"/>
<outlet property="presetTableView" destination="7ve-UC-DYv" id="DY3-Yy-Nem"/>
<outlet property="view" destination="2" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="klJ-ou-M84">
<rect key="frame" x="163" y="30" width="698" height="708"/>
<subviews>
<tableView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="7ve-UC-DYv">
<rect key="frame" x="20" y="20" width="658" height="630"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="separatorColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="dataSource" destination="-1" id="FIu-tQ-zaK"/>
<outlet property="delegate" destination="-1" id="Wmb-vA-YdQ"/>
</connections>
</tableView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IWt-40-Jgp">
<rect key="frame" x="20" y="668" width="30" height="30"/>
<state key="normal" title="Edit">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="toggleEditMode:" destination="-1" eventType="touchUpInside" id="uii-wI-Qvl"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="z3K-EZ-Ed5">
<rect key="frame" x="639" y="668" width="39" height="30"/>
<state key="normal" title="Close">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="cancel:" destination="-1" eventType="touchUpInside" id="BvF-YD-Tk0"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.95000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="7ve-UC-DYv" firstAttribute="top" secondItem="klJ-ou-M84" secondAttribute="top" constant="20" id="3Pq-2h-vKi"/>
<constraint firstAttribute="bottom" secondItem="z3K-EZ-Ed5" secondAttribute="bottom" constant="10" id="9ML-qG-4d0"/>
<constraint firstAttribute="trailing" secondItem="7ve-UC-DYv" secondAttribute="trailing" constant="20" id="Ioq-nG-kzq"/>
<constraint firstItem="IWt-40-Jgp" firstAttribute="leading" secondItem="klJ-ou-M84" secondAttribute="leading" constant="20" id="ZJq-DX-wOM"/>
<constraint firstAttribute="trailing" secondItem="z3K-EZ-Ed5" secondAttribute="trailing" constant="20" id="ag2-M7-lCo"/>
<constraint firstAttribute="bottom" secondItem="7ve-UC-DYv" secondAttribute="bottom" constant="58" id="cj6-1q-Q3I"/>
<constraint firstAttribute="bottom" secondItem="IWt-40-Jgp" secondAttribute="bottom" constant="10" id="kmf-ho-kIC"/>
<constraint firstItem="7ve-UC-DYv" firstAttribute="leading" secondItem="klJ-ou-M84" secondAttribute="leading" constant="20" id="xiJ-zB-UUa"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="klJ-ou-M84" firstAttribute="leading" secondItem="2" secondAttribute="leading" constant="163" id="5LY-UL-APH"/>
<constraint firstItem="klJ-ou-M84" firstAttribute="top" secondItem="2" secondAttribute="top" constant="30" id="G9o-k3-fOs"/>
<constraint firstAttribute="bottom" secondItem="klJ-ou-M84" secondAttribute="bottom" constant="30" id="YXO-2R-3RQ"/>
<constraint firstItem="klJ-ou-M84" firstAttribute="centerX" secondItem="2" secondAttribute="centerX" id="ZKb-qU-rmk"/>
<constraint firstItem="klJ-ou-M84" firstAttribute="centerY" secondItem="2" secondAttribute="centerY" id="fR8-kU-uaI"/>
<constraint firstAttribute="trailing" secondItem="klJ-ou-M84" secondAttribute="trailing" constant="163" id="uzi-AN-g6z"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
</view>
</objects>
</document>
@@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.h
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <functional>
//-----------------------------------------------------------------------------
@interface PresetBrowserViewController
: UIViewController <UITableViewDataSource, UITableViewDelegate>
//-----------------------------------------------------------------------------
- (id)initWithCallback:(std::function<void (const char* presetPath)>)callback;
- (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets;
@end
#endif // __OBJC__
/// \endcond
@@ -0,0 +1,251 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetBrowserViewController.mm
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "PresetBrowserViewController.h"
#import "pluginterfaces/base/funknown.h"
//------------------------------------------------------------------------
@interface PresetBrowserViewController ()
//------------------------------------------------------------------------
{
IBOutlet UITableView* presetTableView;
IBOutlet UIView* containerView;
std::function<void (const char* presetPath)> callback;
Steinberg::FUID uid;
}
@property (strong) NSArray* factoryPresets;
@property (strong) NSArray* userPresets;
@property (strong) NSArray* displayPresets;
@property (assign) BOOL editMode;
@end
//------------------------------------------------------------------------
@implementation PresetBrowserViewController
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (id)initWithCallback:(std::function<void (const char* presetPath)>)_callback
{
self = [super initWithNibName:@"PresetBrowserView" bundle:nil];
if (self)
{
callback = _callback;
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
UIViewController* rootViewController =
[[UIApplication sharedApplication].windows[0] rootViewController];
[rootViewController presentViewController:self animated:YES completion:^{}];
}
return self;
}
//------------------------------------------------------------------------
- (void)setFactoryPresets:(NSArray*)factoryPresets userPresets:(NSArray*)userPresets
{
self.factoryPresets = factoryPresets;
self.userPresets = userPresets;
[self updatePresetArray];
dispatch_async (dispatch_get_main_queue (), ^{ [presetTableView reloadData]; });
}
//------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
containerView.layer.shadowOpacity = 0.5;
containerView.layer.shadowOffset = CGSizeMake (5, 5);
containerView.layer.shadowRadius = 5;
}
//------------------------------------------------------------------------
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//------------------------------------------------------------------------
- (void)updatePresetArray
{
if (self.userPresets)
{
self.displayPresets = [[self.factoryPresets arrayByAddingObjectsFromArray:self.userPresets]
sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) {
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]];
}];
}
else
{
self.displayPresets = self.factoryPresets;
}
}
//------------------------------------------------------------------------
- (void)removeSelf
{
[self dismissViewControllerAnimated:YES completion:^{}];
}
//------------------------------------------------------------------------
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
NSURL* url = [self.displayPresets objectAtIndex:indexPath.row];
if (url)
{
callback ([[url path] UTF8String]);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (IBAction)toggleEditMode:(id)sender
{
self.editMode = !self.editMode;
if (self.editMode)
{
NSMutableArray* indexPaths = [NSMutableArray new];
for (NSURL* url in self.factoryPresets)
{
NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url];
[indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
[presetTableView deleteRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationFade];
}
else
{
[self updatePresetArray];
NSMutableArray* indexPaths = [NSMutableArray new];
for (NSURL* url in self.factoryPresets)
{
NSUInteger index = [self.displayPresets indexOfObjectIdenticalTo:url];
[indexPaths addObject:[NSIndexPath indexPathForRow:index inSection:0]];
}
[presetTableView insertRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationFade];
}
[presetTableView setEditing:self.editMode animated:YES];
}
//------------------------------------------------------------------------
- (IBAction)cancel:(id)sender
{
if (callback)
{
callback (nullptr);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.editMode)
{
return [self.userPresets count];
}
return [self.displayPresets count];
}
//------------------------------------------------------------------------
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"PresetBrowserCell"];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:@"PresetBrowserCell"];
}
cell.backgroundColor = [UIColor clearColor];
NSURL* presetUrl = nil;
if (self.editMode)
{
presetUrl = [self.userPresets objectAtIndex:indexPath.row];
cell.detailTextLabel.text = @"User";
}
else
{
presetUrl = [self.displayPresets objectAtIndex:indexPath.row];
if ([self.factoryPresets indexOfObject:presetUrl] == NSNotFound)
{
cell.detailTextLabel.text = @"User";
}
else
{
cell.detailTextLabel.text = @"Factory";
}
}
cell.textLabel.text = [[presetUrl lastPathComponent] stringByDeletingPathExtension];
return cell;
}
//------------------------------------------------------------------------
- (BOOL)tableView:(UITableView*)tableView canEditRowAtIndexPath:(NSIndexPath*)indexPath
{
if (self.editMode)
{
return YES;
}
return NO;
}
//------------------------------------------------------------------------
- (void)tableView:(UITableView*)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath*)indexPath
{
NSURL* presetUrl = [self.userPresets objectAtIndex:indexPath.row];
if (presetUrl)
{
NSFileManager* fs = [NSFileManager defaultManager];
NSError* error = nil;
if ([fs removeItemAtURL:presetUrl error:&error] == NO)
{
auto alertController =
[UIAlertController alertControllerWithTitle:[error localizedDescription]
message:[error localizedRecoverySuggestion]
preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
}
else
{
NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.userPresets];
[newArray removeObject:presetUrl];
self.userPresets = newArray;
[presetTableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
}
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
@@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetManager.h
// Created by : Steinberg, 10/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "VST3Plugin.h"
#include "base/source/fstring.h"
#include "pluginterfaces/vst/ivstinterappaudio.h"
#if __OBJC__
@class NSArray, PresetBrowserViewController, PresetSaveViewController;
#else
struct NSArray;
struct PresetBrowserViewController;
struct PresetSaveViewController;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
class PresetManager : public FObject, public IInterAppAudioPresetManager
{
public:
PresetManager (VST3Plugin* plugin, const TUID& cid);
tresult PLUGIN_API runLoadPresetBrowser () override;
tresult PLUGIN_API runSavePresetBrowser () override;
tresult PLUGIN_API loadNextPreset () override;
tresult PLUGIN_API loadPreviousPreset () override;
DEFINE_INTERFACES
DEF_INTERFACE (IInterAppAudioPresetManager)
END_DEFINE_INTERFACES (FObject)
REFCOUNT_METHODS (FObject)
private:
enum PresetPathType
{
kFactory,
kUser
};
NSArray* getPresetPaths (PresetPathType type);
tresult loadPreset (bool next);
tresult loadPreset (const char* path);
void savePreset (const char* path);
VST3Plugin* plugin;
PresetBrowserViewController* visiblePresetBrowserViewController;
PresetSaveViewController* visibleSavePresetViewController;
FUID cid;
String lastPreset;
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,281 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetManager.mm
// Created by : Steinberg, 10/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "PresetManager.h"
#import "PresetBrowserViewController.h"
#import "PresetSaveViewController.h"
#import "public.sdk/source/vst/vstpresetfile.h"
#import "pluginterfaces/vst/ivstattributes.h"
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//-----------------------------------------------------------------------------
class PresetStream : public ReadOnlyBStream, public IStreamAttributes
{
public:
PresetStream (IBStream* sourceStream, TSize sourceOffset, TSize sectionSize,
const char* utf8Path)
: ReadOnlyBStream (sourceStream, sourceOffset, sectionSize), fileName (utf8Path)
{
fileName.toWideString (kCP_Utf8);
}
virtual tresult PLUGIN_API getFileName (String128 name) override
{
if (fileName.length () > 0)
{
fileName.copyTo (name, 0, 128);
return kResultTrue;
}
return kResultFalse;
}
virtual IAttributeList* PLUGIN_API getAttributes () override { return nullptr; }
DEF_INTERFACES_1 (IStreamAttributes, ReadOnlyBStream)
REFCOUNT_METHODS (ReadOnlyBStream)
protected:
String fileName;
};
//-----------------------------------------------------------------------------
PresetManager::PresetManager (VST3Plugin* plugin, const TUID& cid)
: plugin (plugin)
, visiblePresetBrowserViewController (nil)
, visibleSavePresetViewController (nil)
, cid (cid)
{
id obj = [[NSUserDefaults standardUserDefaults] objectForKey:@"PresetManager|lastPreset"];
if (obj && [obj isKindOfClass:[NSString class]])
{
lastPreset = [obj UTF8String];
}
}
//-----------------------------------------------------------------------------
NSArray* PresetManager::getPresetPaths (PresetPathType type)
{
if (type == kFactory)
{
return [[NSBundle mainBundle] URLsForResourcesWithExtension:@"vstpreset"
subdirectory:@"Presets"];
}
NSFileManager* fs = [NSFileManager defaultManager];
NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:Nil
create:YES
error:NULL];
if (documentsUrl)
{
NSMutableArray* userUrls = [NSMutableArray new];
NSDirectoryEnumerator* enumerator =
[fs enumeratorAtURL:documentsUrl
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
errorHandler:nil];
for (NSURL* url in enumerator.allObjects)
{
if ([[url pathExtension] isEqualToString:@"vstpreset"])
{
[userUrls addObject:url];
}
}
return [userUrls sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) {
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]];
}];
}
return nil;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::runLoadPresetBrowser ()
{
if (visiblePresetBrowserViewController != nil)
return kResultFalse;
addRef ();
visiblePresetBrowserViewController =
[[PresetBrowserViewController alloc] initWithCallback:[this] (const char* path) {
loadPreset (path);
visiblePresetBrowserViewController = nil;
release ();
}];
addRef ();
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (visiblePresetBrowserViewController)
{
[visiblePresetBrowserViewController setFactoryPresets:getPresetPaths (kFactory)
userPresets:getPresetPaths (kUser)];
}
release ();
});
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::runSavePresetBrowser ()
{
if (visibleSavePresetViewController != nil)
return kResultFalse;
addRef ();
visibleSavePresetViewController =
[[PresetSaveViewController alloc] initWithCallback:[this] (const char* path) {
savePreset (path);
visibleSavePresetViewController = nil;
release ();
}];
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::loadNextPreset ()
{
return loadPreset (true);
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PresetManager::loadPreviousPreset ()
{
return loadPreset (false);
}
//-----------------------------------------------------------------------------
tresult PresetManager::loadPreset (bool next)
{
NSArray* presets =
[[getPresetPaths (kFactory) arrayByAddingObjectsFromArray:getPresetPaths (kUser)]
sortedArrayUsingComparator:^NSComparisonResult (NSURL* obj1, NSURL* obj2) {
return [[obj1 lastPathComponent] caseInsensitiveCompare:[obj2 lastPathComponent]];
}];
__block NSUInteger index = NSNotFound;
if (lastPreset.isEmpty () == false)
{
NSURL* lastUrl =
[[NSURL fileURLWithPath:[NSString stringWithUTF8String:lastPreset]] fileReferenceURL];
if (lastUrl)
{
[presets enumerateObjectsUsingBlock:^(NSURL* obj, NSUInteger idx, BOOL* stop) {
if ([[obj fileReferenceURL] isEqual:lastUrl])
{
index = idx;
*stop = YES;
}
}];
}
}
if (index == NSNotFound)
{
if (next)
index = [presets count] - 1;
else
index = 1;
}
if (index != NSNotFound)
{
if (next)
{
if (index >= [presets count] - 1)
index = 0;
else
index++;
}
else
{
if (index == 0)
index = [presets count] - 1;
else
index--;
}
return loadPreset ([[[presets objectAtIndex:index] path] UTF8String]);
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
tresult PresetManager::loadPreset (const char* path)
{
if (path)
{
IPtr<IBStream> stream = owned (FileStream::open (path, "r"));
if (stream)
{
[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithUTF8String:path]
forKey:@"PresetManager|lastPreset"];
lastPreset = path;
auto component = U::cast<IComponent> (plugin->getAudioProcessor ());
IEditController* controller = plugin->getEditController ();
if (component)
{
PresetFile pf (stream);
if (!pf.readChunkList ())
return kResultFalse;
if (pf.getClassID () != cid)
return kResultFalse;
const PresetFile::Entry* e = pf.getEntry (kComponentState);
if (e == nullptr)
return kResultFalse;
auto filename = strrchr (path, '/');
if (filename)
filename++;
IPtr<PresetStream> readOnlyBStream =
owned (new PresetStream (stream, e->offset, e->size, filename));
tresult result = component->setState (readOnlyBStream);
if ((result == kResultTrue || result == kNotImplemented) && controller)
{
readOnlyBStream->seek (0, IBStream::kIBSeekSet);
controller->setComponentState (readOnlyBStream);
if (pf.contains (kControllerState))
{
e = pf.getEntry (kControllerState);
if (e)
{
readOnlyBStream =
owned (new PresetStream (stream, e->offset, e->size, filename));
controller->setState (readOnlyBStream);
}
}
}
return result;
}
}
}
return kResultFalse;
}
//-----------------------------------------------------------------------------
void PresetManager::savePreset (const char* path)
{
IBStream* stream = FileStream::open (path, "w");
if (stream)
{
auto component = U::cast<IComponent> (plugin->getAudioProcessor ());
IEditController* controller = plugin->getEditController ();
if (component)
{
PresetFile::savePreset (stream, cid, component, controller);
}
stream->release ();
loadPreset (path);
}
}
}
}
} // namespaces
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="ipad10_5" orientation="landscape">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PresetSaveViewController">
<connections>
<outlet property="containerView" destination="SIT-sP-q5k" id="Pam-4d-cjt"/>
<outlet property="presetName" destination="4mQ-Y5-WZh" id="MJT-bn-9QR"/>
<outlet property="view" destination="2" id="0Me-yB-Sts"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SIT-sP-q5k">
<rect key="frame" x="256" y="192" width="512" height="104"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Preset Name :" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="EGr-Dl-Wf3">
<rect key="frame" x="20" y="20" width="108" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="4mQ-Y5-WZh">
<rect key="frame" x="147" y="16" width="345" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="-1" id="LIF-nc-eQa"/>
</connections>
</textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="v51-mb-0Tu">
<rect key="frame" x="455" y="68" width="37" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Save"/>
<connections>
<action selector="save:" destination="-1" eventType="touchUpInside" id="J1n-Ix-DQA"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SBt-7v-tI9">
<rect key="frame" x="20" y="68" width="53" height="33"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<state key="normal" title="Cancel">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="cancel:" destination="-1" eventType="touchUpInside" id="ibs-yW-oi5"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.95000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="4mQ-Y5-WZh" secondAttribute="trailing" constant="20" id="02g-de-fEz"/>
<constraint firstItem="SBt-7v-tI9" firstAttribute="leading" secondItem="SIT-sP-q5k" secondAttribute="leading" constant="20" id="9Qx-G1-uxi"/>
<constraint firstAttribute="bottom" secondItem="v51-mb-0Tu" secondAttribute="bottom" constant="3" id="FL7-Pl-p0S"/>
<constraint firstItem="4mQ-Y5-WZh" firstAttribute="top" secondItem="SIT-sP-q5k" secondAttribute="top" constant="16" id="M1k-Wm-yVW"/>
<constraint firstItem="4mQ-Y5-WZh" firstAttribute="leading" secondItem="EGr-Dl-Wf3" secondAttribute="trailing" constant="19" id="Mrh-oC-pT4"/>
<constraint firstItem="EGr-Dl-Wf3" firstAttribute="leading" secondItem="SIT-sP-q5k" secondAttribute="leading" constant="20" id="dMD-jj-Yya"/>
<constraint firstItem="EGr-Dl-Wf3" firstAttribute="top" secondItem="SIT-sP-q5k" secondAttribute="top" constant="20" id="dbO-Rh-IOx"/>
<constraint firstAttribute="bottom" secondItem="SBt-7v-tI9" secondAttribute="bottom" constant="3" id="lZC-gE-21j"/>
<constraint firstAttribute="height" constant="104" id="wzX-0f-wG5"/>
<constraint firstAttribute="trailing" secondItem="v51-mb-0Tu" secondAttribute="trailing" constant="20" id="x9j-uT-byM"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="SIT-sP-q5k" secondAttribute="trailing" constant="256" id="QRq-bk-Kbd"/>
<constraint firstItem="SIT-sP-q5k" firstAttribute="top" secondItem="2" secondAttribute="top" constant="192" id="uF1-Qs-yD6"/>
<constraint firstItem="SIT-sP-q5k" firstAttribute="leading" secondItem="2" secondAttribute="leading" constant="256" id="yDr-cE-KWA"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
</view>
</objects>
</document>
@@ -0,0 +1,35 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.h
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <functional>
@interface PresetSaveViewController : UIViewController <UIAlertViewDelegate, UITextFieldDelegate>
- (id)initWithCallback:(std::function<void (const char* presetPath)>)callback;
@end
#endif //__OBJC__
/// \endcond
@@ -0,0 +1,161 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/PresetSaveViewController.mm
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "PresetSaveViewController.h"
#import "pluginterfaces/base/funknown.h"
//------------------------------------------------------------------------
@interface PresetSaveViewController ()
//------------------------------------------------------------------------
{
IBOutlet UIView* containerView;
IBOutlet UITextField* presetName;
std::function<void (const char* presetPath)> callback;
Steinberg::FUID uid;
}
@end
//------------------------------------------------------------------------
@implementation PresetSaveViewController
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (id)initWithCallback:(std::function<void (const char* presetPath)>)_callback
{
self = [super initWithNibName:@"PresetSaveView" bundle:nil];
if (self)
{
callback = _callback;
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
UIViewController* rootViewController =
[[UIApplication sharedApplication].windows[0] rootViewController];
[rootViewController presentViewController:self
animated:YES
completion:^{ [self showKeyboard]; }];
}
return self;
}
//------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
containerView.layer.shadowOpacity = 0.5;
containerView.layer.shadowOffset = CGSizeMake (5, 5);
containerView.layer.shadowRadius = 5;
}
//------------------------------------------------------------------------
- (void)showKeyboard
{
[presetName becomeFirstResponder];
}
//------------------------------------------------------------------------
- (void)removeSelf
{
[self dismissViewControllerAnimated:YES completion:^{}];
}
//------------------------------------------------------------------------
- (NSURL*)presetURL
{
NSFileManager* fs = [NSFileManager defaultManager];
NSURL* documentsUrl = [fs URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:Nil
create:YES
error:NULL];
if (documentsUrl)
{
NSURL* presetPath = [[documentsUrl URLByAppendingPathComponent:presetName.text]
URLByAppendingPathExtension:@"vstpreset"];
return presetPath;
}
return nil;
}
//------------------------------------------------------------------------
- (BOOL)textFieldShouldReturn:(UITextField*)textField
{
if ([textField.text length] > 0)
{
[self save:textField];
return YES;
}
return NO;
}
//------------------------------------------------------------------------
- (IBAction)save:(id)sender
{
if (callback)
{
NSURL* presetPath = [self presetURL];
NSFileManager* fs = [NSFileManager defaultManager];
if ([fs fileExistsAtPath:[presetPath path]])
{
// alert for overwrite
auto alertController = [UIAlertController
alertControllerWithTitle:NSLocalizedString (
@"A Preset with this name already exists",
"Alert title")
message:NSLocalizedString (@"Save it anyway ?", "Alert message")
preferredStyle:UIAlertControllerStyleAlert];
[alertController
addAction:[UIAlertAction
actionWithTitle:NSLocalizedString (@"Save", "Alert Save Button")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction* _Nonnull action) {
callback ([[[self presetURL] path] UTF8String]);
[self removeSelf];
}]];
[alertController
addAction:[UIAlertAction
actionWithTitle:NSLocalizedString (@"Cancel", "Alert Cancel Button")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction* _Nonnull action) {}]];
[self presentViewController:alertController animated:YES completion:nil];
return;
}
callback ([[presetPath path] UTF8String]);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (IBAction)cancel:(id)sender
{
if (callback)
{
callback (nullptr);
}
[self removeSelf];
}
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="ipad9_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="SettingsViewController">
<connections>
<outlet property="containerView" destination="RNQ-ag-wXs" id="Snq-jH-mGX"/>
<outlet property="midiOnSwitch" destination="RUK-3J-c68" id="fsn-qn-B1b"/>
<outlet property="tempoView" destination="7Wa-1f-ZEh" id="aAo-kk-UXa"/>
<outlet property="view" destination="2" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RNQ-ag-wXs">
<rect key="frame" x="272" y="266" width="481" height="236"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RUK-3J-c68">
<rect key="frame" x="165" y="92" width="51" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="enableMidi:" destination="-1" eventType="valueChanged" id="W1y-Cl-XZT"/>
</connections>
</switch>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Enable MIDI Input" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uh8-Ad-fHW">
<rect key="frame" x="20" y="97" width="137" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gsH-Se-9I1">
<rect key="frame" x="220" y="186" width="40" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Close">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="close:" destination="-1" eventType="touchUpInside" id="8fR-1E-VU7"/>
</connections>
</button>
<pickerView contentMode="scaleAspectFit" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Wa-1f-ZEh">
<rect key="frame" x="329" y="0.0" width="86" height="216"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<outlet property="dataSource" destination="-1" id="kTa-mw-jgX"/>
<outlet property="delegate" destination="-1" id="afl-U5-l43"/>
</connections>
</pickerView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="BPM" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ccr-8n-eGh">
<rect key="frame" x="423" y="97" width="38" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tempo :" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jiY-aT-q6h">
<rect key="frame" x="222" y="97" width="97" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.95000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="481" id="CNb-19-uMh"/>
<constraint firstAttribute="height" constant="236" id="Gag-Ic-lk4"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="RNQ-ag-wXs" firstAttribute="centerX" secondItem="2" secondAttribute="centerX" id="d7Y-kL-wHV"/>
<constraint firstItem="RNQ-ag-wXs" firstAttribute="centerY" secondItem="2" secondAttribute="centerY" id="rA4-Fa-esq"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="297" y="-102"/>
</view>
</objects>
</document>
@@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/SettingsViewController.h
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#ifdef __OBJC__
#import <UIKit/UIKit.h>
@interface SettingsViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
@end
#endif // __OBJC__
extern void showIOSettings ();
@@ -0,0 +1,126 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/SettingsViewController.mm
// Created by : Steinberg, 09/2013
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "SettingsViewController.h"
#import "AudioIO.h"
#import "MidiIO.h"
#import <CoreMIDI/MIDINetworkSession.h>
using namespace Steinberg::Vst::InterAppAudio;
static const NSUInteger kMinTempo = 30;
//------------------------------------------------------------------------
@interface SettingsViewController ()
//------------------------------------------------------------------------
{
IBOutlet UIView* containerView;
IBOutlet UISwitch* midiOnSwitch;
IBOutlet UIPickerView* tempoView;
}
@end
//------------------------------------------------------------------------
@implementation SettingsViewController
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (id)init
{
self = [super initWithNibName:@"SettingsView" bundle:nil];
if (self)
{
self.modalPresentationStyle = UIModalPresentationOverCurrentContext;
self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
}
return self;
}
//------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad];
containerView.layer.shadowOpacity = 0.5;
containerView.layer.shadowOffset = CGSizeMake (5, 5);
containerView.layer.shadowRadius = 5;
midiOnSwitch.on = MidiIO::instance ().isEnabled ();
Float64 tempo = AudioIO::instance ()->getStaticFallbackTempo ();
[tempoView selectRow:tempo - kMinTempo inComponent:0 animated:YES];
}
//------------------------------------------------------------------------
- (IBAction)enableMidi:(id)sender
{
BOOL state = midiOnSwitch.on;
MidiIO::instance ().setEnabled (state);
}
//------------------------------------------------------------------------
- (IBAction)close:(id)sender
{
[self dismissViewControllerAnimated:YES completion:^{}];
}
//------------------------------------------------------------------------
- (void)pickerView:(UIPickerView*)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component
{
AudioIO::instance ()->setStaticFallbackTempo (row + kMinTempo);
}
//------------------------------------------------------------------------
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
{
return 1;
}
//------------------------------------------------------------------------
- (NSInteger)pickerView:(UIPickerView*)pickerView numberOfRowsInComponent:(NSInteger)component
{
return 301 - kMinTempo;
}
//------------------------------------------------------------------------
- (NSString*)pickerView:(UIPickerView*)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
return [@(row + kMinTempo) stringValue];
}
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
@end
//------------------------------------------------------------------------
void showIOSettings ()
{
SettingsViewController* controller = [[SettingsViewController alloc] init];
UIViewController* rootViewController =
[[UIApplication sharedApplication].windows[0] rootViewController];
[rootViewController presentViewController:controller animated:YES completion:^{}];
}
@@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Editor.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#import "base/source/fobject.h"
#import "pluginterfaces/gui/iplugview.h"
#import <UIKit/UIKit.h>
namespace Steinberg {
namespace Vst {
class IEditController;
namespace InterAppAudio {
//------------------------------------------------------------------------
class VST3Editor : public FObject, public IPlugFrame
{
public:
//------------------------------------------------------------------------
VST3Editor ();
virtual ~VST3Editor ();
bool init (const CGRect& frame);
bool attach (IEditController* editController);
UIViewController* getViewController () const { return viewController; }
OBJ_METHODS (VST3Editor, FObject)
REFCOUNT_METHODS (FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IPlugFrame)
END_DEFINE_INTERFACES (FObject)
protected:
// IPlugFrame
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) override;
IPlugView* plugView {nullptr};
UIViewController* viewController {nullptr};
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,112 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Editor.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "VST3Editor.h"
#import "pluginterfaces/vst/ivsteditcontroller.h"
//------------------------------------------------------------------------
@interface VST3EditorViewController : UIViewController
//------------------------------------------------------------------------
@end
//------------------------------------------------------------------------
@implementation VST3EditorViewController
//------------------------------------------------------------------------
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
@end
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
VST3Editor::VST3Editor () = default;
//------------------------------------------------------------------------
VST3Editor::~VST3Editor ()
{
if (plugView)
{
plugView->release ();
}
}
//------------------------------------------------------------------------
bool VST3Editor::init (const CGRect& frame)
{
viewController = [VST3EditorViewController new];
viewController.view = [[UIView alloc] initWithFrame:frame];
return true;
}
//------------------------------------------------------------------------
bool VST3Editor::attach (IEditController* editController)
{
auto ec2 = U::cast<IEditController2> (editController);
if (ec2)
{
ec2->setKnobMode (kLinearMode);
}
plugView = editController->createView (ViewType::kEditor);
if (plugView)
{
if (plugView->isPlatformTypeSupported (kPlatformTypeUIView) == kResultTrue)
{
plugView->setFrame (this);
if (plugView->attached ((__bridge void*)viewController.view, kPlatformTypeUIView) ==
kResultTrue)
{
return true;
}
}
plugView->release ();
plugView = nullptr;
}
return false;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Editor::resizeView (IPlugView* view, ViewRect* newSize)
{
if (newSize && plugView && plugView == view)
{
if (view->onSize (newSize) == kResultTrue)
return kResultTrue;
return kResultFalse;
}
return kInvalidArgument;
}
//------------------------------------------------------------------------
} // InterAppAudio
} // Vst
} // Steinberg
@@ -0,0 +1,133 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Plugin.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
/// \cond ignore
#import "AudioIO.h"
#import "public.sdk/source/vst/hosting/eventlist.h"
#import "public.sdk/source/vst/hosting/parameterchanges.h"
#import "public.sdk/source/vst/hosting/processdata.h"
#import "public.sdk/source/vst/utility/ringbuffer.h"
#import "base/source/fobject.h"
#import "base/source/timer.h"
#import "pluginterfaces/vst/ivstaudioprocessor.h"
#import "pluginterfaces/vst/ivsteditcontroller.h"
#import "pluginterfaces/vst/ivstprocesscontext.h"
#import <atomic>
#import <map>
#ifndef __OBJC__
struct NSData;
#endif
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
static const int32 kMaxUIEvents = 100;
//------------------------------------------------------------------------
class VST3Plugin : public FObject,
public IComponentHandler,
public IAudioIOProcessor,
public ITimerCallback
{
public:
//------------------------------------------------------------------------
VST3Plugin ();
virtual ~VST3Plugin ();
bool init ();
IEditController* getEditController () const { return editController; }
IAudioProcessor* getAudioProcessor () const { return processor; }
tresult scheduleEventFromUI (Event& event);
NSData* getProcessorState ();
bool setProcessorState (NSData* data);
NSData* getControllerState ();
bool setControllerState (NSData* data);
OBJ_METHODS (VST3Plugin, FObject)
REFCOUNT_METHODS (FObject)
DEFINE_INTERFACES
DEF_INTERFACE (IComponentHandler)
END_DEFINE_INTERFACES (FObject)
protected:
typedef std::map<uint32, uint32> NoteIDPitchMap;
typedef uint32 ChannelAndCtrlNumber;
typedef std::map<ChannelAndCtrlNumber, ParamID> MIDIControllerToParamIDMap;
void createProcessorAndController ();
void updateProcessContext (AudioIO* audioIO);
MIDIControllerToParamIDMap createMIDIControllerToParamIDMap ();
// IComponentHandler
tresult PLUGIN_API beginEdit (ParamID id) override;
tresult PLUGIN_API performEdit (ParamID id, ParamValue valueNormalized) override;
tresult PLUGIN_API endEdit (ParamID id) override;
tresult PLUGIN_API restartComponent (int32 flags) override;
// IAudioIOProcessor
void willStartAudio (AudioIO* audioIO) override;
void didStopAudio (AudioIO* audioIO) override;
void onMIDIEvent (UInt32 status, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread) override;
void process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO) override;
// ITimerCallback
void onTimer (Timer* timer) override;
IAudioProcessor* processor {nullptr};
IEditController* editController {nullptr};
Timer* timer {nullptr};
HostProcessData processData;
ProcessContext processContext;
ParameterChangeTransfer inputParamChangeTransfer;
ParameterChangeTransfer outputParamChangeTransfer;
ParameterChanges inputParamChanges;
ParameterChanges outputParamChanges;
EventList inputEvents;
NoteIDPitchMap noteIDPitchMap;
std::atomic<int32> lastNodeID {0};
bool processing {false};
MIDIControllerToParamIDMap midiControllerToParamIDMap;
OneReaderOneWriter::RingBuffer<Event> uiScheduledEvents;
static ChannelAndCtrlNumber channelAndCtrlNumber (uint16 channel, CtrlNumber ctrler)
{
return (channel << 16) + ctrler;
}
};
//------------------------------------------------------------------------
} // namespace InterAppAudio
} // namespace Vst
} // namespace Steinberg
/// \endcond
@@ -0,0 +1,598 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VST3Plugin.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "VST3Plugin.h"
#import "HostApp.h"
#import "public.sdk/source/vst/auwrapper/NSDataIBStream.h"
#import "public.sdk/source/vst/hosting/hostclasses.h"
#import "base/source/updatehandler.h"
#import "pluginterfaces/base/ipluginbase.h"
#import "pluginterfaces/vst/ivstinterappaudio.h"
#import "pluginterfaces/vst/ivstmessage.h"
#import "pluginterfaces/vst/ivstmidicontrollers.h"
#import <libkern/OSAtomic.h>
//------------------------------------------------------------------------
extern "C" {
bool bundleEntry (CFBundleRef);
bool bundleExit (void);
}
namespace Steinberg {
namespace Vst {
namespace InterAppAudio {
//------------------------------------------------------------------------
__attribute__ ((constructor)) static void InitUpdateHandler ()
{
UpdateHandler::instance ();
}
//------------------------------------------------------------------------
VST3Plugin::VST3Plugin ()
{
processData.processContext = &processContext;
processData.inputParameterChanges = &inputParamChanges;
processData.outputParameterChanges = &outputParamChanges;
processData.inputEvents = &inputEvents;
}
//------------------------------------------------------------------------
VST3Plugin::~VST3Plugin ()
{
}
//------------------------------------------------------------------------
bool VST3Plugin::init ()
{
if (processor == nullptr && editController == nullptr)
{
::bundleEntry (CFBundleGetMainBundle ());
createProcessorAndController ();
}
return processor && editController;
}
//------------------------------------------------------------------------
void VST3Plugin::createProcessorAndController ()
{
Steinberg::IPluginFactory* factory = GetPluginFactory ();
if (factory == nullptr)
return;
IComponent* component = nullptr;
PClassInfo classInfo;
int32 classCount = factory->countClasses ();
for (int32 i = 0; i < classCount; i++)
{
if (factory->getClassInfo (i, &classInfo) != kResultTrue)
return;
if (strcmp (classInfo.category, kVstAudioEffectClass) == 0)
{
if (factory->createInstance (classInfo.cid, IComponent::iid, (void**)&component) !=
kResultTrue)
{
return;
}
break;
}
}
if (component)
{
if (component->initialize (InterAppAudioHostApp::instance ()->unknownCast ()) !=
kResultTrue)
{
component->release ();
return;
}
if (component->queryInterface (IEditController::iid, (void**)&editController) !=
kResultTrue)
{
TUID controllerCID {};
if (component->getControllerClassId (controllerCID) == kResultTrue)
{
if (factory->createInstance (controllerCID, IEditController::iid,
(void**)&editController) != kResultTrue)
return;
editController->setComponentHandler (this);
if (editController->initialize (
InterAppAudioHostApp::instance ()->unknownCast ()) != kResultTrue)
{
component->release ();
editController->release ();
editController = nullptr;
return;
}
auto compConnection = U::cast<IConnectionPoint> (component);
auto ctrlerConnection = U::cast<IConnectionPoint> (editController);
if (compConnection && ctrlerConnection)
{
compConnection->connect (ctrlerConnection);
ctrlerConnection->connect (compConnection);
}
}
else
{
component->release ();
return;
}
}
component->queryInterface (IAudioProcessor::iid, (void**)&processor);
if (processor == nullptr)
{
if (editController)
{
editController->release ();
editController = nullptr;
}
}
else
{
NSMutableData* data = [NSMutableData new];
NSMutableDataIBStream state (data);
if (component->getState (&state) == kResultTrue)
{
state.seek (0, IBStream::kIBSeekSet);
editController->setComponentState (&state);
}
int32 paramCount = editController->getParameterCount ();
inputParamChanges.setMaxParameters (paramCount);
inputParamChangeTransfer.setMaxParameters (paramCount);
outputParamChanges.setMaxParameters (paramCount);
outputParamChangeTransfer.setMaxParameters (paramCount);
midiControllerToParamIDMap = createMIDIControllerToParamIDMap ();
uiScheduledEvents.resize (kMaxUIEvents);
}
component->release ();
}
}
//------------------------------------------------------------------------
VST3Plugin::MIDIControllerToParamIDMap VST3Plugin::createMIDIControllerToParamIDMap ()
{
MIDIControllerToParamIDMap newMap;
auto midiMapping = U::cast<IMidiMapping> (editController);
if (midiMapping)
{
uint16 channelCount = 0;
auto component = U::cast<IComponent> (processor);
if (component)
{
int32 busCount = component->getBusCount (kEvent, kInput);
if (busCount > 0)
{
BusInfo busInfo;
if (component->getBusInfo (kEvent, kInput, 0, busInfo) == kResultTrue)
{
channelCount = busInfo.channelCount;
}
}
}
ParamID paramID;
for (int32 channel = 0; channel < channelCount; channel++)
{
for (CtrlNumber ctrler = 0; ctrler < kCountCtrlNumber; ctrler++)
{
if (midiMapping->getMidiControllerAssignment (0, channel, ctrler, paramID) ==
kResultTrue)
{
newMap.insert (
std::make_pair (channelAndCtrlNumber (channel, ctrler), paramID));
}
}
}
}
return newMap;
}
//------------------------------------------------------------------------
tresult VST3Plugin::scheduleEventFromUI (Event& event)
{
if (event.type == Event::kNoteOnEvent)
event.noteOn.noteId = lastNodeID++;
return uiScheduledEvents.push (event) ? kResultTrue : kResultFalse;
}
//------------------------------------------------------------------------
NSData* VST3Plugin::getProcessorState ()
{
if (processor)
{
NSMutableData* data = [NSMutableData new];
NSMutableDataIBStream state (data);
auto comp = U::cast<IComponent> (processor);
if (comp->getState (&state) == kResultTrue)
{
return data;
}
}
return nil;
}
//------------------------------------------------------------------------
bool VST3Plugin::setProcessorState (NSData* data)
{
if (editController && processor)
{
NSDataIBStream stream (data);
auto comp = U::cast<IComponent> (processor);
if (comp->setState (&stream) == kResultTrue)
{
stream.seek (0, IBStream::kIBSeekSet);
editController->setComponentState (&stream);
return true;
}
}
return false;
}
//------------------------------------------------------------------------
NSData* VST3Plugin::getControllerState ()
{
if (editController)
{
NSMutableData* data = [NSMutableData new];
NSMutableDataIBStream state (data);
if (editController->getState (&state) == kResultTrue)
{
return data;
}
}
return nil;
}
//------------------------------------------------------------------------
bool VST3Plugin::setControllerState (NSData* data)
{
if (editController)
{
NSDataIBStream stream (data);
if (editController->setState (&stream) == kResultTrue)
{
return true;
}
}
return false;
}
//------------------------------------------------------------------------
void VST3Plugin::willStartAudio (AudioIO* audioIO)
{
noteIDPitchMap.clear ();
lastNodeID.store (0);
ProcessSetup setup;
setup.processMode = kRealtime;
setup.symbolicSampleSize = kSample32;
setup.maxSamplesPerBlock = 4096; // TODO:
setup.sampleRate = audioIO->getSampleRate ();
processor->setupProcessing (setup);
SpeakerArrangement inputs[1];
SpeakerArrangement outputs[1];
inputs[0] = SpeakerArr::kStereo;
outputs[0] = SpeakerArr::kStereo;
processor->setBusArrangements (inputs, 1, outputs, 1);
auto comp = U::cast<IComponent> (processor);
comp->setActive (true);
processData.prepare (*comp, setup.maxSamplesPerBlock, setup.symbolicSampleSize);
auto iaaConnectionNotification = U::cast<IInterAppAudioConnectionNotification> (editController);
if (iaaConnectionNotification)
{
iaaConnectionNotification->onInterAppAudioConnectionStateChange (
audioIO->getInterAppAudioConnected () ? true : false);
}
timer = Timer::create (this, 16);
}
//------------------------------------------------------------------------
void VST3Plugin::didStopAudio (AudioIO* audioIO)
{
processor->setProcessing (false);
processing = false;
auto comp = U::cast<IComponent> (processor);
comp->setActive (false);
timer->release ();
timer = nullptr;
}
//------------------------------------------------------------------------
void VST3Plugin::onMIDIEvent (UInt32 inStatus, UInt32 data1, UInt32 data2, UInt32 sampleOffset,
bool withinRealtimeThread)
{
Event e = {};
e.flags = Event::kIsLive;
uint16 status = inStatus & 0xF0;
uint16 channel = inStatus & 0x0F;
if (status == 0x90 && data2 != 0) // note on
{
auto noteID = noteIDPitchMap.find ((channel << 8) + data1);
if (noteID != noteIDPitchMap.end ())
{
// for now, we just turn off the old note on
Event e2 = {};
e2.type = Event::kNoteOffEvent;
e2.noteOff.noteId = noteID->second;
e2.noteOff.channel = channel;
e2.noteOff.pitch = data1;
e2.noteOff.velocity = (float)data2 / 128.f;
e2.sampleOffset = 0;
inputEvents.addEvent (e2);
noteIDPitchMap.erase (noteID);
}
e.type = Event::kNoteOnEvent;
e.noteOn.channel = channel;
e.noteOn.pitch = data1;
e.noteOn.velocity = (float)data2 / 128.f;
e.noteOn.length = -1;
e.sampleOffset = sampleOffset;
if (withinRealtimeThread)
{
e.noteOn.noteId = lastNodeID++;
inputEvents.addEvent (e);
noteIDPitchMap.insert (std::make_pair ((channel << 8) + data1, e.noteOn.noteId));
}
else
{
scheduleEventFromUI (e);
}
}
else if (status == 0x80 || (status == 0x90 && data2 == 0)) // note off
{
auto noteID = noteIDPitchMap.find ((channel << 8) + data1);
if (noteID != noteIDPitchMap.end ())
{
e.type = Event::kNoteOffEvent;
e.noteOff.noteId = noteID->second;
e.noteOff.channel = channel;
e.noteOff.pitch = data1;
e.noteOff.velocity = (float)data2 / 128.f;
e.sampleOffset = sampleOffset;
if (withinRealtimeThread)
{
inputEvents.addEvent (e);
noteIDPitchMap.erase (noteID);
}
else
{
scheduleEventFromUI (e);
}
}
else
{
NSLog (@"NoteID not found:%d", (unsigned int)data1);
}
}
else if (status == 0xb0 && data1 < kAfterTouch) // controller
{
auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, data1));
if (it != midiControllerToParamIDMap.end ())
{
ParamValue value = (ParamValue)data2 / 128.;
if (withinRealtimeThread)
{
int32 index;
IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
else
{
inputParamChangeTransfer.addChange (it->second, value, sampleOffset);
}
}
}
else if (status == 0xe0) // pitch bend
{
auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kPitchBend));
if (it != midiControllerToParamIDMap.end ())
{
uint16 _14bit;
_14bit = (uint16)data2;
_14bit <<= 7;
_14bit |= (uint16)data1;
ParamValue value = (double)_14bit / (double)0x3fff;
if (withinRealtimeThread)
{
int32 index;
IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
else
{
inputParamChangeTransfer.addChange (it->second, value, sampleOffset);
}
}
}
else if (status == 0xd0) // aftertouch
{
auto it = midiControllerToParamIDMap.find (channelAndCtrlNumber (channel, kAfterTouch));
if (it != midiControllerToParamIDMap.end ())
{
ParamValue value = (ParamValue)data1 / 128.;
if (withinRealtimeThread)
{
int32 index;
IParamValueQueue* queue = inputParamChanges.addParameterData (it->second, index);
if (queue)
{
queue->addPoint (sampleOffset, value, index);
}
}
else
{
inputParamChangeTransfer.addChange (it->second, value, sampleOffset);
}
}
}
}
//------------------------------------------------------------------------
void VST3Plugin::process (const AudioTimeStamp* timeStamp, UInt32 busNumber, UInt32 numFrames,
AudioBufferList* ioData, bool& outputIsSilence, AudioIO* audioIO)
{
if (processing == false)
{
processor->setProcessing (true);
processing = true;
}
updateProcessContext (audioIO);
if (timeStamp)
processContext.systemTime = timeStamp->mHostTime;
// TODO: silence state update
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
{
processData.setChannelBuffer (kInput, 0, i, (float*)ioData->mBuffers[i].mData);
processData.setChannelBuffer (kOutput, 0, i, (float*)ioData->mBuffers[i].mData);
}
Event e;
while (uiScheduledEvents.pop (e))
{
inputEvents.addEvent (e);
if (e.type == Event::kNoteOnEvent)
{
auto noteID = noteIDPitchMap.find ((e.noteOn.channel << 8) + e.noteOn.pitch);
if (noteID == noteIDPitchMap.end ())
noteIDPitchMap.insert (
std::make_pair ((e.noteOn.channel << 8) + e.noteOn.pitch, e.noteOn.noteId));
}
}
processData.numSamples = numFrames;
inputParamChangeTransfer.transferChangesTo (inputParamChanges);
if (processor->process (processData) == kResultTrue)
{
inputParamChanges.clearQueue ();
outputParamChangeTransfer.transferChangesFrom (outputParamChanges);
outputParamChanges.clearQueue ();
inputEvents.clear ();
}
}
//------------------------------------------------------------------------
void VST3Plugin::updateProcessContext (AudioIO* audioIO)
{
memset (&processContext, 0, sizeof (ProcessContext));
processContext.sampleRate = audioIO->getSampleRate ();
Float64 beat = 0., tempo = 0.;
if (audioIO->getBeatAndTempo (beat, tempo))
{
processContext.state |=
ProcessContext::kTempoValid | ProcessContext::kProjectTimeMusicValid;
processContext.tempo = tempo;
processContext.projectTimeMusic = beat;
}
else
{
processContext.state |= ProcessContext::kTempoValid;
processContext.tempo = 120.;
}
UInt32 deltaSampleOffsetToNextBeat = 0;
Float32 timeSigNumerator = 0;
UInt32 timeSigDenominator = 0;
Float64 currentMeasureDownBeat = 0;
if (audioIO->getMusicalTimeLocation (deltaSampleOffsetToNextBeat, timeSigNumerator,
timeSigDenominator, currentMeasureDownBeat))
{
processContext.state |= ProcessContext::kTimeSigValid | ProcessContext::kBarPositionValid |
ProcessContext::kClockValid;
processContext.timeSigNumerator = timeSigNumerator;
processContext.timeSigDenominator = timeSigDenominator;
processContext.samplesToNextClock = deltaSampleOffsetToNextBeat;
processContext.barPositionMusic = currentMeasureDownBeat;
}
Boolean isPlaying;
Boolean isRecording;
Boolean transportStateChanged;
Float64 currentSampleInTimeLine;
Boolean isCycling;
Float64 cycleStartBeat;
Float64 cycleEndBeat;
if (audioIO->getTransportState (isPlaying, isRecording, transportStateChanged,
currentSampleInTimeLine, isCycling, cycleStartBeat,
cycleEndBeat))
{
processContext.state |= ProcessContext::kCycleValid;
processContext.cycleStartMusic = cycleStartBeat;
processContext.cycleEndMusic = cycleEndBeat;
processContext.projectTimeSamples = currentSampleInTimeLine;
if (isPlaying)
processContext.state |= ProcessContext::kPlaying;
if (isCycling)
processContext.state |= ProcessContext::kCycleActive;
if (isRecording)
processContext.state |= ProcessContext::kRecording;
}
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::beginEdit (ParamID id)
{
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::performEdit (ParamID id, ParamValue valueNormalized)
{
inputParamChangeTransfer.addChange (id, valueNormalized, 0);
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::endEdit (ParamID id)
{
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API VST3Plugin::restartComponent (int32 flags)
{
tresult result = kNotImplemented;
return result;
}
//------------------------------------------------------------------------
void VST3Plugin::onTimer (Timer* timer)
{
ParamID paramID;
ParamValue paramValue;
int32 sampleOffset;
while (outputParamChangeTransfer.getNextChange (paramID, paramValue, sampleOffset))
{
editController->setParamNormalized (paramID, paramValue);
}
UpdateHandler::instance ()->triggerDeferedUpdates ();
}
}
}
}
@@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.h
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import <UIKit/UIKit.h>
//------------------------------------------------------------------------
/** Base UIApplicationDelegate class.
* This class provides the base handling of the audio engine, plug-in and plug-in editor\n
* You should subclass it for customization\n
* Make sure to call the methods of this class if you override one in your subclass !
*/
//------------------------------------------------------------------------
@interface VSTInterAppAudioAppDelegateBase : UIResponder <UIApplicationDelegate>
//------------------------------------------------------------------------
@property (strong, nonatomic) UIWindow* window;
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
- (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder;
- (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder;
- (void)applicationDidBecomeActive:(UIApplication*)application;
- (void)applicationWillResignActive:(UIApplication*)application;
@end
@@ -0,0 +1,193 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/interappaudio/VSTInterAppAudioAppDelegateBase.mm
// Created by : Steinberg, 08/2013.
// Description : VST 3 InterAppAudio
// Flags : clang-format SMTGSequencer
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#import "VSTInterAppAudioAppDelegateBase.h"
#import "public.sdk/source/vst/interappaudio/AudioIO.h"
#import "public.sdk/source/vst/interappaudio/HostApp.h"
#import "public.sdk/source/vst/interappaudio/MidiIO.h"
#import "public.sdk/source/vst/interappaudio/VST3Editor.h"
#import "public.sdk/source/vst/interappaudio/VST3Plugin.h"
using namespace Steinberg::Vst::InterAppAudio;
//------------------------------------------------------------------------
static OSType fourCharCodeToOSType (NSString* inCode)
{
OSType rval = 0;
NSData* data = [inCode dataUsingEncoding:NSMacOSRomanStringEncoding];
[data getBytes:&rval length:sizeof (rval)];
HTONL (rval);
return rval;
}
//------------------------------------------------------------------------
@interface VSTInterAppAudioAppDelegateBase ()
//------------------------------------------------------------------------
{
VST3Plugin plugin;
VST3Editor editor;
BOOL audioIOInitialized;
}
@end
//------------------------------------------------------------------------
@implementation VSTInterAppAudioAppDelegateBase
//------------------------------------------------------------------------
//------------------------------------------------------------------------
- (BOOL)initAudioIO
{
id auArray = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AudioComponents"];
if (auArray)
{
id desc = [auArray objectAtIndex:0];
if (desc)
{
NSString* typeStr = [desc objectForKey:@"type"];
NSString* subtypeStr = [desc objectForKey:@"subtype"];
NSString* manufacturerStr = [desc objectForKey:@"manufacturer"];
NSString* nameStr = [desc objectForKey:@"name"];
if (typeStr && subtypeStr && manufacturerStr && nameStr)
{
OSType type = fourCharCodeToOSType (typeStr);
OSType subtype = fourCharCodeToOSType (subtypeStr);
OSType manufacturer = fourCharCodeToOSType (manufacturerStr);
AudioIO* audioIO = AudioIO::instance ();
if (audioIO->init (type, subtype, manufacturer, (__bridge CFStringRef)nameStr) ==
Steinberg::kResultTrue)
{
if (plugin.init ())
{
InterAppAudioHostApp::instance ()->setPlugin (&plugin);
audioIO->addProcessor (&plugin);
audioIOInitialized = YES;
return YES;
}
}
}
}
}
return NO;
}
//------------------------------------------------------------------------
- (BOOL)createUI
{
if (audioIOInitialized)
{
[UIApplication sharedApplication].statusBarHidden = YES;
self.window = [UIWindow new];
self.window.backgroundColor = [UIColor redColor];
CGRect screenSize = self.window.bounds;
if (editor.init (screenSize))
{
self.window.rootViewController = editor.getViewController ();
[self.window makeKeyAndVisible];
if (editor.attach (plugin.getEditController ()) == false)
{
return NO;
}
}
return YES;
}
return NO;
}
//------------------------------------------------------------------------
- (void)savePluginState:(NSCoder*)coder
{
NSData* processorState = plugin.getProcessorState ();
NSData* controllerState = plugin.getControllerState ();
if (processorState)
[coder encodeObject:processorState forKey:@"VST3ProcessorState"];
if (controllerState)
[coder encodeObject:controllerState forKey:@"VST3ControllerState"];
}
//------------------------------------------------------------------------
- (void)restorePluginState:(NSCoder*)coder
{
NSData* processorState = [coder decodeObjectForKey:@"VST3ProcessorState"];
if (processorState)
{
plugin.setProcessorState (processorState);
}
NSData* controllerState = [coder decodeObjectForKey:@"VST3ControllerState"];
if (controllerState)
{
plugin.setControllerState (controllerState);
}
}
//------------------------------------------------------------------------
// UIApplicationDelegate methods
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
return [self initAudioIO];
}
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
BOOL result = [self createUI];
if (result)
{
AudioIO::instance ()->start ();
}
return result;
}
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application shouldSaveApplicationState:(NSCoder*)coder
{
[self savePluginState:coder];
[coder encodeBool:MidiIO::instance ().isEnabled () forKey:@"MIDI Enabled"];
return YES;
}
//------------------------------------------------------------------------
- (BOOL)application:(UIApplication*)application shouldRestoreApplicationState:(NSCoder*)coder
{
[self restorePluginState:coder];
BOOL midiEnabled = [coder decodeBoolForKey:@"MIDI Enabled"];
MidiIO::instance ().setEnabled (midiEnabled);
return YES;
}
//------------------------------------------------------------------------
- (void)applicationDidBecomeActive:(UIApplication*)application
{
AudioIO* audioIO = AudioIO::instance ();
audioIO->start ();
}
//------------------------------------------------------------------------
- (void)applicationWillResignActive:(UIApplication*)application
{
AudioIO* audioIO = AudioIO::instance ();
if (audioIO->getInterAppAudioConnected () == false && MidiIO::instance ().isEnabled () == false)
{
audioIO->stop ();
}
}
@end
@@ -0,0 +1,43 @@
# ModuleInfoLib
This is a c++17 library to parse and create the Steinberg moduleinfo.json files.
## Parsing
To parse a moduleinfo.json file you need to include the following files to your project:
* moduleinfoparser.cpp
* moduleinfoparser.h
* moduleinfo.h
* json.h
* jsoncxx.h
And add a header search path to the root folder of the VST SDK.
Now to parse a moduleinfo.json file in code you need to read the moduleinfo.json into a memory buffer and call
``` c++
auto moduleInfo = ModuleInfoLib::parseCompatibilityJson (std::string_view (buffer, bufferSize), &std::cerr);
```
Afterwards if parsing succeeded the moduleInfo optional has a value containing the ModuleInfo.
## Creating
The VST3 SDK contains the moduleinfotool utility that can create moduleinfo.json files from VST3 modules.
To add this capability to your own project you need to link to the sdk_hosting library from the SDK and include the following files to your project:
* moduleinfocreator.cpp
* moduleinfocreator.h
* moduleinfo.h
Additionally you need to add the module platform implementation from the hosting directory (module_win32.cpp, module_mac.mm or module_linux.cpp).
Now you can use the two methods in moduleinfocreator.h to create a moduleinfo.json file:
``` c++
auto moduleInfo = ModuleInfoLib::createModuleInfo (module, false);
ModuleInfoLib::outputJson (moduleInfo, std::cout);
```
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,407 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category :
// Filename : public.sdk/source/vst/moduleinfo/jsoncxx.h
// Created by : Steinberg, 12/2021
// Description :
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "json.h"
#include <cassert>
#include <cstdlib>
#include <optional>
#include <string>
#include <string_view>
#include <variant>
#if defined(_MSC_VER) || __has_include(<charconv>)
#include <charconv>
#define SMTG_HAS_CHARCONV
#endif
//------------------------------------------------------------------------
namespace JSON {
namespace Detail {
//------------------------------------------------------------------------
template <typename JsonT>
struct Base
{
explicit Base (JsonT* o) : object_ (o) {}
explicit Base (const Base& o) : object_ (o.object_) {}
Base& operator= (const Base& o) = default;
operator JsonT* () const { return object_; }
JsonT* jsonValue () const { return object_; }
protected:
Base () : object_ (nullptr) {}
JsonT* object_;
};
//------------------------------------------------------------------------
template <typename JsonElement>
struct Iterator
{
explicit Iterator (JsonElement el) : el (el) {}
bool operator== (const Iterator& other) const { return other.el == el; }
bool operator!= (const Iterator& other) const { return other.el != el; }
const JsonElement& operator* () const { return el; }
const JsonElement& operator-> () const { return el; }
Iterator& operator++ ()
{
if (el)
el = el.next ();
return *this;
}
Iterator operator++ (int)
{
auto it = Iterator (el);
operator++ ();
return it;
}
private:
JsonElement el;
};
//------------------------------------------------------------------------
} // Detail
struct Object;
struct Array;
struct String;
struct Number;
struct Boolean;
//------------------------------------------------------------------------
enum class Type
{
Object,
Array,
String,
Number,
True,
False,
Null,
};
//------------------------------------------------------------------------
struct SourceLocation
{
size_t offset;
size_t line;
size_t row;
};
//------------------------------------------------------------------------
struct Value : Detail::Base<json_value_s>
{
using Detail::Base<json_value_s>::Base;
using VariantT = std::variant<Object, Array, String, Number, Boolean, std::nullptr_t>;
std::optional<Object> asObject () const;
std::optional<Array> asArray () const;
std::optional<String> asString () const;
std::optional<Number> asNumber () const;
std::optional<Boolean> asBoolean () const;
std::optional<std::nullptr_t> asNull () const;
VariantT asVariant () const;
Type type () const;
SourceLocation getSourceLocation () const;
};
//------------------------------------------------------------------------
struct Boolean
{
Boolean (size_t type) : value (type == json_type_true) {}
operator bool () const { return value; }
private:
bool value;
};
//------------------------------------------------------------------------
struct String : Detail::Base<json_string_s>
{
using Detail::Base<json_string_s>::Base;
std::string_view text () const { return {jsonValue ()->string, jsonValue ()->string_size}; }
SourceLocation getSourceLocation () const;
};
//------------------------------------------------------------------------
struct Number : Detail::Base<json_number_s>
{
using Detail::Base<json_number_s>::Base;
std::string_view text () const { return {jsonValue ()->number, jsonValue ()->number_size}; }
std::optional<int64_t> getInteger () const;
std::optional<double> getDouble () const;
};
//------------------------------------------------------------------------
struct ObjectElement : Detail::Base<json_object_element_s>
{
using Detail::Base<json_object_element_s>::Base;
String name () const { return String (jsonValue ()->name); }
Value value () const { return Value (jsonValue ()->value); }
ObjectElement next () const { return ObjectElement (jsonValue ()->next); }
};
//------------------------------------------------------------------------
struct Object : Detail::Base<json_object_s>
{
using Detail::Base<json_object_s>::Base;
using Iterator = Detail::Iterator<ObjectElement>;
size_t size () const { return jsonValue ()->length; }
Iterator begin () const { return Iterator (ObjectElement (jsonValue ()->start)); }
Iterator end () const { return Iterator (ObjectElement (nullptr)); }
};
//------------------------------------------------------------------------
struct ArrayElement : Detail::Base<json_array_element_s>
{
using Detail::Base<json_array_element_s>::Base;
Value value () const { return Value (jsonValue ()->value); }
ArrayElement next () const { return ArrayElement (jsonValue ()->next); }
};
//------------------------------------------------------------------------
struct Array : Detail::Base<json_array_s>
{
using Detail::Base<json_array_s>::Base;
using Iterator = Detail::Iterator<ArrayElement>;
size_t size () const { return jsonValue ()->length; }
Iterator begin () const { return Iterator (ArrayElement (jsonValue ()->start)); }
Iterator end () const { return Iterator (ArrayElement (nullptr)); }
};
//------------------------------------------------------------------------
struct Document : Value
{
static std::variant<Document, json_parse_result_s> parse (std::string_view data)
{
auto allocate = [] (void*, size_t allocSize) { return std::malloc (allocSize); };
json_parse_result_s parse_result {};
auto value = json_parse_ex (data.data (), data.size (),
json_parse_flags_allow_json5 |
json_parse_flags_allow_location_information,
allocate, nullptr, &parse_result);
if (value)
return Document (value);
return parse_result;
}
~Document () noexcept
{
if (object_)
std::free (object_);
}
Document (Document&& doc) noexcept { *this = std::move (doc); }
Document& operator= (Document&& doc) noexcept
{
std::swap (object_, doc.object_);
return *this;
}
private:
using Value::Value;
};
//------------------------------------------------------------------------
inline std::optional<Object> Value::asObject () const
{
if (type () != Type::Object)
return {};
return Object (json_value_as_object (jsonValue ()));
}
//------------------------------------------------------------------------
inline std::optional<Array> Value::asArray () const
{
if (type () != Type::Array)
return {};
return Array (json_value_as_array (jsonValue ()));
}
//------------------------------------------------------------------------
inline std::optional<String> Value::asString () const
{
if (type () != Type::String)
return {};
return String (json_value_as_string (jsonValue ()));
}
//------------------------------------------------------------------------
inline std::optional<Number> Value::asNumber () const
{
if (type () != Type::Number)
return {};
return Number (json_value_as_number (jsonValue ()));
}
//------------------------------------------------------------------------
inline std::optional<Boolean> Value::asBoolean () const
{
if (type () == Type::True || type () == Type::False)
return Boolean (jsonValue ()->type);
return {};
}
//------------------------------------------------------------------------
inline std::optional<std::nullptr_t> Value::asNull () const
{
if (type () != Type::Null)
return {};
return nullptr;
}
//------------------------------------------------------------------------
inline Type Value::type () const
{
switch (jsonValue ()->type)
{
case json_type_string: return Type::String;
case json_type_number: return Type::Number;
case json_type_object: return Type::Object;
case json_type_array: return Type::Array;
case json_type_true: return Type::True;
case json_type_false: return Type::False;
case json_type_null: return Type::Null;
}
assert (false);
return Type::Null;
}
//------------------------------------------------------------------------
inline Value::VariantT Value::asVariant () const
{
switch (type ())
{
case Type::String: return *asString ();
case Type::Number: return *asNumber ();
case Type::Object: return *asObject ();
case Type::Array: return *asArray ();
case Type::True: return *asBoolean ();
case Type::False: return *asBoolean ();
case Type::Null: return *asNull ();
}
assert (false);
return nullptr;
}
//------------------------------------------------------------------------
inline SourceLocation Value::getSourceLocation () const
{
auto exValue = reinterpret_cast<json_value_ex_s*> (jsonValue ());
return {exValue->offset, exValue->line_no, exValue->row_no};
}
//------------------------------------------------------------------------
inline SourceLocation String::getSourceLocation () const
{
auto exValue = reinterpret_cast<json_string_ex_s*> (jsonValue ());
return {exValue->offset, exValue->line_no, exValue->row_no};
}
//------------------------------------------------------------------------
inline std::optional<int64_t> Number::getInteger () const
{
#if defined(SMTG_HAS_CHARCONV)
int64_t result {0};
auto res = std::from_chars (jsonValue ()->number,
jsonValue ()->number + jsonValue ()->number_size, result);
if (res.ec == std::errc ())
return result;
return {};
#else
int64_t result {0};
std::string str (jsonValue ()->number, jsonValue ()->number + jsonValue ()->number_size);
if (std::sscanf (str.data (), "%lld", &result) != 1)
return {};
return result;
#endif
}
//------------------------------------------------------------------------
inline std::optional<double> Number::getDouble () const
{
#if 1 // clang still has no floting point from_chars version
size_t ctrl {0};
auto result = std::stod (std::string (jsonValue ()->number, jsonValue ()->number_size), &ctrl);
if (ctrl > 0)
return result;
#else
double result {0.};
auto res = std::from_chars (jsonValue ()->number,
jsonValue ()->number + jsonValue ()->number_size, result);
if (res.ec == std::errc ())
return result;
#endif
return {};
}
//------------------------------------------------------------------------
inline std::string_view errorToString (json_parse_error_e error)
{
switch (error)
{
case json_parse_error_e::json_parse_error_none: return {};
case json_parse_error_e::json_parse_error_expected_comma_or_closing_bracket:
return "json_parse_error_expected_comma_or_closing_bracket";
case json_parse_error_e::json_parse_error_expected_colon:
return "json_parse_error_expected_colon";
case json_parse_error_e::json_parse_error_expected_opening_quote:
return "json_parse_error_expected_opening_quote";
case json_parse_error_e::json_parse_error_invalid_string_escape_sequence:
return "json_parse_error_invalid_string_escape_sequence";
case json_parse_error_e::json_parse_error_invalid_number_format:
return "json_parse_error_invalid_number_format";
case json_parse_error_e::json_parse_error_invalid_value:
return "json_parse_error_invalid_value";
case json_parse_error_e::json_parse_error_premature_end_of_buffer:
return "json_parse_error_premature_end_of_buffer";
case json_parse_error_e::json_parse_error_invalid_string:
return "json_parse_error_invalid_string";
case json_parse_error_e::json_parse_error_allocator_failed:
return "json_parse_error_allocator_failed";
case json_parse_error_e::json_parse_error_unexpected_trailing_characters:
return "json_parse_error_unexpected_trailing_characters";
case json_parse_error_e::json_parse_error_unknown: return "json_parse_error_unknown";
}
return {};
}
//------------------------------------------------------------------------
} // JSON
@@ -0,0 +1,80 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfo.h
// Created by : Steinberg, 12/2021
// Description :
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include <cstdint>
#include <string>
#include <vector>
//------------------------------------------------------------------------
namespace Steinberg {
//------------------------------------------------------------------------
struct ModuleInfo
{
//------------------------------------------------------------------------
struct FactoryInfo
{
std::string vendor;
std::string url;
std::string email;
int32_t flags {0};
};
//------------------------------------------------------------------------
struct Snapshot
{
double scaleFactor {1.};
std::string path;
};
using SnapshotList = std::vector<Snapshot>;
//------------------------------------------------------------------------
struct ClassInfo
{
std::string cid;
std::string category;
std::string name;
std::string vendor;
std::string version;
std::string sdkVersion;
std::vector<std::string> subCategories;
SnapshotList snapshots;
int32_t cardinality {0x7FFFFFFF};
uint32_t flags {0};
};
//------------------------------------------------------------------------
struct Compatibility
{
std::string newCID;
std::vector<std::string> oldCID;
};
using ClassList = std::vector<ClassInfo>;
using CompatibilityList = std::vector<Compatibility>;
std::string name;
std::string version;
FactoryInfo factoryInfo;
ClassList classes;
CompatibilityList compatibility;
};
//------------------------------------------------------------------------
} // Steinberg
@@ -0,0 +1,289 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
// Flags : clang-format SMTGSequencer
//
// Category : moduleinfo
// Filename : public.sdk/source/vst/moduleinfo/moduleinfocreator.cpp
// Created by : Steinberg, 12/2021
// Description : utility functions to create moduleinfo json files
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "moduleinfocreator.h"
#include "jsoncxx.h"
#include <algorithm>
#include <stdexcept>
#include <string>
//------------------------------------------------------------------------
namespace Steinberg::ModuleInfoLib {
using namespace VST3;
namespace {
//------------------------------------------------------------------------
struct JSON5Writer
{
private:
std::ostream& stream;
bool beautify;
bool lastIsComma {false};
int32_t intend {0};
void doBeautify ()
{
if (beautify)
{
stream << '\n';
for (int i = 0; i < intend; ++i)
stream << " ";
}
}
void writeComma ()
{
if (lastIsComma)
return;
stream << ",";
lastIsComma = true;
}
void startObject ()
{
stream << "{";
++intend;
lastIsComma = false;
}
void endObject ()
{
--intend;
doBeautify ();
stream << "}";
lastIsComma = false;
}
void startArray ()
{
stream << "[";
++intend;
lastIsComma = false;
}
void endArray ()
{
--intend;
doBeautify ();
stream << "]";
lastIsComma = false;
}
public:
JSON5Writer (std::ostream& stream, bool beautify = true) : stream (stream), beautify (beautify)
{
}
void string (std::string_view str)
{
stream << "\"" << str << "\"";
lastIsComma = false;
}
void boolean (bool val)
{
stream << (val ? "true" : "false");
lastIsComma = false;
}
template <typename ValueT>
void value (ValueT val)
{
stream << val;
lastIsComma = false;
}
template <typename Proc>
void object (Proc proc)
{
startObject ();
proc ();
endObject ();
}
template <typename Iterator, typename Proc>
void array (Iterator begin, Iterator end, Proc proc)
{
startArray ();
while (begin != end)
{
doBeautify ();
proc (begin);
++begin;
writeComma ();
}
endArray ();
}
template <typename Proc>
void keyValue (std::string_view key, Proc proc)
{
doBeautify ();
string (key);
stream << ": ";
proc ();
writeComma ();
}
};
//------------------------------------------------------------------------
void writeSnapshots (const ModuleInfo::SnapshotList& snapshots, JSON5Writer& w)
{
w.keyValue ("Snapshots", [&] () {
w.array (snapshots.begin (), snapshots.end (), [&] (const auto& el) {
w.object ([&] () {
w.keyValue ("Scale Factor", [&] () { w.value (el->scaleFactor); });
w.keyValue ("Path", [&] () { w.string (el->path); });
});
});
});
}
//------------------------------------------------------------------------
void writeClassInfo (const ModuleInfo::ClassInfo& cls, JSON5Writer& w)
{
w.keyValue ("CID", [&] () { w.string (cls.cid); });
w.keyValue ("Category", [&] () { w.string (cls.category); });
w.keyValue ("Name", [&] () { w.string (cls.name); });
w.keyValue ("Vendor", [&] () { w.string (cls.vendor); });
w.keyValue ("Version", [&] () { w.string (cls.version); });
w.keyValue ("SDKVersion", [&] () { w.string (cls.sdkVersion); });
const auto& sc = cls.subCategories;
if (!sc.empty ())
{
w.keyValue ("Sub Categories", [&] () {
w.array (sc.begin (), sc.end (), [&] (const auto& cat) { w.string (*cat); });
});
}
w.keyValue ("Class Flags", [&] () { w.value (cls.flags); });
w.keyValue ("Cardinality", [&] () { w.value (cls.cardinality); });
writeSnapshots (cls.snapshots, w);
}
//------------------------------------------------------------------------
void writePluginCompatibility (const ModuleInfo::CompatibilityList& compat, JSON5Writer& w)
{
if (compat.empty ())
return;
w.keyValue ("Compatibility", [&] () {
w.array (compat.begin (), compat.end (), [&] (auto& el) {
w.object ([&] () {
w.keyValue ("New", [&] () { w.string (el->newCID); });
w.keyValue ("Old", [&] () {
w.array (el->oldCID.begin (), el->oldCID.end (),
[&] (auto& oldEl) { w.string (*oldEl); });
});
});
});
});
}
//------------------------------------------------------------------------
void writeFactoryInfo (const ModuleInfo::FactoryInfo& fi, JSON5Writer& w)
{
w.keyValue ("Factory Info", [&] () {
w.object ([&] () {
w.keyValue ("Vendor", [&] () { w.string (fi.vendor); });
w.keyValue ("URL", [&] () { w.string (fi.url); });
w.keyValue ("E-Mail", [&] () { w.string (fi.email); });
w.keyValue ("Flags", [&] () {
w.object ([&] () {
w.keyValue ("Unicode",
[&] () { w.boolean (fi.flags & PFactoryInfo::kUnicode); });
w.keyValue ("Classes Discardable", [&] () {
w.boolean (fi.flags & PFactoryInfo::kClassesDiscardable);
});
w.keyValue ("Component Non Discardable", [&] () {
w.boolean (fi.flags & PFactoryInfo::kComponentNonDiscardable);
});
});
});
});
});
}
//------------------------------------------------------------------------
} // anonymous
//------------------------------------------------------------------------
ModuleInfo createModuleInfo (const VST3::Hosting::Module& module, bool includeDiscardableClasses)
{
ModuleInfo info;
const auto& factory = module.getFactory ();
auto factoryInfo = factory.info ();
info.name = module.getName ();
auto pos = info.name.find_last_of ('.');
if (pos != std::string::npos)
info.name.erase (pos);
info.factoryInfo.vendor = factoryInfo.vendor ();
info.factoryInfo.url = factoryInfo.url ();
info.factoryInfo.email = factoryInfo.email ();
info.factoryInfo.flags = factoryInfo.flags ();
if (factoryInfo.classesDiscardable () == false ||
(factoryInfo.classesDiscardable () && includeDiscardableClasses))
{
auto snapshots = VST3::Hosting::Module::getSnapshots (module.getPath ());
for (const auto& ci : factory.classInfos ())
{
ModuleInfo::ClassInfo classInfo;
classInfo.cid = ci.ID ().toString ();
classInfo.category = ci.category ();
classInfo.name = ci.name ();
classInfo.vendor = ci.vendor ();
classInfo.version = ci.version ();
classInfo.sdkVersion = ci.sdkVersion ();
classInfo.subCategories = ci.subCategories ();
classInfo.cardinality = ci.cardinality ();
classInfo.flags = ci.classFlags ();
auto snapshotIt = std::find_if (snapshots.begin (), snapshots.end (),
[&] (const auto& el) { return el.uid == ci.ID (); });
if (snapshotIt != snapshots.end ())
{
for (auto& s : snapshotIt->images)
{
std::string_view path (s.path);
if (path.find (module.getPath ()) == 0)
path.remove_prefix (module.getPath ().size () + 1);
classInfo.snapshots.emplace_back (
ModuleInfo::Snapshot {s.scaleFactor, {path.data (), path.size ()}});
}
snapshots.erase (snapshotIt);
}
info.classes.emplace_back (std::move (classInfo));
}
}
return info;
}
//------------------------------------------------------------------------
void outputJson (const ModuleInfo& info, std::ostream& output)
{
JSON5Writer w (output);
w.object ([&] () {
w.keyValue ("Name", [&] () { w.string (info.name); });
w.keyValue ("Version", [&] () { w.string (info.version); });
writeFactoryInfo (info.factoryInfo, w);
writePluginCompatibility (info.compatibility, w);
w.keyValue ("Classes", [&] () {
w.array (info.classes.begin (), info.classes.end (),
[&] (const auto& cls) { w.object ([&] () { writeClassInfo (*cls, w); }); });
});
});
}
//------------------------------------------------------------------------
} // Steinberg::ModuleInfoLib

Some files were not shown because too many files have changed in this diff Show More