1403 lines
42 KiB
C++
1403 lines
42 KiB
C++
//------------------------------------------------------------------------
|
|
// Flags : clang-format auto
|
|
// Project : VST SDK
|
|
//
|
|
// Category : Helpers
|
|
// Filename : public.sdk/source/vst/aaxwrapper/aaxwrapper.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.h"
|
|
|
|
#include "AAX.h"
|
|
#include "AAX_Assert.h"
|
|
#include "AAX_Errors.h"
|
|
#include "AAX_ICollection.h"
|
|
#include "AAX_IComponentDescriptor.h"
|
|
#include "AAX_IEffectDescriptor.h"
|
|
#include "AAX_IMIDINode.h"
|
|
#include "AAX_IPropertyMap.h"
|
|
#include "AAX_Version.h"
|
|
|
|
static_assert (AAX_SDK_CURRENT_REVISION >= AAX_SDK_2p3p2_REVISION,
|
|
"VST3 SDK requires AAX SDK version 2.3.2 or higher");
|
|
|
|
#include "aaxwrapper_description.h"
|
|
#include "aaxwrapper_gui.h"
|
|
#include "aaxwrapper_parameters.h"
|
|
|
|
#include "base/thread/include/fcondition.h"
|
|
|
|
#include "pluginterfaces/base/funknownimpl.h"
|
|
|
|
#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
|
|
|
|
#if SMTG_OS_WINDOWS
|
|
#include <windows.h>
|
|
#define getCurrentThread() ((void*)(size_t)GetCurrentThreadId ())
|
|
#else
|
|
#include <pthread.h>
|
|
#define getCurrentThread() (pthread_self ())
|
|
#endif
|
|
|
|
using namespace Steinberg;
|
|
using namespace Steinberg::Vst;
|
|
using namespace Steinberg::Base::Thread;
|
|
|
|
//------------------------------------------------------------------------
|
|
class AAXEditorWrapper : public BaseEditorWrapper
|
|
{
|
|
public:
|
|
AAXEditorWrapper (AAXWrapper* wrapper, IEditController* controller)
|
|
: BaseEditorWrapper (controller), mAAXWrapper (wrapper)
|
|
{
|
|
}
|
|
|
|
tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) SMTG_OVERRIDE;
|
|
|
|
private:
|
|
AAXWrapper* mAAXWrapper;
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXEditorWrapper::resizeView (IPlugView* view, ViewRect* newSize)
|
|
{
|
|
tresult result = kResultFalse;
|
|
if (view && newSize)
|
|
{
|
|
if (mAAXWrapper->_sizeWindow (newSize->getWidth (), newSize->getHeight ()))
|
|
{
|
|
result = view->onSize (newSize);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
AAXWrapper::AAXWrapper (BaseWrapper::SVST3Config& config, AAXWrapper_Parameters* p,
|
|
AAX_Plugin_Desc* desc)
|
|
: BaseWrapper (config), mAAXParams (p), mPluginDesc (desc)
|
|
{
|
|
HLOG (HAPI, "%s", __FUNCTION__);
|
|
|
|
mBlockSize = 1024; // never explicitly changed by Pro Tools, so assume the maximum
|
|
mUseExportedBypass = true;
|
|
mUseIncIndex = false;
|
|
|
|
mainThread = getCurrentThread ();
|
|
|
|
// must be in lock step with DescribeAlgorithmComponent
|
|
int32 idx = idxBufferSize + 1;
|
|
if (desc->mInputChannels || desc->mOutputChannels)
|
|
idxInputChannels = idx++;
|
|
if (desc->mOutputChannels)
|
|
idxOutputChannels = idx++;
|
|
if (desc->mSideChainInputChannels)
|
|
idxSideChainInputChannels = idx++;
|
|
|
|
if (desc->mMIDIports)
|
|
{
|
|
for (AAX_MIDI_Desc* mdesc = desc->mMIDIports; mdesc->mName; mdesc++)
|
|
mCountMIDIports++;
|
|
|
|
if (mCountMIDIports > 0)
|
|
{
|
|
idxMidiPorts = idx;
|
|
idx += mCountMIDIports;
|
|
}
|
|
}
|
|
|
|
int32 numAuxOutputs = 0;
|
|
mAAXOutputs = static_cast<uint32> (desc->mOutputChannels);
|
|
if (desc->mAuxOutputChannels)
|
|
{
|
|
for (AAX_Aux_Desc *adesc = desc->mAuxOutputChannels; adesc->mName; adesc++, numAuxOutputs++)
|
|
mAAXOutputs += adesc->mChannels < 0 ? desc->mOutputChannels : adesc->mChannels;
|
|
|
|
if (numAuxOutputs > 0)
|
|
{
|
|
idxAuxOutputs = idx;
|
|
idx += numAuxOutputs;
|
|
}
|
|
}
|
|
|
|
if (desc->mMeters)
|
|
{
|
|
idxMeters = idx++;
|
|
|
|
for (auto mdesc = desc->mMeters; mdesc->mName; mdesc++)
|
|
mCntMeters++;
|
|
mMeterIds.reset (NEW Steinberg::Vst::ParamID (mCntMeters));
|
|
mCntMeters = 0;
|
|
for (auto mdesc = desc->mMeters; mdesc->mName; mdesc++)
|
|
mMeterIds[mCntMeters++] = static_cast<Steinberg::Vst::ParamID> (mdesc->mID);
|
|
}
|
|
|
|
numDataPointers = idx;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
AAXWrapper::~AAXWrapper ()
|
|
{
|
|
HLOG (HAPI, "%s", __FUNCTION__);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::getName (String128 name)
|
|
{
|
|
String str ("AAXWrapper");
|
|
str.copyTo16 (name, 0, 127);
|
|
return kResultTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
Vst::ParamID getVstParamID (AAX_CParamID aaxid)
|
|
{
|
|
if (aaxid[0] != 'p')
|
|
return kNoParamId;
|
|
long id;
|
|
if (sscanf (aaxid + 1, "%lx", &id) != 1)
|
|
return kNoParamId;
|
|
return static_cast<Vst::ParamID> (id);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// IComponentHandler
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::beginEdit (ParamID tag)
|
|
{
|
|
HLOG (HAPI, "%s(tag=%x)", __FUNCTION__, tag);
|
|
|
|
AAX_CID aaxid (tag);
|
|
mAAXParams->TouchParameter (aaxid);
|
|
return kResultTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::performEdit (ParamID tag, ParamValue valueNormalized)
|
|
{
|
|
HLOG (HAPI, "%s(tag=%x, value=%lf)", __FUNCTION__, tag, valueNormalized);
|
|
|
|
AAX_CID aaxid (tag);
|
|
mAAXParams->SetParameterNormalizedValue (aaxid, valueNormalized);
|
|
return kResultTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::endEdit (ParamID tag)
|
|
{
|
|
HLOG (HAPI, "%s(tag=%x)", __FUNCTION__, tag);
|
|
|
|
AAX_CID aaxid (tag);
|
|
mAAXParams->ReleaseParameter (aaxid);
|
|
return kResultTrue;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::setDirty (TBool state)
|
|
{
|
|
mAAXParams->setDirty (state != 0);
|
|
return kResultOk;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::requestOpenEditor (FIDString /*name*/)
|
|
{
|
|
return kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::startGroupEdit ()
|
|
{
|
|
return kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult PLUGIN_API AAXWrapper::finishGroupEdit ()
|
|
{
|
|
return kResultFalse;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool AAXWrapper::init ()
|
|
{
|
|
bool res = BaseWrapper::init ();
|
|
|
|
if (mController && BaseEditorWrapper::hasEditor (mController))
|
|
_setEditor (NEW AAXEditorWrapper (this, mController));
|
|
|
|
return res;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::setupProcessTimeInfo ()
|
|
{
|
|
mProcessContext.state = 0;
|
|
mProcessContext.sampleRate = mSampleRate;
|
|
|
|
if (AAX_ITransport* transport = mAAXParams->Transport ())
|
|
{
|
|
int64_t splPos, ppqPos, loopStart, loopEnd;
|
|
bool playing, looping;
|
|
if (transport->GetCurrentNativeSampleLocation (&splPos) == AAX_SUCCESS)
|
|
mProcessContext.projectTimeSamples = (TSamples)splPos;
|
|
|
|
if (transport->GetCurrentTickPosition (&ppqPos) == AAX_SUCCESS)
|
|
{
|
|
mProcessContext.projectTimeMusic = (double)ppqPos / 960000.0;
|
|
mProcessContext.state |= ProcessContext::kProjectTimeMusicValid;
|
|
}
|
|
else
|
|
mProcessContext.projectTimeMusic = 0;
|
|
|
|
if (transport->GetCurrentTempo (&mProcessContext.tempo) == AAX_SUCCESS)
|
|
mProcessContext.state |= ProcessContext::kTempoValid;
|
|
|
|
if (transport->GetCurrentLoopPosition (&looping, &loopStart, &loopEnd) == AAX_SUCCESS)
|
|
{
|
|
mProcessContext.cycleStartMusic = (double)loopStart / 960000.0;
|
|
mProcessContext.cycleEndMusic = (double)loopEnd / 960000.0;
|
|
mProcessContext.state |= ProcessContext::kCycleValid;
|
|
if (looping)
|
|
mProcessContext.state |= ProcessContext::kCycleActive;
|
|
}
|
|
|
|
if (transport->IsTransportPlaying (&playing) == AAX_SUCCESS)
|
|
{
|
|
mProcessContext.state |= (playing ? ProcessContext::kPlaying : 0);
|
|
}
|
|
|
|
// workaround ppqPos not updating for every 2nd audio blocks @ 96 kHz (and more for higher
|
|
// frequencies)
|
|
// and while the UI is frozen, e.g. during save
|
|
static const int32 playflags = ProcessContext::kPlaying |
|
|
ProcessContext::kProjectTimeMusicValid |
|
|
ProcessContext::kTempoValid;
|
|
if ((playflags & mProcessContext.state) == playflags && mSampleRate != 0)
|
|
{
|
|
TQuarterNotes ppq = mProcessContext.projectTimeMusic;
|
|
if (ppq == mLastPpqPos && mLastPpqPos != 0 && mNextPpqPos != 0)
|
|
{
|
|
TQuarterNotes nextppq = mNextPpqPos;
|
|
if (mProcessContext.state & ProcessContext::kCycleActive)
|
|
if (nextppq >= mProcessContext.cycleEndMusic)
|
|
nextppq += mProcessContext.cycleStartMusic - mProcessContext.cycleEndMusic;
|
|
|
|
mProcessContext.projectTimeMusic = nextppq;
|
|
}
|
|
mLastPpqPos = ppq;
|
|
mNextPpqPos = mProcessContext.projectTimeMusic +
|
|
mProcessContext.tempo / 60 * mProcessData.numSamples / mSampleRate;
|
|
}
|
|
else
|
|
{
|
|
mLastPpqPos = mNextPpqPos = 0;
|
|
}
|
|
|
|
int32_t num, den;
|
|
if (transport->GetCurrentMeter (&num, &den) == AAX_SUCCESS)
|
|
{
|
|
mProcessContext.timeSigNumerator = num;
|
|
mProcessContext.timeSigDenominator = den;
|
|
mProcessContext.state |= ProcessContext::kTimeSigValid;
|
|
}
|
|
else
|
|
mProcessContext.timeSigNumerator = mProcessContext.timeSigDenominator = 4;
|
|
|
|
AAX_EFrameRate frameRate;
|
|
int32_t offset;
|
|
if (transport->GetTimeCodeInfo (&frameRate, &offset) == AAX_SUCCESS)
|
|
{
|
|
mProcessContext.state |= ProcessContext::kSmpteValid;
|
|
mProcessContext.smpteOffsetSubframes = offset;
|
|
switch (frameRate)
|
|
{
|
|
case AAX_eFrameRate_24Frame: mProcessContext.frameRate.framesPerSecond = 24; break;
|
|
case AAX_eFrameRate_25Frame: mProcessContext.frameRate.framesPerSecond = 25; break;
|
|
case AAX_eFrameRate_2997NonDrop:
|
|
mProcessContext.frameRate.framesPerSecond = 30;
|
|
mProcessContext.frameRate.flags = FrameRate::kPullDownRate;
|
|
break;
|
|
case AAX_eFrameRate_2997DropFrame:
|
|
mProcessContext.frameRate.framesPerSecond = 30;
|
|
mProcessContext.frameRate.flags =
|
|
FrameRate::kDropRate | FrameRate::kPullDownRate;
|
|
break;
|
|
case AAX_eFrameRate_30NonDrop:
|
|
mProcessContext.frameRate.framesPerSecond = 30;
|
|
break;
|
|
case AAX_eFrameRate_30DropFrame:
|
|
mProcessContext.frameRate.framesPerSecond = 30;
|
|
mProcessContext.frameRate.flags = FrameRate::kDropRate;
|
|
break;
|
|
case AAX_eFrameRate_23976:
|
|
mProcessContext.frameRate.framesPerSecond = 24;
|
|
mProcessContext.frameRate.flags = FrameRate::kPullDownRate;
|
|
break;
|
|
case AAX_eFrameRate_Undeclared:
|
|
default: mProcessContext.state &= ~ProcessContext::kSmpteValid;
|
|
}
|
|
}
|
|
|
|
mProcessData.processContext = &mProcessContext;
|
|
}
|
|
else
|
|
mProcessData.processContext = nullptr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool AAXWrapper::_sizeWindow (int32 width, int32 height)
|
|
{
|
|
HLOG (HAPI, "%s(width=%x, height=%x)", __FUNCTION__, width, height);
|
|
|
|
AAX_ASSERT (mainThread == getCurrentThread ());
|
|
|
|
if (!mAAXGUI)
|
|
return false;
|
|
|
|
AAX_Point size (static_cast<float> (height), static_cast<float> (width));
|
|
return mAAXGUI->setWindowSize (size);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
struct AAXWrapper::GetChunkMessage : public FCondition
|
|
{
|
|
void* mData = nullptr;
|
|
int32 mDataSize = 0;
|
|
int32 mResult = 0;
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
int32 AAXWrapper::_getChunk (void** data, bool isPreset)
|
|
{
|
|
if (mWantsSetChunk)
|
|
{
|
|
// isPreset is always false for AAX, so we can ignore it
|
|
*data = mChunk.getData ();
|
|
return static_cast<int32> (mChunk.getSize ());
|
|
}
|
|
if (mainThread == getCurrentThread ())
|
|
return BaseWrapper::_getChunk (data, isPreset);
|
|
|
|
auto* msg = NEW GetChunkMessage;
|
|
{
|
|
FGuard guard (msgQueueLock);
|
|
msgQueue.push_back (msg);
|
|
}
|
|
msg->wait ();
|
|
|
|
*data = msg->mData;
|
|
return msg->mResult;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
int32 AAXWrapper::_setChunk (void* data, int32 byteSize, bool isPreset)
|
|
{
|
|
if (mainThread == getCurrentThread ())
|
|
return BaseWrapper::_setChunk (data, byteSize, isPreset);
|
|
|
|
FGuard guard (msgQueueLock);
|
|
mChunk.setSize (byteSize);
|
|
memcpy (mChunk.getData (), data, static_cast<size_t> (byteSize));
|
|
mWantsSetChunk = true;
|
|
mWantsSetChunkIsPreset = isPreset;
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::onTimer (Timer* timer)
|
|
{
|
|
BaseWrapper::onTimer (timer);
|
|
|
|
AAX_ASSERT (mainThread == getCurrentThread ());
|
|
|
|
if (mWantsSetChunk && !mSettingChunk)
|
|
{
|
|
mSettingChunk = true;
|
|
FGuard guard (msgQueueLock);
|
|
BaseWrapper::_setChunk (mChunk.getData (), static_cast<int32> (mChunk.getSize ()),
|
|
mWantsSetChunkIsPreset);
|
|
mWantsSetChunk = false;
|
|
mSettingChunk = false;
|
|
mWantsSetChunkIsPreset = false;
|
|
|
|
if (mPresetChanged)
|
|
{
|
|
int32_t numParams;
|
|
if (mAAXParams->GetNumberOfParameters (&numParams) == AAX_SUCCESS)
|
|
{
|
|
AAX_CID bypassId (mBypassParameterID);
|
|
for (auto i = 0; i < numParams; i++)
|
|
{
|
|
AAX_CString id;
|
|
if (mAAXParams->GetParameterIDFromIndex (i, &id) == AAX_SUCCESS)
|
|
{
|
|
if (id == (const char*)bypassId)
|
|
{
|
|
mAAXParams->SetParameterNormalizedValue (id.CString (),
|
|
mBypassBeforePresetChanged);
|
|
}
|
|
else
|
|
{
|
|
double value;
|
|
if (mAAXParams->GetParameterNormalizedValue (id.CString (), &value) ==
|
|
AAX_SUCCESS)
|
|
{
|
|
mAAXParams->SetParameterNormalizedValue (id.CString (), value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mPresetChanged = false;
|
|
}
|
|
}
|
|
|
|
updateActiveOutputState ();
|
|
|
|
while (!msgQueue.empty ())
|
|
{
|
|
GetChunkMessage* msg = nullptr;
|
|
{
|
|
FGuard guard (msgQueueLock);
|
|
if (!msgQueue.empty ())
|
|
{
|
|
msg = msgQueue.front ();
|
|
msgQueue.pop_front ();
|
|
}
|
|
}
|
|
if (msg)
|
|
{
|
|
msg->mResult = BaseWrapper::_getChunk (&msg->mData, false);
|
|
msg->signal ();
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
int32 AAXWrapper::getParameterInfo (AAX_CParamID aaxId, Vst::ParameterInfo& paramInfo)
|
|
{
|
|
HLOG (HAPI, "%s(id=%s)", __FUNCTION__, aaxId);
|
|
|
|
Vst::ParamID id = getVstParamID (aaxId);
|
|
if (id == -1)
|
|
return AAX_ERROR_INVALID_PARAMETER_ID;
|
|
|
|
std::map<ParamID, int32>::iterator iter = mParamIndexMap.find (id);
|
|
if (iter == mParamIndexMap.end ())
|
|
return AAX_ERROR_INVALID_PARAMETER_ID;
|
|
|
|
if (mController->getParameterInfo (iter->second, paramInfo) != kResultTrue)
|
|
return AAX_ERROR_INVALID_PARAMETER_ID;
|
|
|
|
return AAX_SUCCESS;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
bool AAXWrapper::generatePageTables (const char* outputFile)
|
|
{
|
|
#if 0 && DEVELOPMENT
|
|
FILE* fh = fopen (outputFile, "w");
|
|
if (!fh)
|
|
return false;
|
|
|
|
fprintf (fh, "<?xml version='1.0' encoding='US-ASCII' standalone='yes'?>\n"
|
|
"<PageTables vers='6.4.0.89'>\n"
|
|
"\t<PageTableLayouts>\n");
|
|
|
|
AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription ();
|
|
for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++)
|
|
{
|
|
uint32_t manId = effDesc->mManufacturerID; SWAP_32 (manId);
|
|
uint32_t prodId = effDesc->mProductID; SWAP_32 (prodId);
|
|
uint32_t plugId = pdesc->mPlugInIDNative; SWAP_32 (plugId);
|
|
fprintf (fh, "\t\t<Plugin manID='%4.4s' prodID='%4.4s' plugID='%4.4s'>\n"
|
|
"\t\t\t<Desc>%s</Desc>\n"
|
|
"\t\t\t<Layout>PageTable 1</Layout>\n"
|
|
"\t\t</Plugin>",
|
|
&manId, &prodId, &plugId, pdesc->mName);
|
|
fprintf (fh, "\t\t\t<PageTable type='PgTL' pgsz='1'>\n");
|
|
|
|
int32 cntParams = controller->getParameterCount ();
|
|
for (int32 i = 0; i < cntParams; i++)
|
|
{
|
|
Vst::ParameterInfo info;
|
|
if (controller->getParameterInfo (i, info) == kResultTrue &&
|
|
(info.flags & ParameterInfo::kCanAutomate))
|
|
{
|
|
AAX_CID id (info.id);
|
|
String stitle (info.shortTitle);
|
|
fprintf (fh, "\t\t\t\t<Page num='%d'><ID>%s</ID></Page><!-- %s -->\n", i + 1, (const char*) id, stitle.text8 ());
|
|
}
|
|
}
|
|
fprintf (fh, "\t\t\t</PageTable>\n");
|
|
}
|
|
|
|
fprintf (fh, "\t</PageTableLayouts>\n");
|
|
fprintf (fh, "\t</PageTableLayouts>\n");
|
|
fprintf (fh, "\t<Editor vers='1.1.0.1'>\n"
|
|
"\t\t<PluginList>\n"
|
|
"\t\t\t<RTAS>\n");
|
|
for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++)
|
|
{
|
|
uint32_t manId = effDesc->mManufacturerID; SWAP_32 (manId);
|
|
uint32_t prodId = effDesc->mProductID; SWAP_32 (prodId);
|
|
uint32_t plugId = pdesc->mPlugInIDNative; SWAP_32 (plugId);
|
|
fprintf (fh, "\t\t\t\t<PluginID manID='%4.4s' prodID='%4.4s' plugID='%4.4s'>\n"
|
|
"\t\t\t\t\t<MenuStr>%s</MenuStr>\n"
|
|
"\t\t\t\t</PluginID>\n",
|
|
&manId, &prodId, &plugId, pdesc->mName);
|
|
}
|
|
fprintf (fh, "\t\t\t</RTAS>\n"
|
|
"\t\t</PluginList>\n"
|
|
"\t</Editor>\n");
|
|
fprintf (fh, "</PageTables>\n");
|
|
|
|
fclose (fh);
|
|
#else
|
|
outputFile;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
static int32 getChannelsStem (int32 channels)
|
|
{
|
|
switch (channels)
|
|
{
|
|
case 1: return AAX_eStemFormat_Mono;
|
|
case 2: return AAX_eStemFormat_Stereo;
|
|
case 3: return AAX_eStemFormat_LCR;
|
|
case 4:
|
|
return AAX_eStemFormat_Ambi_1_ACN; // AAX_eStemFormat_Quad
|
|
case 5: return AAX_eStemFormat_5_0;
|
|
case 6:
|
|
return AAX_eStemFormat_5_1; // AAX_eStemFormat_6_0
|
|
case 7:
|
|
return AAX_eStemFormat_6_1; // AAX_eStemFormat_7_0_DTS
|
|
case 8: return AAX_eStemFormat_7_1_DTS;
|
|
case 9:
|
|
return AAX_eStemFormat_Ambi_2_ACN; // AAX_eStemFormat_7_0_2;
|
|
case 10: return AAX_eStemFormat_7_1_2;
|
|
case 16: return AAX_eStemFormat_Ambi_3_ACN;
|
|
}
|
|
return AAX_eStemFormat_None;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------
|
|
static SpeakerArrangement numChannelsToSpeakerArrangement (int32 numChannels)
|
|
{
|
|
switch (numChannels)
|
|
{
|
|
case 1: return SpeakerArr::kMono;
|
|
case 2: return SpeakerArr::kStereo;
|
|
case 3: return SpeakerArr::k30Cine;
|
|
case 4: return SpeakerArr::kAmbi1stOrderACN;
|
|
case 5: return SpeakerArr::k50;
|
|
case 6: return SpeakerArr::k51;
|
|
case 7: return SpeakerArr::k61Cine;
|
|
case 8: return SpeakerArr::k71Cine;
|
|
case 9: return SpeakerArr::kAmbi2cdOrderACN;
|
|
case 10: return SpeakerArr::k71_2;
|
|
case 16: return SpeakerArr::kAmbi3rdOrderACN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// static
|
|
//------------------------------------------------------------------------
|
|
AAXWrapper* AAXWrapper::create (IPluginFactory* factory, const TUID vst3ComponentID,
|
|
AAX_Plugin_Desc* desc, AAXWrapper_Parameters* params)
|
|
{
|
|
// mostly a copy of BaseWrapper::create
|
|
if (!factory)
|
|
return nullptr;
|
|
|
|
BaseWrapper::SVST3Config config;
|
|
config.processor = nullptr;
|
|
config.factory = factory;
|
|
|
|
FReleaser factoryReleaser (factory);
|
|
|
|
factory->createInstance (vst3ComponentID, IAudioProcessor::iid, (void**)&config.processor);
|
|
if (!config.processor)
|
|
return nullptr;
|
|
|
|
config.controller = nullptr;
|
|
if (config.processor->queryInterface (IEditController::iid, (void**)&config.controller) !=
|
|
kResultTrue)
|
|
{
|
|
if (auto component = U::cast<IComponent> (config.processor))
|
|
{
|
|
TUID editorCID;
|
|
if (component->getControllerClassId (editorCID) == kResultTrue)
|
|
{
|
|
factory->createInstance (editorCID, IEditController::iid,
|
|
(void**)&config.controller);
|
|
}
|
|
}
|
|
}
|
|
config.vst3ComponentID = FUID::fromTUID (vst3ComponentID);
|
|
|
|
auto* wrapper = NEW AAXWrapper (config, params, desc);
|
|
if (wrapper->init () == false || wrapper->setupBusArrangements (desc) != kResultOk)
|
|
{
|
|
wrapper->release ();
|
|
return nullptr;
|
|
}
|
|
wrapper->setupBuses (); // again to adjust to changes done by setupBusArrangements
|
|
|
|
// vstwrapper ignores side chain channels, pretend they are main inputs
|
|
uint64 scBusChannels;
|
|
wrapper->countSidechainBusChannels (kInput, scBusChannels);
|
|
wrapper->mMainAudioInputBuses |= scBusChannels;
|
|
|
|
if (auto factory2 = U::cast<IPluginFactory2> (factory))
|
|
{
|
|
PFactoryInfo factoryInfo;
|
|
if (factory2->getFactoryInfo (&factoryInfo) == kResultTrue)
|
|
wrapper->setVendorName (factoryInfo.vendor);
|
|
|
|
for (int32 i = 0; i < factory2->countClasses (); i++)
|
|
{
|
|
Steinberg::PClassInfo2 classInfo2;
|
|
if (factory2->getClassInfo2 (i, &classInfo2) == Steinberg::kResultTrue)
|
|
{
|
|
if (memcmp (classInfo2.cid, vst3ComponentID, sizeof (TUID)) == 0)
|
|
{
|
|
wrapper->setSubCategories (classInfo2.subCategories);
|
|
wrapper->setEffectName (classInfo2.name);
|
|
|
|
if (classInfo2.vendor[0] != 0)
|
|
wrapper->setVendorName (classInfo2.vendor);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return wrapper;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------
|
|
int32 AAXWrapper::countSidechainBusChannels (BusDirection dir, uint64& scBusBitset)
|
|
{
|
|
int32 result = 0;
|
|
scBusBitset = 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 == kAux)
|
|
{
|
|
result += busInfo.channelCount;
|
|
scBusBitset |= (uint64 (1) << i);
|
|
// no longer activate side chains by default, use the host
|
|
// notifications instead
|
|
// mComponent->activateBus (kAudio, dir, i, true);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
tresult AAXWrapper::setupBusArrangements (AAX_Plugin_Desc* desc)
|
|
{
|
|
uint32 inputBusCount =
|
|
(desc->mInputChannels > 0 ? 1u : 0u) + (desc->mSideChainInputChannels > 0 ? 1u : 0u);
|
|
uint32 outputBusCount = (desc->mOutputChannels > 0 ? 1u : 0u);
|
|
|
|
if (AAX_Aux_Desc* aux = desc->mAuxOutputChannels)
|
|
while ((aux++)->mName)
|
|
outputBusCount++;
|
|
|
|
std::vector<SpeakerArrangement> inputs (inputBusCount);
|
|
std::vector<SpeakerArrangement> outputs (outputBusCount);
|
|
|
|
uint32 in = 0;
|
|
if (desc->mInputChannels)
|
|
inputs[in++] = numChannelsToSpeakerArrangement (desc->mInputChannels);
|
|
if (desc->mSideChainInputChannels)
|
|
inputs[in++] = numChannelsToSpeakerArrangement (desc->mSideChainInputChannels);
|
|
|
|
if (desc->mOutputChannels)
|
|
outputs[0] = numChannelsToSpeakerArrangement (desc->mOutputChannels);
|
|
|
|
if (AAX_Aux_Desc* aux = desc->mAuxOutputChannels)
|
|
for (uint32 i = 0; aux[i].mName; i++)
|
|
outputs[i + 1u] = numChannelsToSpeakerArrangement (aux[i].mChannels);
|
|
|
|
return mProcessor->setBusArrangements (inputs.data (), static_cast<int32> (inputBusCount),
|
|
outputs.data (), static_cast<int32> (outputBusCount));
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::guessActiveOutputs (float** out, uint32 num)
|
|
{
|
|
// a channel is considered inactive if the output pointer is to the
|
|
// same location as one of it's neighboring channels (ProTools seems
|
|
// to direct all inactive channels to the same output).
|
|
// FIXME: this heuristic fails for mono outputs
|
|
std::bitset<maxActiveChannels> active;
|
|
for (uint32 i = 0; i < num; i++)
|
|
{
|
|
auto prev = (i > 0 ? out[i - 1] : nullptr);
|
|
auto next = (i + 1 < num ? out[i + 1] : nullptr);
|
|
active[i] = (out[i] != prev && out[i] != next);
|
|
}
|
|
mActiveChannels = active;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::updateActiveOutputState ()
|
|
{
|
|
// some additional copying to avoid missing updates
|
|
std::bitset<maxActiveChannels> channels = mActiveChannels;
|
|
if (channels == mPropagatedChannels)
|
|
return;
|
|
mPropagatedChannels = channels;
|
|
|
|
uint32 channelPos = 0;
|
|
int32 busCount = mComponent->getBusCount (kAudio, kOutput);
|
|
for (int32 i = 0; i < busCount; i++)
|
|
{
|
|
BusInfo busInfo = {};
|
|
if (mComponent->getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue)
|
|
{
|
|
bool active = false;
|
|
for (uint32 c = 0; c < static_cast<uint32> (busInfo.channelCount); c++)
|
|
if (channels[channelPos + c])
|
|
active = true;
|
|
|
|
channelPos += busInfo.channelCount;
|
|
mComponent->activateBus (kAudio, kOutput, i, active);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::setSideChainEnable (bool enable)
|
|
{
|
|
int32 busCount = mComponent->getBusCount (kAudio, kInput);
|
|
for (int32 i = 0; i < busCount; i++)
|
|
{
|
|
BusInfo busInfo = {};
|
|
if (mComponent->getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue)
|
|
{
|
|
if (busInfo.busType == kAux)
|
|
{
|
|
mComponent->activateBus (kAudio, kInput, i, enable);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void AAXWrapper::setRenderingOffline (bool val)
|
|
{
|
|
if (val)
|
|
{
|
|
if (mVst3processMode == kOffline)
|
|
return;
|
|
mVst3processMode = kOffline;
|
|
}
|
|
else
|
|
{
|
|
if (mVst3processMode == kRealtime)
|
|
return;
|
|
mVst3processMode = kRealtime;
|
|
}
|
|
bool callStartStop = mProcessing;
|
|
if (callStartStop)
|
|
BaseWrapper::_stopProcess ();
|
|
setupProcessing ();
|
|
if (callStartStop)
|
|
BaseWrapper::_startProcess ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// Context structure
|
|
//------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------
|
|
using fnCreateParameters = AAX_IEffectParameters* AAX_CALLBACK ();
|
|
|
|
template <int32_t pluginIndex>
|
|
struct CP
|
|
{
|
|
static AAX_IEffectParameters* AAX_CALLBACK Create_Parameters ()
|
|
{
|
|
auto p = NEW AAXWrapper_Parameters (pluginIndex);
|
|
if (!p->getWrapper ())
|
|
{
|
|
delete p;
|
|
p = nullptr;
|
|
}
|
|
return p;
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------------------------
|
|
AAX_IEffectGUI* AAX_CALLBACK Create_GUI ()
|
|
{
|
|
return NEW AAXWrapper_GUI ();
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
int32_t AAX_CALLBACK AlgorithmInitFunction (const AAXWrapper_Context* /*inInstance*/,
|
|
AAX_EComponentInstanceInitAction /*inAction*/)
|
|
{
|
|
return AAX_SUCCESS;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
Steinberg::int32 AAXWrapper::ResetFieldData (Steinberg::int32 index, void* inData,
|
|
Steinberg::uint32 inDataSize)
|
|
{
|
|
if (index == idxContext && inDataSize == sizeof (AAXWrapper*))
|
|
{
|
|
_suspend ();
|
|
_resume ();
|
|
*(AAXWrapper**)inData = this;
|
|
}
|
|
else
|
|
// Default implementation is just to zero out all data.
|
|
memset (inData, 0, inDataSize);
|
|
return AAX_SUCCESS;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
Steinberg::int32 AAXWrapper::Process (AAXWrapper_Context* instance)
|
|
{
|
|
//--- ------ Retrieve instance-specific information ---------
|
|
// Memory blocks
|
|
const int32_t bufferSize = *static_cast<int32_t*> (instance->ptr[idxBufferSize]);
|
|
AAX_ASSERT (bufferSize <= 1024);
|
|
|
|
uint32 cntMidiPorts = getNumMIDIports ();
|
|
for (uint32 m = 0; m < cntMidiPorts; m++)
|
|
{
|
|
auto* midiNode = static_cast<AAX_IMIDINode*> (instance->ptr[idxMidiPorts + m]);
|
|
AAX_CMidiStream* midiBuffer = midiNode->GetNodeBuffer ();
|
|
|
|
//- Check incoming MIDI packets ()
|
|
//
|
|
for (uint32_t i = 0; i < midiBuffer->mBufferSize; i++)
|
|
{
|
|
AAX_CMidiPacket& buf = midiBuffer->mBuffer[i];
|
|
if (buf.mLength > 0)
|
|
{
|
|
// skip note-on events if bypassed to reduce processor load for instruments,
|
|
// but let everything else through to avoid hanging notes
|
|
if (mSimulateBypass && mBypass)
|
|
if ((buf.mData[0] & Vst::kStatusMask) == Vst::kNoteOn && buf.mData[2] != 0)
|
|
continue;
|
|
|
|
Event toAdd = {static_cast<int32> (m), static_cast<int32> (buf.mTimestamp), 0};
|
|
bool isLive = buf.mIsImmediate || buf.mTimestamp == 0;
|
|
processMidiEvent (toAdd, (char*)&buf.mData[0], isLive);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto pdI = idxInputChannels < 0 ?
|
|
nullptr :
|
|
static_cast<float**> (instance->ptr[idxInputChannels]); // First input
|
|
float* inputs[16] = {nullptr};
|
|
if (pdI && idxSideChainInputChannels >= 0)
|
|
{
|
|
if (auto psc = instance->ptr[idxSideChainInputChannels])
|
|
{
|
|
int32 scChannel = *static_cast<int32_t*> (psc);
|
|
|
|
int32 idx = mPluginDesc->mInputChannels;
|
|
memcpy (inputs, pdI, idx * sizeof (inputs[0]));
|
|
for (int32 i = 0; i < mPluginDesc->mSideChainInputChannels; i++)
|
|
inputs[idx + i] = pdI[scChannel];
|
|
pdI = inputs;
|
|
}
|
|
}
|
|
|
|
// First output
|
|
float** const AAX_RESTRICT pdO = static_cast<float**> (instance->ptr[idxOutputChannels]);
|
|
if (pdO == nullptr)
|
|
return AAX_ERROR_NULL_ARGUMENT;
|
|
|
|
uint32 cntOut = getNumOutputs ();
|
|
uint32 aaxOut = getNumAAXOutputs ();
|
|
float* outputs[maxActiveChannels];
|
|
uint32 mainOuts = static_cast<uint32> (mPluginDesc->mOutputChannels);
|
|
if (mainOuts == 6)
|
|
{
|
|
// sort Surround channels from AAX (L C R Ls Rs LFE) to VST (L R C LFE Ls Rs)
|
|
outputs[0] = pdO[0];
|
|
outputs[1] = pdO[2];
|
|
outputs[2] = pdO[1];
|
|
outputs[3] = pdO[5];
|
|
outputs[4] = pdO[3];
|
|
outputs[5] = pdO[4];
|
|
}
|
|
else
|
|
mainOuts = 0;
|
|
for (uint32 i = mainOuts; i < aaxOut; i++)
|
|
outputs[i] = pdO[i];
|
|
float buf[1024];
|
|
for (uint32 i = aaxOut; i < cntOut; i++)
|
|
outputs[i] = buf;
|
|
guessActiveOutputs (outputs, cntOut);
|
|
|
|
mMetersTmp = (mCntMeters > 0) ? *static_cast<float**> (instance->ptr[idxMeters]) : nullptr;
|
|
|
|
_processReplacing (pdI, outputs, bufferSize);
|
|
|
|
mMetersTmp = nullptr;
|
|
|
|
// apply bypass if not supported (only without inputs for now, i.e. instruments)
|
|
// ----------------------
|
|
if (mSimulateBypass && mPluginDesc->mInputChannels == 0)
|
|
{
|
|
static const float kDiffGain = 0.001f;
|
|
if (mBypass)
|
|
{
|
|
int32 bufPos = 0;
|
|
while (mBypassGain > 0 && bufPos < bufferSize)
|
|
{
|
|
for (uint32 i = 0; i < cntOut; i++)
|
|
outputs[i][bufPos] *= mBypassGain;
|
|
mBypassGain -= kDiffGain;
|
|
bufPos++;
|
|
}
|
|
for (uint32 i = 0; i < cntOut; i++)
|
|
memset (&outputs[i][bufPos], 0, (bufferSize - bufPos) * sizeof (float));
|
|
}
|
|
else if (mBypassGain < 1)
|
|
{
|
|
int32 bufPos = 0;
|
|
while (mBypassGain < 1 && bufPos < bufferSize)
|
|
{
|
|
for (uint32 i = 0; i < cntOut; i++)
|
|
outputs[i][bufPos] *= mBypassGain;
|
|
mBypassGain += kDiffGain;
|
|
bufPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AAX_SUCCESS;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::processOutputParametersChanges ()
|
|
{
|
|
if (!mMetersTmp)
|
|
return;
|
|
|
|
uint32 found = 0;
|
|
|
|
// VU Meter readout
|
|
for (int32 i = 0, count = mOutputChanges.getParameterCount (); i < count; i++)
|
|
{
|
|
auto queue = mOutputChanges.getParameterData (i);
|
|
if (!queue)
|
|
break;
|
|
for (uint32 m = 0; m < mCntMeters; m++)
|
|
{
|
|
if (mMeterIds[m] == queue->getParameterId ())
|
|
{
|
|
int32 sampleOffset;
|
|
ParamValue value;
|
|
queue->getPoint (queue->getPointCount () - 1, sampleOffset, value);
|
|
mMetersTmp[m] = static_cast<float> (value);
|
|
found++;
|
|
break;
|
|
}
|
|
}
|
|
if (found == mCntMeters)
|
|
break;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Steinberg::tresult PLUGIN_API AAXWrapper::restartComponent (Steinberg::int32 flags)
|
|
{
|
|
tresult result = BaseWrapper::restartComponent (flags);
|
|
|
|
//--- ----------------------
|
|
if (flags & kLatencyChanged)
|
|
{
|
|
if (mAAXParams && mProcessor)
|
|
{
|
|
AAX_IController* ctrler = mAAXParams->Controller ();
|
|
if (ctrler)
|
|
ctrler->SetSignalLatency (static_cast<int32_t> (mProcessor->getLatencySamples ()));
|
|
}
|
|
result = kResultTrue;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
static void AAX_CALLBACK AlgorithmProcessFunction (AAXWrapper_Context* const inInstancesBegin[],
|
|
const void* inInstancesEnd)
|
|
{
|
|
//--- ------ Iterate over plug-in instances ---------//
|
|
for (AAXWrapper_Context* const* walk = inInstancesBegin; walk < inInstancesEnd; ++walk)
|
|
{
|
|
AAXWrapper_Context* AAX_RESTRICT instance = *walk;
|
|
|
|
// first element is Context
|
|
AAXWrapper* wrapper = *reinterpret_cast<AAXWrapper**> (instance->ptr[0]);
|
|
if (!wrapper)
|
|
continue;
|
|
|
|
wrapper->Process (instance);
|
|
|
|
} // End instance-iteration loop
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
static uint32 vst3Category2AAXPlugInCategory (const char* cat)
|
|
{
|
|
static const uint32 PDA_ePlugInCategory_Effect =
|
|
AAX_ePlugInCategory_None; // does not exist anymore?
|
|
|
|
uint32 result = AAX_ePlugInCategory_None;
|
|
|
|
if (strstr (cat, "Fx") != nullptr)
|
|
{
|
|
result = PDA_ePlugInCategory_Effect;
|
|
}
|
|
|
|
if (strstr (cat, "Instrument") != nullptr || strstr (cat, "Generator") != nullptr)
|
|
{
|
|
if (strstr (cat, "External") != nullptr)
|
|
result |= AAX_ePlugInCategory_HWGenerators;
|
|
else
|
|
result |= AAX_ePlugInCategory_SWGenerators;
|
|
}
|
|
|
|
if (strstr (cat, "Delay") != nullptr)
|
|
result |= AAX_ePlugInCategory_Delay;
|
|
|
|
if (strstr (cat, "Distortion") != nullptr)
|
|
result |= AAX_ePlugInCategory_Harmonic;
|
|
|
|
if (strstr (cat, "Dynamics") != nullptr)
|
|
result |= AAX_ePlugInCategory_Dynamics;
|
|
|
|
if (strstr (cat, "EQ") != nullptr)
|
|
result |= AAX_ePlugInCategory_EQ;
|
|
|
|
if (strstr (cat, "Mastering") != nullptr)
|
|
result |= AAX_ePlugInCategory_Dither;
|
|
|
|
if (strstr (cat, "Modulation") != nullptr)
|
|
result |= AAX_ePlugInCategory_Modulation;
|
|
|
|
if (strstr (cat, "Pitch Shift") != nullptr)
|
|
result |= AAX_ePlugInCategory_PitchShift;
|
|
|
|
if (strstr (cat, "Restoration") != nullptr)
|
|
result |= AAX_ePlugInCategory_NoiseReduction;
|
|
|
|
if (strstr (cat, "Reverb") != nullptr)
|
|
result |= AAX_ePlugInCategory_Reverb;
|
|
|
|
if (strstr (cat, "Spatial") != nullptr || strstr (cat, "Surround") != nullptr ||
|
|
strstr (cat, "Up-Downmix") != nullptr)
|
|
{
|
|
result |= AAX_ePlugInCategory_SoundField;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
// ROUTINE: DescribeAlgorithmComponent
|
|
// Algorithm component description
|
|
//------------------------------------------------------------------------
|
|
void AAXWrapper::DescribeAlgorithmComponent (AAX_IComponentDescriptor* outDesc,
|
|
const AAX_Effect_Desc* desc,
|
|
const AAX_Plugin_Desc* pdesc)
|
|
{
|
|
// Describe algorithm's context structure
|
|
//
|
|
// Subscribe context fields to host-provided services or information
|
|
HLOG (HAPI, "%s", __FUNCTION__);
|
|
|
|
AAX_Result err = AAX_SUCCESS;
|
|
|
|
// must be in lock step with AAXWrapper ctor
|
|
int32 idx = idxBufferSize + 1;
|
|
|
|
// ProTools does not like instruments without inputs (maybe because they are treated as
|
|
// inserts?)
|
|
int32 inChannels = pdesc->mInputChannels;
|
|
if (inChannels == 0)
|
|
inChannels = pdesc->mOutputChannels;
|
|
if (inChannels)
|
|
err = outDesc->AddAudioIn (idx++);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
if (pdesc->mOutputChannels)
|
|
err = outDesc->AddAudioOut (idx++);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
err = outDesc->AddAudioBufferLength (AAXWrapper::idxBufferSize);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
if (pdesc->mSideChainInputChannels)
|
|
err = outDesc->AddSideChainIn (idx++); // max 1 side chain!?
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
if (pdesc->mMIDIports)
|
|
{
|
|
for (AAX_MIDI_Desc* mdesc = pdesc->mMIDIports; mdesc->mName; mdesc++)
|
|
{
|
|
err = outDesc->AddMIDINode (idx++, AAX_eMIDINodeType_LocalInput, mdesc->mName,
|
|
mdesc->mMask);
|
|
AAX_ASSERT (err == 0);
|
|
}
|
|
}
|
|
|
|
if (pdesc->mAuxOutputChannels)
|
|
{
|
|
for (AAX_Aux_Desc* auxdesc = pdesc->mAuxOutputChannels; auxdesc->mName; auxdesc++)
|
|
{
|
|
int32 ch = (auxdesc->mChannels < 0 ? pdesc->mOutputChannels : auxdesc->mChannels);
|
|
err = outDesc->AddAuxOutputStem (idx++, getChannelsStem (ch), auxdesc->mName);
|
|
AAX_ASSERT (err == 0);
|
|
}
|
|
}
|
|
if (pdesc->mMeters)
|
|
{
|
|
uint32 cntMeters = 0;
|
|
for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++)
|
|
cntMeters++;
|
|
std::unique_ptr<Steinberg::Vst::ParamID[]> meterIds;
|
|
meterIds.reset (NEW Steinberg::Vst::ParamID (cntMeters));
|
|
cntMeters = 0;
|
|
for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++)
|
|
meterIds[cntMeters++] = mdesc->mID;
|
|
|
|
err = outDesc->AddMeters (idx++, (AAX_CTypeID*)meterIds.get (), cntMeters);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
}
|
|
|
|
// Register context fields as private data
|
|
err = outDesc->AddPrivateData (AAXWrapper::idxContext, sizeof (void*),
|
|
AAX_ePrivateDataOptions_DefaultOptions);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
// Register processing callbacks
|
|
//
|
|
// Create a property map
|
|
AAX_IPropertyMap* properties = outDesc->NewPropertyMap ();
|
|
AAX_ASSERT (properties);
|
|
if (!properties)
|
|
return;
|
|
//
|
|
// Generic properties
|
|
properties->AddProperty (AAX_eProperty_ManufacturerID,
|
|
static_cast<AAX_CPropertyValue> (desc->mManufacturerID));
|
|
properties->AddProperty (AAX_eProperty_ProductID,
|
|
static_cast<AAX_CPropertyValue> (desc->mProductID));
|
|
properties->AddProperty (AAX_eProperty_CanBypass, true);
|
|
properties->AddProperty (AAX_eProperty_LatencyContribution,
|
|
static_cast<AAX_CPropertyValue> (pdesc->mLatency));
|
|
// properties->AddProperty (AAX_eProperty_UsesClientGUI, true); // Uses auto-GUI
|
|
|
|
//
|
|
// Stem format -specific properties
|
|
// AAX_IPropertyMap* propertiesMono = outDesc->NewPropertyMap();
|
|
// AAX_ASSERT (propertiesMono);
|
|
// if ( !propertiesMono ) return;
|
|
// properties->Clone(propertiesMono);
|
|
//
|
|
if (pdesc->mInputChannels)
|
|
properties->AddProperty (AAX_eProperty_InputStemFormat,
|
|
getChannelsStem (pdesc->mInputChannels));
|
|
else if (pdesc->mOutputChannels)
|
|
properties->AddProperty (AAX_eProperty_InputStemFormat,
|
|
getChannelsStem (pdesc->mOutputChannels));
|
|
|
|
if (pdesc->mOutputChannels)
|
|
properties->AddProperty (AAX_eProperty_OutputStemFormat,
|
|
getChannelsStem (pdesc->mOutputChannels));
|
|
if (pdesc->mSideChainInputChannels)
|
|
{
|
|
properties->AddProperty (AAX_eProperty_SupportsSideChainInput, true);
|
|
#if 0 // only mono supported, setting stem format causes plugin load failure
|
|
properties->AddProperty (AAX_eProperty_SideChainStemFormat,
|
|
getChannelsStem (pdesc->mSideChainInputChannels));
|
|
#endif
|
|
}
|
|
|
|
properties->AddProperty (AAX_eProperty_PlugInID_Native,
|
|
static_cast<AAX_CPropertyValue> (pdesc->mPlugInIDNative));
|
|
properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite,
|
|
static_cast<AAX_CPropertyValue> (pdesc->mPlugInIDAudioSuite));
|
|
|
|
// Register callbacks
|
|
// Native (Native and AudioSuite)
|
|
err = outDesc->AddProcessProc_Native<AAXWrapper_Context> (
|
|
AlgorithmProcessFunction, properties, AlgorithmInitFunction,
|
|
nullptr /*AlgorithmBackgroundFunction*/);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
static AAX_Result GetPlugInDescription (AAX_IEffectDescriptor* outDescriptor,
|
|
const AAX_Effect_Desc* desc, const AAX_Plugin_Desc* pdesc)
|
|
{
|
|
HLOG (HAPI, "%s", __FUNCTION__);
|
|
|
|
int err = AAX_SUCCESS;
|
|
AAX_IComponentDescriptor* compDesc = outDescriptor->NewComponentDescriptor ();
|
|
if (!compDesc)
|
|
return AAX_ERROR_NULL_OBJECT;
|
|
|
|
// Effect identifiers
|
|
outDescriptor->AddName (pdesc->mName);
|
|
outDescriptor->AddCategory (vst3Category2AAXPlugInCategory (desc->mCategory));
|
|
|
|
// Effect components
|
|
//
|
|
// Algorithm component
|
|
AAXWrapper::DescribeAlgorithmComponent (compDesc, desc, pdesc);
|
|
err = outDescriptor->AddComponent (compDesc);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
// Data model
|
|
int32 plugIndex = static_cast<int32> (pdesc - desc->mPluginDesc);
|
|
fnCreateParameters* fn = nullptr;
|
|
switch (plugIndex)
|
|
{
|
|
#define ADDPROCPTR(N) \
|
|
case N: fn = &CP<N>::Create_Parameters; break;
|
|
ADDPROCPTR (0)
|
|
ADDPROCPTR (1)
|
|
ADDPROCPTR (2)
|
|
ADDPROCPTR (3)
|
|
ADDPROCPTR (4)
|
|
ADDPROCPTR (5)
|
|
ADDPROCPTR (6)
|
|
ADDPROCPTR (7)
|
|
ADDPROCPTR (8)
|
|
ADDPROCPTR (9)
|
|
ADDPROCPTR (10)
|
|
ADDPROCPTR (11) ADDPROCPTR (12) ADDPROCPTR (13) ADDPROCPTR (14) ADDPROCPTR (15)
|
|
}
|
|
AAX_ASSERT (fn);
|
|
err = outDescriptor->AddProcPtr ((void*)fn, kAAX_ProcPtrID_Create_EffectParameters);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
if (desc->mPageFile)
|
|
outDescriptor->AddResourceInfo (AAX_eResourceType_PageTable, desc->mPageFile);
|
|
|
|
// Effect's meter display properties
|
|
if (pdesc->mMeters)
|
|
{
|
|
for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++)
|
|
{
|
|
AAX_IPropertyMap* meterProperties = outDescriptor->NewPropertyMap ();
|
|
if (!meterProperties)
|
|
return AAX_ERROR_NULL_OBJECT;
|
|
|
|
// Support different meter types offered by AAX here
|
|
meterProperties->AddProperty (AAX_eProperty_Meter_Type,
|
|
static_cast<AAX_CPropertyValue> (mdesc->mType));
|
|
|
|
meterProperties->AddProperty (AAX_eProperty_Meter_Orientation,
|
|
static_cast<AAX_CPropertyValue> (mdesc->mOrientation));
|
|
outDescriptor->AddMeterDescription (mdesc->mID, mdesc->mName, meterProperties);
|
|
}
|
|
}
|
|
|
|
// plugin supplied GUI
|
|
err = outDescriptor->AddProcPtr ((void*)Create_GUI, kAAX_ProcPtrID_Create_EffectGUI);
|
|
AAX_ASSERT (err == AAX_SUCCESS);
|
|
|
|
return AAX_SUCCESS;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
AAX_Result GetEffectDescriptions (AAX_ICollection* outCollection)
|
|
{
|
|
HLOG (HAPI, "%s", __FUNCTION__);
|
|
|
|
AAX_Result result = AAX_ERROR_NULL_OBJECT;
|
|
|
|
AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription ();
|
|
for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++)
|
|
{
|
|
if (AAX_IEffectDescriptor* plugInDescriptor = outCollection->NewDescriptor ())
|
|
{
|
|
result = GetPlugInDescription (plugInDescriptor, effDesc, pdesc);
|
|
if (result == AAX_SUCCESS)
|
|
result = outCollection->AddEffect (pdesc->mEffectID, plugInDescriptor);
|
|
AAX_ASSERT (result == AAX_SUCCESS);
|
|
}
|
|
}
|
|
|
|
outCollection->SetManufacturerName (effDesc->mManufacturer);
|
|
|
|
/* needed ?
|
|
char packageName[512];
|
|
sprintf (packageName, "%s AAX Plug-In", effDesc->mProduct);
|
|
outCollection->AddPackageName (packageName);
|
|
|
|
sprintf (packageName, "%s Plug-In", effDesc->mProduct);
|
|
outCollection->AddPackageName (packageName);*/
|
|
|
|
outCollection->AddPackageName (effDesc->mProduct);
|
|
|
|
if (strlen (effDesc->mProduct) > 16)
|
|
{
|
|
char packageShortName[17] = {0};
|
|
strncpy (packageShortName, effDesc->mProduct, 16);
|
|
outCollection->AddPackageName (packageShortName);
|
|
}
|
|
|
|
outCollection->SetPackageVersion (effDesc->mVersion);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// \endcond
|