Files
2026-08-16 18:24:52 +07:00

1470 lines
40 KiB
C++

//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Helpers
// Filename : public.sdk/source/vst/basewrapper/basewrapper.cpp
// Created by : Steinberg, 01/2009
// 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.
//-----------------------------------------------------------------------------
/// \cond ignore
#include "public.sdk/source/vst/basewrapper/basewrapper.h"
#include "public.sdk/source/vst/hosting/connectionproxy.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/base/futils.h"
#include "pluginterfaces/base/keycodes.h"
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/vst/ivstmessage.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "pluginterfaces/vst/vstpresetkeys.h"
#include "base/source/fstreamer.h"
#include <cstdio>
#include <cstdlib>
#include <limits>
#if SMTG_OS_MACOS
#include <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#endif
extern "C" {
#if SMTG_OS_MACOS
// implemented in macmain.cpp
SMTG_EXPORT_SYMBOL bool bundleEntry (CFBundleRef);
SMTG_EXPORT_SYMBOL bool bundleExit (void);
#elif SMTG_OS_WINDOWS
// implemented in dllmain.cpp
SMTG_EXPORT_SYMBOL bool InitDll ();
SMTG_EXPORT_SYMBOL bool ExitDll ();
#else
#error platform not supported!
#endif
}
#if SMTG_OS_MACOS
//------------------------------------------------------------------------
static CFBundleRef GetBundleFromExecutable (const char* filepath)
{
// AutoreleasePool ap;
char* fname = strdup (filepath);
size_t pos = strlen (fname);
int level = 3;
while (level > 0 && --pos >= 0)
{
if (fname[pos] == '/')
level--;
}
if (level > 0)
return 0;
fname[pos] = 0;
CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*)fname, pos, true);
CFBundleRef bundle = CFBundleCreate (0, url);
return bundle;
}
//------------------------------------------------------------------------
static CFBundleRef GetCurrentBundle ()
{
Dl_info info;
if (dladdr ((const void*)GetCurrentBundle, &info))
{
if (info.dli_fname)
{
return GetBundleFromExecutable (info.dli_fname);
}
}
return 0;
}
#endif // SMTG_OS_MACOS
//------------------------------------------------------------------------
bool _InitModule ()
{
#if SMTG_OS_MACOS
return bundleEntry (GetCurrentBundle ());
#else
return InitDll ();
#endif
}
//------------------------------------------------------------------------
bool _DeinitModule ()
{
#if SMTG_OS_MACOS
return bundleExit ();
#else
return ExitDll ();
#endif
}
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// some Globals
//------------------------------------------------------------------------
// In order to speed up hasEditor function gPluginHasEditor can be set in EditController::initialize
enum
{
kDontKnow = -1,
kNoEditor = 0,
kEditor,
};
// default: kDontKnow which uses createView to find out
using EditorAvailability = int32;
EditorAvailability gPluginHasEditor = kDontKnow;
// Set to 'true' in EditController::initialize
// default: VST 3 kIsProgramChange parameter will not be exported
bool gExportProgramChangeParameters = false;
//------------------------------------------------------------------------
BaseEditorWrapper::BaseEditorWrapper (IEditController* controller)
: mController (controller)
{
}
//------------------------------------------------------------------------
BaseEditorWrapper::~BaseEditorWrapper ()
{
if (mView)
_close ();
mController = nullptr;
}
//------------------------------------------------------------------------
tresult PLUGIN_API BaseEditorWrapper::queryInterface (const char* _iid, void** obj)
{
QUERY_INTERFACE (_iid, obj, FUnknown::iid, IPlugFrame)
QUERY_INTERFACE (_iid, obj, IPlugFrame::iid, IPlugFrame)
*obj = nullptr;
return kNoInterface;
}
//------------------------------------------------------------------------
tresult PLUGIN_API BaseEditorWrapper::resizeView (IPlugView* view, ViewRect* newSize)
{
tresult result = kResultFalse;
if (view && newSize)
{
result = view->onSize (newSize);
}
return result;
}
//------------------------------------------------------------------------
bool BaseEditorWrapper::hasEditor (IEditController* controller)
{
/* Some plug-ins might have large GUIs. In order to speed up hasEditor function while
* initializing the plug-in gPluginHasEditor can be set in EditController::initialize
* beforehand. */
bool result = false;
if (gPluginHasEditor == kEditor)
{
result = true;
}
else if (gPluginHasEditor == kNoEditor)
{
result = false;
}
else
{
IPlugView* view = controller ? controller->createView (ViewType::kEditor) : nullptr;
FReleaser viewRel (view);
result = (view != nullptr);
}
return result;
}
//------------------------------------------------------------------------
void BaseEditorWrapper::createView ()
{
if (mView == nullptr && mController != nullptr)
{
mView = owned (mController->createView (ViewType::kEditor));
mView->setFrame (this);
#if SMTG_OS_MACOS
#if SMTG_PLATFORM_64
if (mView && mView->isPlatformTypeSupported (kPlatformTypeNSView) != kResultTrue)
#else
if (mView && mView->isPlatformTypeSupported (kPlatformTypeHIView) != kResultTrue)
#endif
{
mView = nullptr;
mController = nullptr;
}
#endif
}
}
//------------------------------------------------------------------------
bool BaseEditorWrapper::getRect (ViewRect& rect)
{
createView ();
if (!mView)
return false;
if (mView->getSize (&rect) == kResultTrue)
{
if ((rect.bottom - rect.top) > 0 && (rect.right - rect.left) > 0)
{
mViewRect = rect;
return true;
}
}
return false;
}
//------------------------------------------------------------------------
bool BaseEditorWrapper::_open (void* ptr)
{
createView ();
if (mView)
{
#if SMTG_OS_WINDOWS
FIDString type = kPlatformTypeHWND;
#elif SMTG_OS_MACOS && SMTG_PLATFORM_64
FIDString type = kPlatformTypeNSView;
#elif SMTG_OS_MACOS
FIDString type = kPlatformTypeHIView;
#endif
return mView->attached (ptr, type) == kResultTrue;
}
return false;
}
//------------------------------------------------------------------------
void BaseEditorWrapper::_close ()
{
if (mView)
{
mView->setFrame (nullptr);
mView->removed ();
mView = nullptr;
}
}
//------------------------------------------------------------------------
bool BaseEditorWrapper::_setKnobMode (Vst::KnobMode val)
{
bool result = false;
auto editController2 = U::cast<IEditController2> (mController);
if (editController2)
result = editController2->setKnobMode (val) == kResultTrue;
return result;
}
//------------------------------------------------------------------------
// MemoryStream with attributes to add information "preset or project"
//------------------------------------------------------------------------
class VstPresetStream : public MemoryStream, Vst::IStreamAttributes
{
public:
VstPresetStream () {}
VstPresetStream (void* memory, TSize memorySize) : MemoryStream (memory, memorySize) {}
//---from Vst::IStreamAttributes-----
tresult PLUGIN_API getFileName (String128 /*name*/) SMTG_OVERRIDE { return kNotImplemented; }
IAttributeList* PLUGIN_API getAttributes () SMTG_OVERRIDE { return attrList; }
//------------------------------------------------------------------------
DELEGATE_REFCOUNT (MemoryStream)
tresult PLUGIN_API queryInterface (const TUID iid, void** obj) SMTG_OVERRIDE
{
QUERY_INTERFACE (iid, obj, IStreamAttributes::iid, IStreamAttributes)
return MemoryStream::queryInterface (iid, obj);
}
protected:
IPtr<IAttributeList> attrList {HostAttributeList::make ()};
};
//------------------------------------------------------------------------
// BaseWrapper
//------------------------------------------------------------------------
BaseWrapper::BaseWrapper (SVST3Config& config)
{
mPlugInterfaceSupport = owned (NEW PlugInterfaceSupport);
mProcessor = owned (config.processor);
mController = owned (config.controller);
mFactory = config.factory; // share it
mVst3EffectClassID = config.vst3ComponentID;
memset (mName, 0, sizeof (mName));
memset (mVendor, 0, sizeof (mVendor));
memset (mSubCategories, 0, sizeof (mSubCategories));
memset (&mProcessContext, 0, sizeof (ProcessContext));
mProcessContext.sampleRate = 44100;
mProcessContext.tempo = 120;
for (int32 b = 0; b < kMaxMidiMappingBusses; b++)
{
for (int32 i = 0; i < 16; i++)
{
mMidiCCMapping[b][i] = nullptr;
}
}
for (int32 i = 0; i < kMaxProgramChangeParameters; i++)
{
mProgramChangeParameterIDs[i] = kNoParamId;
mProgramChangeParameterIdxs[i] = -1;
}
}
//------------------------------------------------------------------------
BaseWrapper::~BaseWrapper ()
{
term ();
}
//------------------------------------------------------------------------
void BaseWrapper::term ()
{
mTimer = nullptr;
mProcessData.unprepare ();
//---Disconnect components
if (mComponentsConnected)
{
auto cp1 = U::cast<IConnectionPoint> (mProcessor);
auto cp2 = U::cast<IConnectionPoint> (mController);
if (cp1 && cp2)
{
if (mProcessorConnection)
mProcessorConnection->disconnect (cp2);
if (mControllerConnection)
mControllerConnection->disconnect (cp1);
}
mComponentsConnected = false;
}
//---Terminate Controller Component
if (mController)
{
mController->setComponentHandler (nullptr);
if (mControllerInitialized)
mController->terminate ();
mControllerInitialized = false;
}
//---Terminate Processor Component
if (mComponent && mComponentInitialized)
{
mComponent->terminate ();
mComponentInitialized = false;
}
mInputEvents = nullptr;
mOutputEvents = nullptr;
mUnitInfo = nullptr;
mMidiMapping = nullptr;
if (mMidiCCMapping[0][0])
for (int32 b = 0; b < kMaxMidiMappingBusses; b++)
for (int32 i = 0; i < 16; i++)
{
delete mMidiCCMapping[b][i];
mMidiCCMapping[b][i] = nullptr;
}
mEditor = nullptr;
mController = nullptr;
mProcessor = nullptr;
mComponent = nullptr;
mFactory = nullptr;
mPlugInterfaceSupport = nullptr;
mProcessorConnection = nullptr;
mControllerConnection = nullptr;
}
//------------------------------------------------------------------------
bool BaseWrapper::init ()
{
// VST 3 stuff -----------------------------------------------
if (mProcessor)
mProcessor->queryInterface (IComponent::iid, (void**)&mComponent);
if (mController)
{
mController->queryInterface (IUnitInfo::iid, (void**)&mUnitInfo);
mController->queryInterface (IMidiMapping::iid, (void**)&mMidiMapping);
}
//---init the processor component
mComponentInitialized = true;
if (mComponent->initialize ((IHostApplication*)this) != kResultTrue)
return false;
//---init the controller component
if (mController)
{
// do not initialize 2 times the component if it is singleComponent
if (FUnknownPtr<IEditController> (mComponent).getInterface () != mController)
{
mControllerInitialized = true;
if (mController->initialize ((IHostApplication*)this) != kResultTrue)
return false;
}
//---set this class as Component Handler
mController->setComponentHandler (this);
//---connect the 2 components
auto cp1 = U::cast<IConnectionPoint> (mProcessor);
auto cp2 = U::cast<IConnectionPoint> (mController);
if (cp1 && cp2)
{
mProcessorConnection = owned (NEW ConnectionProxy (cp1));
mProcessorConnection->connect (cp2);
mControllerConnection = owned (NEW ConnectionProxy (cp2));
mControllerConnection->connect (cp1);
mComponentsConnected = true;
}
//---inform the component "controller" with the component "processor" state
MemoryStream stream;
if (mComponent->getState (&stream) == kResultTrue)
{
stream.seek (0, IBStream::kIBSeekSet, nullptr);
mController->setComponentState (&stream);
}
}
// Wrapper -----------------------------------------------
if (mProcessor)
{
if (mProcessor->canProcessSampleSize (kSample64) == kResultTrue)
{
_canDoubleReplacing (true); // supports double precision processing
// we use the 64 as default only if 32 bit not supported
if (mProcessor->canProcessSampleSize (kSample32) != kResultTrue)
mVst3SampleSize = kSample64;
else
mVst3SampleSize = kSample32;
}
// latency -------------------------------
_setInitialDelay (mProcessor->getLatencySamples ());
if (mProcessor->getTailSamples () == kNoTail)
_noTail (true);
setupProcessing (); // initialize vst3 component processing parameters
}
// parameters
setupParameters ();
// setup inputs and outputs
setupBuses ();
// find out programs of root unit --------------------------
mNumPrograms = 0;
if (mUnitInfo)
{
int32 programListCount = mUnitInfo->getProgramListCount ();
if (programListCount > 0)
{
ProgramListID rootUnitProgramListId = kNoProgramListId;
for (int32 i = 0; i < mUnitInfo->getUnitCount (); i++)
{
UnitInfo unit = {};
if (mUnitInfo->getUnitInfo (i, unit) == kResultTrue)
{
if (unit.id == kRootUnitId)
{
rootUnitProgramListId = unit.programListId;
break;
}
}
}
if (rootUnitProgramListId != kNoProgramListId)
{
for (int32 i = 0; i < programListCount; i++)
{
ProgramListInfo progList = {};
if (mUnitInfo->getProgramListInfo (i, progList) == kResultTrue)
{
if (progList.id == rootUnitProgramListId)
{
mNumPrograms = progList.programCount;
break;
}
}
}
}
}
}
if (mTimer == nullptr)
mTimer = owned (Timer::create (this, 50));
initMidiCtrlerAssignment ();
return true;
}
//------------------------------------------------------------------------
void BaseWrapper::_suspend ()
{
_stopProcess ();
if (mComponent && mActive == true)
mComponent->setActive (false);
mActive = false;
}
//------------------------------------------------------------------------
void BaseWrapper::_resume ()
{
if (mComponent && mActive == false)
mComponent->setActive (true);
mActive = true;
}
//------------------------------------------------------------------------
void BaseWrapper::_startProcess ()
{
if (mProcessor && mProcessing == false)
{
mProcessing = true;
mProcessor->setProcessing (true);
}
}
//------------------------------------------------------------------------
void BaseWrapper::_stopProcess ()
{
if (mProcessor && mProcessing == true)
{
mProcessor->setProcessing (false);
mProcessing = false;
}
}
//------------------------------------------------------------------------
void BaseWrapper::_setEditor (BaseEditorWrapper* editor)
{
mEditor = owned (editor);
}
//------------------------------------------------------------------------
bool BaseWrapper::_setBlockSize (int32 newBlockSize)
{
if (mProcessing)
return false;
if (mBlockSize != newBlockSize)
{
mBlockSize = newBlockSize;
setupProcessing ();
return true;
}
return false;
}
//------------------------------------------------------------------------
bool BaseWrapper::setupProcessing (int32 processModeOverwrite)
{
if (!mProcessor)
return false;
ProcessSetup setup;
if (processModeOverwrite >= 0)
setup.processMode = processModeOverwrite;
else
setup.processMode = mVst3processMode;
setup.maxSamplesPerBlock = mBlockSize;
setup.sampleRate = mSampleRate;
setup.symbolicSampleSize = mVst3SampleSize;
return mProcessor->setupProcessing (setup) == kResultTrue;
}
//------------------------------------------------------------------------
bool BaseWrapper::getEditorSize (int32& width, int32& height) const
{
if (auto* editor = getEditor ())
{
ViewRect rect;
if (editor->getRect (rect))
{
width = rect.right - rect.left;
height = rect.bottom - rect.top;
return true;
}
}
return false;
}
//------------------------------------------------------------------------
float BaseWrapper::_getParameter (int32 index) const
{
if (!mController)
return 0.f;
if (index < static_cast<int32> (mParameterMap.size ()))
{
ParamID id = mParameterMap.at (index).vst3ID;
return (float)mController->getParamNormalized (id);
}
return 0.f;
}
//------------------------------------------------------------------------
void BaseWrapper::addParameterChange (ParamID id, ParamValue value, int32 sampleOffset)
{
mGuiTransfer.addChange (id, value, sampleOffset);
mInputTransfer.addChange (id, value, sampleOffset);
}
//------------------------------------------------------------------------
/*! Usually VST 2 hosts call setParameter (...) and getParameterDisplay (...) synchronously.
In setParameter (...) param changes get queued (guiTransfer) and transfered in idle (::onTimer).
The ::onTimer call almost always comes AFTER getParameterDisplay (...) and therefore returns an
old
value. To avoid sending back old values, getLastParamChange (...) returns the latest value
from the guiTransfer queue. */
//------------------------------------------------------------------------
bool BaseWrapper::getLastParamChange (ParamID id, ParamValue& value)
{
ParameterChanges changes;
mGuiTransfer.transferChangesTo (changes);
for (int32 i = 0; i < changes.getParameterCount (); ++i)
{
IParamValueQueue* queue = changes.getParameterData (i);
if (queue)
{
if (queue->getParameterId () == id)
{
int32 points = queue->getPointCount ();
if (points > 0)
{
mGuiTransfer.transferChangesFrom (changes);
int32 sampleOffset = 0;
return queue->getPoint (points - 1, sampleOffset, value) == kResultTrue;
}
}
}
}
mGuiTransfer.transferChangesFrom (changes);
return false;
}
//------------------------------------------------------------------------
void BaseWrapper::getUnitPath (UnitID unitID, String& path) const
{
if (!mUnitInfo)
return;
//! Build the unit path up to the root unit (e.g. "Modulators.LFO 1.". Separator is a ".")
for (int32 unitIndex = 0; unitIndex < mUnitInfo->getUnitCount (); ++unitIndex)
{
UnitInfo info = {};
mUnitInfo->getUnitInfo (unitIndex, info);
if (info.id == unitID)
{
String unitName (info.name);
unitName.append (".");
path.insertAt (0, unitName);
if (info.parentUnitId != kRootUnitId)
getUnitPath (info.parentUnitId, path);
break;
}
}
}
//------------------------------------------------------------------------
int32 BaseWrapper::_getChunk (void** data, bool /*isPreset*/)
{
// Host stores plug-in state. Returns the size in bytes of the chunk (Plug-in allocates the data
// array)
MemoryStream componentStream;
if (mComponent && mComponent->getState (&componentStream) != kResultTrue)
componentStream.setSize (0);
MemoryStream controllerStream;
if (mController && mController->getState (&controllerStream) != kResultTrue)
controllerStream.setSize (0);
if (componentStream.getSize () + controllerStream.getSize () == 0)
return 0;
mChunk.setSize (0);
IBStreamer acc (&mChunk, kLittleEndian);
acc.writeInt64 (componentStream.getSize ());
acc.writeInt64 (controllerStream.getSize ());
acc.writeRaw (componentStream.getData (), static_cast<int32> (componentStream.getSize ()));
acc.writeRaw (controllerStream.getData (), static_cast<int32> (controllerStream.getSize ()));
auto chunkSize = static_cast<int32> (mChunk.getSize ());
*data = mChunk.getData ();
return chunkSize;
}
//------------------------------------------------------------------------
int32 BaseWrapper::_setChunk (void* data, int32 byteSize, bool isPreset)
{
if (!mComponent)
return 0;
// throw away all previously queued parameter changes, they are obsolete
mGuiTransfer.removeChanges ();
mInputTransfer.removeChanges ();
MemoryStream chunk (data, byteSize);
IBStreamer acc (&chunk, kLittleEndian);
int64 componentDataSize = 0;
int64 controllerDataSize = 0;
acc.readInt64 (componentDataSize);
acc.readInt64 (controllerDataSize);
VstPresetStream componentStream (((char*)data) + acc.tell (), componentDataSize);
VstPresetStream controllerStream (((char*)data) + acc.tell () + componentDataSize,
controllerDataSize);
if (!isPreset)
{
if (Vst::IAttributeList* attr = componentStream.getAttributes ())
attr->setString (Vst::PresetAttributes::kStateType,
String (Vst::StateType::kProject));
if (Vst::IAttributeList* attr = controllerStream.getAttributes ())
attr->setString (Vst::PresetAttributes::kStateType,
String (Vst::StateType::kProject));
}
mComponent->setState (&componentStream);
componentStream.seek (0, IBStream::kIBSeekSet, nullptr);
if (mController)
{
mController->setComponentState (&componentStream);
mController->setState (&controllerStream);
}
return 0;
}
//------------------------------------------------------------------------
bool BaseWrapper::_setBypass (bool onOff)
{
if (mBypassParameterID != kNoParamId)
{
addParameterChange (mBypassParameterID, onOff ? 1.0 : 0.0, 0);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
bool BaseWrapper::getProgramListAndUnit (int32 midiChannel, UnitID& unitId,
ProgramListID& programListId)
{
programListId = kNoProgramListId;
unitId = -1;
if (!mUnitInfo)
return false;
// use the first input event bus (VST 2 has only 1 bus for event)
if (mUnitInfo->getUnitByBus (kEvent, kInput, 0, midiChannel, unitId) == kResultTrue)
{
for (int32 i = 0, unitCount = mUnitInfo->getUnitCount (); i < unitCount; i++)
{
UnitInfo unitInfoStruct = {};
if (mUnitInfo->getUnitInfo (i, unitInfoStruct) == kResultTrue)
{
if (unitId == unitInfoStruct.id)
{
programListId = unitInfoStruct.programListId;
return programListId != kNoProgramListId;
}
}
}
}
return false;
}
//-----------------------------------------------------------------------------
bool BaseWrapper::getProgramListInfoByProgramListID (ProgramListID programListId,
ProgramListInfo& info)
{
if (mUnitInfo)
{
int32 programListCount = mUnitInfo->getProgramListCount ();
for (int32 i = 0; i < programListCount; i++)
{
memset (&info, 0, sizeof (ProgramListInfo));
if (mUnitInfo->getProgramListInfo (i, info) == kResultTrue)
{
if (info.id == programListId)
{
return true;
}
}
}
}
return false;
}
//-----------------------------------------------------------------------------
void BaseWrapper::setVendorName (char* name)
{
memcpy (mVendor, name, sizeof (mVendor));
}
//-----------------------------------------------------------------------------
void BaseWrapper::setEffectName (char* effectName)
{
memcpy (mName, effectName, sizeof (mName));
}
//-----------------------------------------------------------------------------
void BaseWrapper::setEffectVersion (char* version)
{
if (!version)
mVersion = 0;
else
{
long major = 1;
long minor = 0;
long subminor = 0;
long subsubminor = 0;
int32 ret = sscanf (version, "%ld.%ld.%ld.%ld", &major, &minor, &subminor, &subsubminor);
mVersion = static_cast<int32> ((major & 0xff) << 24);
if (ret > 3)
mVersion += static_cast<int32> (subsubminor & 0xff);
if (ret > 2)
mVersion += static_cast<int32> ((subminor & 0xff) << 8);
if (ret > 1)
mVersion += static_cast<int32> ((minor & 0xff) << 16);
}
}
//-----------------------------------------------------------------------------
void BaseWrapper::setSubCategories (char* string)
{
memcpy (mSubCategories, string, sizeof (mSubCategories));
}
//-----------------------------------------------------------------------------
void BaseWrapper::setupBuses ()
{
if (!mComponent)
return;
mProcessData.prepare (*mComponent, 0, mVst3SampleSize);
_setNumInputs (countMainBusChannels (kInput, mMainAudioInputBuses));
_setNumOutputs (countMainBusChannels (kOutput, mMainAudioOutputBuses));
mHasEventInputBuses = mComponent->getBusCount (kEvent, kInput) > 0;
mHasEventOutputBuses = mComponent->getBusCount (kEvent, kOutput) > 0;
if (mHasEventInputBuses)
{
if (mInputEvents == nullptr)
mInputEvents = owned (new EventList (kMaxEvents));
}
else
mInputEvents = nullptr;
if (mHasEventOutputBuses)
{
if (mOutputEvents == nullptr)
mOutputEvents = owned (new EventList (kMaxEvents));
}
else
mOutputEvents = nullptr;
}
//-----------------------------------------------------------------------------
void BaseWrapper::setupParameters ()
{
mParameterMap.clear ();
mParamIndexMap.clear ();
mBypassParameterID = mProgramParameterID = kNoParamId;
mProgramParameterIdx = -1;
std::vector<ParameterInfo> programParameterInfos;
std::vector<int32> programParameterIdxs;
int32 paramCount = mController ? mController->getParameterCount () : 0;
int32 numParamID = 0;
for (int32 i = 0; i < paramCount; i++)
{
ParameterInfo paramInfo = {};
if (mController->getParameterInfo (i, paramInfo) == kResultTrue)
{
//--- ------------------------------------------
if ((paramInfo.flags & ParameterInfo::kIsBypass) != 0)
{
if (mBypassParameterID == kNoParamId)
mBypassParameterID = paramInfo.id;
if (mUseExportedBypass)
{
ParamMapEntry entry = {paramInfo.id, i};
mParameterMap.push_back (entry);
mParamIndexMap[paramInfo.id] = mUseIncIndex ? numParamID : i;
numParamID++;
}
}
//--- ------------------------------------------
else if ((paramInfo.flags & ParameterInfo::kIsProgramChange) != 0)
{
programParameterInfos.push_back (paramInfo);
programParameterIdxs.push_back (i);
if (paramInfo.unitId == kRootUnitId)
{
if (mProgramParameterID == kNoParamId)
{
mProgramParameterID = paramInfo.id;
mProgramParameterIdx = i;
}
}
if (gExportProgramChangeParameters == true)
{
ParamMapEntry entry = {paramInfo.id, i};
mParameterMap.push_back (entry);
mParamIndexMap[paramInfo.id] = mUseIncIndex ? numParamID : i;
numParamID++;
}
}
//--- ------------------------------------------
// do not export read only parameters
else if ((paramInfo.flags & ParameterInfo::kIsReadOnly) == 0)
{
ParamMapEntry entry = {paramInfo.id, i};
mParameterMap.push_back (entry);
mParamIndexMap[paramInfo.id] = mUseIncIndex ? numParamID : i;
numParamID++;
}
}
}
mNumParams = static_cast<int32> (mParameterMap.size ());
mInputTransfer.setMaxParameters (paramCount);
mOutputTransfer.setMaxParameters (paramCount);
mGuiTransfer.setMaxParameters (paramCount);
mInputChanges.setMaxParameters (paramCount);
mOutputChanges.setMaxParameters (paramCount);
for (int32 midiChannel = 0; midiChannel < kMaxProgramChangeParameters; midiChannel++)
{
mProgramChangeParameterIDs[midiChannel] = kNoParamId;
mProgramChangeParameterIdxs[midiChannel] = -1;
UnitID unitId;
ProgramListID programListId;
if (getProgramListAndUnit (midiChannel, unitId, programListId))
{
for (uint32 i = 0; i < programParameterInfos.size (); i++)
{
const ParameterInfo& paramInfo = programParameterInfos.at (i);
if (paramInfo.unitId == unitId)
{
mProgramChangeParameterIDs[midiChannel] = paramInfo.id;
mProgramChangeParameterIdxs[midiChannel] = programParameterIdxs.at (i);
break;
}
}
}
}
}
//-----------------------------------------------------------------------------
void BaseWrapper::initMidiCtrlerAssignment ()
{
if (!mMidiMapping || !mComponent)
return;
int32 busses = Min<int32> (mComponent->getBusCount (kEvent, kInput), kMaxMidiMappingBusses);
if (!mMidiCCMapping[0][0])
{
for (int32 b = 0; b < busses; b++)
for (int32 i = 0; i < 16; i++)
mMidiCCMapping[b][i] = NEW ParamID[Vst::kCountCtrlNumber];
}
ParamID paramID;
for (int32 b = 0; b < busses; b++)
{
for (int16 ch = 0; ch < 16; ch++)
{
for (int32 i = 0; i < Vst::kCountCtrlNumber; i++)
{
paramID = kNoParamId;
if (mMidiMapping->getMidiControllerAssignment (b, ch, (CtrlNumber)i, paramID) ==
kResultTrue)
{
// TODO check if tag is associated to a parameter
mMidiCCMapping[b][ch][i] = paramID;
}
else
mMidiCCMapping[b][ch][i] = kNoParamId;
}
}
}
}
//-----------------------------------------------------------------------------
void BaseWrapper::_setSampleRate (float newSamplerate)
{
if (mProcessing)
return;
if (newSamplerate != mSampleRate)
{
mSampleRate = newSamplerate;
setupProcessing ();
}
}
//-----------------------------------------------------------------------------
uint32 BaseWrapper::countMainBusChannels (BusDirection dir, uint64& mainBusBitset)
{
uint32 result = 0;
mainBusBitset = 0;
int32 busCount = mComponent->getBusCount (kAudio, dir);
for (int32 i = 0; i < busCount; i++)
{
BusInfo busInfo = {};
if (mComponent->getBusInfo (kAudio, dir, i, busInfo) == kResultTrue)
{
if (busInfo.busType == kMain)
{
result += busInfo.channelCount;
mainBusBitset |= (uint64 (1) << i);
mComponent->activateBus (kAudio, dir, i, true);
}
else if (busInfo.flags & BusInfo::kDefaultActive)
{
mComponent->activateBus (kAudio, dir, i, false);
}
}
}
return result;
}
//-----------------------------------------------------------------------------
void BaseWrapper::processMidiEvent (Event& toAdd, char* midiData, bool isLive, int32 noteLength,
float noteOffVelocity, float detune)
{
uint8 status = static_cast<uint8> (midiData[0] & kStatusMask);
uint8 channel = static_cast<uint8> (midiData[0] & kChannelMask);
// not allowed
if (channel >= 16)
return;
if (isLive)
toAdd.flags |= Event::kIsLive;
//--- -----------------------------
switch (status)
{
case kNoteOn:
case kNoteOff:
{
if (status == kNoteOff || midiData[2] == 0) // note off
{
toAdd.type = Event::kNoteOffEvent;
toAdd.noteOff.channel = channel;
toAdd.noteOff.pitch = midiData[1];
toAdd.noteOff.velocity = noteOffVelocity;
toAdd.noteOff.noteId = -1; // TODO ?
}
else if (status == kNoteOn) // note on
{
toAdd.type = Event::kNoteOnEvent;
toAdd.noteOn.channel = channel;
toAdd.noteOn.pitch = midiData[1];
toAdd.noteOn.tuning = detune;
toAdd.noteOn.velocity = (float)midiData[2] * kMidiScaler;
toAdd.noteOn.length = noteLength;
toAdd.noteOn.noteId = -1; // TODO ?
}
mInputEvents->addEvent (toAdd);
}
break;
//--- -----------------------------
case kPolyPressure:
{
toAdd.type = Vst::Event::kPolyPressureEvent;
toAdd.polyPressure.channel = channel;
toAdd.polyPressure.pitch = static_cast<int16>(midiData[1] & kDataMask);
toAdd.polyPressure.pressure = (float)(midiData[2] & kDataMask) * kMidiScaler;
toAdd.polyPressure.noteId = -1; // TODO ?
mInputEvents->addEvent (toAdd);
}
break;
//--- -----------------------------
case kController:
{
if (toAdd.busIndex < kMaxMidiMappingBusses && mMidiCCMapping[toAdd.busIndex][channel])
{
ParamID paramID =
mMidiCCMapping[toAdd.busIndex][channel][static_cast<size_t> (midiData[1])];
if (paramID != kNoParamId)
{
ParamValue value = (double)midiData[2] * kMidiScaler;
int32 index = 0;
if (IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index))
queue->addPoint (toAdd.sampleOffset, value, index);
mGuiTransfer.addChange (paramID, value, toAdd.sampleOffset);
}
}
}
break;
//--- -----------------------------
case kPitchBendStatus:
{
if (toAdd.busIndex < kMaxMidiMappingBusses && mMidiCCMapping[toAdd.busIndex][channel])
{
ParamID paramID = mMidiCCMapping[toAdd.busIndex][channel][Vst::kPitchBend];
if (paramID != kNoParamId)
{
const double kPitchWheelScaler = 1. / (double)0x3FFF;
const int32 ctrl = static_cast<int32> ((midiData[1] & kDataMask) | ((midiData[2] & kDataMask) << 7));
ParamValue value = kPitchWheelScaler * (double)ctrl;
int32 index = 0;
if (IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index))
queue->addPoint (toAdd.sampleOffset, value, index);
mGuiTransfer.addChange (paramID, value, toAdd.sampleOffset);
}
}
}
break;
//--- -----------------------------
case kAfterTouchStatus:
{
if (toAdd.busIndex < kMaxMidiMappingBusses && mMidiCCMapping[toAdd.busIndex][channel])
{
ParamID paramID = mMidiCCMapping[toAdd.busIndex][channel][Vst::kAfterTouch];
if (paramID != kNoParamId)
{
ParamValue value = (ParamValue) (midiData[1] & kDataMask) * kMidiScaler;
int32 index = 0;
if (IParamValueQueue* queue = mInputChanges.addParameterData (paramID, index))
queue->addPoint (toAdd.sampleOffset, value, index);
mGuiTransfer.addChange (paramID, value, toAdd.sampleOffset);
}
}
}
break;
//--- -----------------------------
case kProgramChangeStatus:
{
if (mProgramChangeParameterIDs[channel] != kNoParamId &&
mProgramChangeParameterIdxs[channel] != -1)
{
ParameterInfo paramInfo = {};
if (mController->getParameterInfo (mProgramChangeParameterIdxs[channel],
paramInfo) == kResultTrue)
{
int32 program = midiData[1];
if (paramInfo.stepCount > 0 && program <= paramInfo.stepCount)
{
ParamValue normalized =
(ParamValue)program / (ParamValue)paramInfo.stepCount;
addParameterChange (mProgramChangeParameterIDs[channel], normalized,
toAdd.sampleOffset);
}
}
}
}
break;
}
}
//-----------------------------------------------------------------------------
template <class T>
inline void BaseWrapper::setProcessingBuffers (T** inputs, T** outputs)
{
// set processing buffers
int32 sourceIndex = 0;
for (int32 i = 0; i < mProcessData.numInputs; i++)
{
AudioBusBuffers& buffers = mProcessData.inputs[i];
if (mMainAudioInputBuses & (uint64 (1) << i))
{
for (int32 j = 0; j < buffers.numChannels; j++)
{
buffers.channelBuffers32[j] = (Sample32*)inputs[sourceIndex++];
}
}
else
buffers.silenceFlags = HostProcessData::kAllChannelsSilent;
}
sourceIndex = 0;
for (int32 i = 0; i < mProcessData.numOutputs; i++)
{
AudioBusBuffers& buffers = mProcessData.outputs[i];
buffers.silenceFlags = 0;
if (mMainAudioOutputBuses & (uint64 (1) << i))
{
for (int32 j = 0; j < buffers.numChannels; j++)
{
buffers.channelBuffers32[j] = (Sample32*)outputs[sourceIndex++];
}
}
}
}
//-----------------------------------------------------------------------------
inline void BaseWrapper::setEventPPQPositions ()
{
if (!mInputEvents)
return;
int32 eventCount = mInputEvents->getEventCount ();
if (eventCount > 0 && (mProcessContext.state & ProcessContext::kTempoValid) &&
(mProcessContext.state & ProcessContext::kProjectTimeMusicValid))
{
TQuarterNotes projectTimeMusic = mProcessContext.projectTimeMusic;
double secondsToQuarterNoteScaler = mProcessContext.tempo / 60.0;
double multiplicator = secondsToQuarterNoteScaler / mSampleRate;
for (int32 i = 0; i < eventCount; i++)
{
Event* e = mInputEvents->getEventByIndex (i);
if (e)
{
TQuarterNotes localTimeMusic = e->sampleOffset * multiplicator;
e->ppqPosition = projectTimeMusic + localTimeMusic;
}
}
}
}
//-----------------------------------------------------------------------------
inline void BaseWrapper::doProcess (int32 sampleFrames)
{
if (!mProcessor)
return;
mProcessData.numSamples = sampleFrames;
if (mProcessing == false)
_startProcess ();
mProcessData.inputEvents = mInputEvents;
mProcessData.outputEvents = mOutputEvents;
setupProcessTimeInfo ();
setEventPPQPositions ();
mInputTransfer.transferChangesTo (mInputChanges);
mProcessData.inputParameterChanges = &mInputChanges;
mProcessData.outputParameterChanges = &mOutputChanges;
mOutputChanges.clearQueue ();
// VST 3 process call
mProcessor->process (mProcessData);
processOutputParametersChanges ();
mOutputTransfer.transferChangesFrom (mOutputChanges);
processOutputEvents ();
// clear input parameters and events
mInputChanges.clearQueue ();
if (mInputEvents)
mInputEvents->clear ();
}
//-----------------------------------------------------------------------------
void BaseWrapper::_processReplacing (float** inputs, float** outputs, int32 sampleFrames)
{
if (mProcessData.symbolicSampleSize != kSample32)
return;
setProcessingBuffers<float> (inputs, outputs);
doProcess (sampleFrames);
}
//-----------------------------------------------------------------------------
void BaseWrapper::_processDoubleReplacing (double** inputs, double** outputs, int32 sampleFrames)
{
if (mProcessData.symbolicSampleSize != kSample64)
return;
setProcessingBuffers<double> (inputs, outputs);
doProcess (sampleFrames);
}
//-----------------------------------------------------------------------------
void BaseWrapper::onTimer (Timer*)
{
if (!mController)
return;
ParamID id;
ParamValue value;
int32 sampleOffset;
while (mOutputTransfer.getNextChange (id, value, sampleOffset))
{
mController->setParamNormalized (id, value);
}
while (mGuiTransfer.getNextChange (id, value, sampleOffset))
{
mController->setParamNormalized (id, value);
}
}
// FUnknown
//-----------------------------------------------------------------------------
tresult PLUGIN_API BaseWrapper::queryInterface (const char* iid, void** obj)
{
QUERY_INTERFACE (iid, obj, FUnknown::iid, Vst::IHostApplication)
QUERY_INTERFACE (iid, obj, Vst::IHostApplication::iid, Vst::IHostApplication)
QUERY_INTERFACE (iid, obj, Vst::IComponentHandler::iid, Vst::IComponentHandler)
QUERY_INTERFACE (iid, obj, Vst::IUnitHandler::iid, Vst::IUnitHandler)
if (mPlugInterfaceSupport && mPlugInterfaceSupport->queryInterface (iid, obj) == kResultTrue)
return ::Steinberg::kResultOk;
*obj = nullptr;
return kNoInterface;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API BaseWrapper::restartComponent (int32 flags)
{
tresult result = kResultFalse;
//--- ----------------------
if (flags & kIoChanged)
{
setupBuses ();
_ioChanged ();
result = kResultTrue;
}
//--- ----------------------
if ((flags & kParamValuesChanged) || (flags & kParamTitlesChanged))
{
_updateDisplay ();
result = kResultTrue;
}
//--- ----------------------
if (flags & kLatencyChanged)
{
if (mProcessor)
_setInitialDelay (mProcessor->getLatencySamples ());
_ioChanged ();
result = kResultTrue;
}
//--- ----------------------
if (flags & kMidiCCAssignmentChanged)
{
initMidiCtrlerAssignment ();
result = kResultTrue;
}
// kReloadComponent is Not supported
return result;
}
//-----------------------------------------------------------------------------
// IHostApplication
//-----------------------------------------------------------------------------
tresult PLUGIN_API BaseWrapper::createInstance (TUID cid, TUID iid, void** obj)
{
FUID classID (FUID::fromTUID (cid));
FUID interfaceID (FUID::fromTUID (iid));
if (classID == IMessage::iid && interfaceID == IMessage::iid)
{
*obj = new HostMessage;
return kResultTrue;
}
else if (classID == IAttributeList::iid && interfaceID == IAttributeList::iid)
{
if (auto al = HostAttributeList::make ())
{
*obj = al.take ();
return kResultTrue;
}
return kOutOfMemory;
}
*obj = nullptr;
return kResultFalse;
}
//-----------------------------------------------------------------------------
// IUnitHandler
//-----------------------------------------------------------------------------
tresult PLUGIN_API BaseWrapper::notifyUnitSelection (UnitID /*unitId*/)
{
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API BaseWrapper::notifyProgramListChange (ProgramListID /*listId*/, int32 /*programIndex*/)
{
// TODO -> redirect to hasMidiProgramsChanged somehow...
return kResultTrue;
}
//-----------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
//-----------------------------------------------------------------------------
/// \endcond