1 //------------------------------------------------------------------------
2 // Project     : VST SDK
3 //
4 // Category    : Helpers
5 // Filename    : public.sdk/source/vst/aaxwrapper/aaxwrapper.cpp
6 // Created by  : Steinberg, 08/2017
7 // Description : VST 3 -> AAX Wrapper
8 //
9 //-----------------------------------------------------------------------------
10 // LICENSE
11 // (c) 2018, Steinberg Media Technologies GmbH, All Rights Reserved
12 //-----------------------------------------------------------------------------
13 // Redistribution and use in source and binary forms, with or without modification,
14 // are permitted provided that the following conditions are met:
15 //
16 //   * Redistributions of source code must retain the above copyright notice,
17 //     this list of conditions and the following disclaimer.
18 //   * Redistributions in binary form must reproduce the above copyright notice,
19 //     this list of conditions and the following disclaimer in the documentation
20 //     and/or other materials provided with the distribution.
21 //   * Neither the name of the Steinberg Media Technologies nor the names of its
22 //     contributors may be used to endorse or promote products derived from this
23 //     software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33 // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
34 // OF THE POSSIBILITY OF SUCH DAMAGE.
35 //-----------------------------------------------------------------------------
36 
37 /// \cond ignore
38 
39 #include "aaxwrapper.h"
40 
41 #include "AAX.h"
42 #include "AAX_Assert.h"
43 #include "AAX_Errors.h"
44 #include "AAX_ICollection.h"
45 #include "AAX_IComponentDescriptor.h"
46 #include "AAX_IEffectDescriptor.h"
47 #include "AAX_IMIDINode.h"
48 #include "AAX_IPropertyMap.h"
49 #include "AAX_IViewContainer.h"
50 
51 #include "aaxwrapper_description.h"
52 #include "aaxwrapper_gui.h"
53 #include "aaxwrapper_parameters.h"
54 
55 #include "base/thread/include/fcondition.h"
56 
57 #define USE_TRACE 1
58 
59 #if USE_TRACE
60 #define HAPI AAX_eTracePriorityHost_Normal
61 #define HLOG AAX_TRACE
62 
63 #else
64 #define HAPI 0
65 #if SMTG_OS_WINDOWS
66 #define HLOG __noop
67 #else
68 #define HLOG(...) \
69 	do            \
70 	{             \
71 	} while (false)
72 #endif
73 #endif
74 
75 #if SMTG_OS_WINDOWS
76 #include <windows.h>
77 #define getCurrentThread() ((void*)(size_t)GetCurrentThreadId ())
78 #else
79 #include <pthread.h>
80 #define getCurrentThread() (pthread_self ())
81 #endif
82 
83 using namespace Steinberg;
84 using namespace Steinberg::Vst;
85 using namespace Steinberg::Base::Thread;
86 
87 //------------------------------------------------------------------------
88 class AAXEditorWrapper : public BaseEditorWrapper
89 {
90 public:
AAXEditorWrapper(AAXWrapper * wrapper,IEditController * controller)91 	AAXEditorWrapper (AAXWrapper* wrapper, IEditController* controller)
92 	: BaseEditorWrapper (controller), mAAXWrapper (wrapper) {}
93 
94 	virtual tresult PLUGIN_API resizeView (IPlugView* view, ViewRect* newSize) SMTG_OVERRIDE;
95 
96 private:
97 	AAXWrapper* mAAXWrapper;
98 };
99 
100 //------------------------------------------------------------------------
resizeView(IPlugView * view,ViewRect * newSize)101 tresult PLUGIN_API AAXEditorWrapper::resizeView (IPlugView* view, ViewRect* newSize)
102 {
103 	tresult result = kResultFalse;
104 	if (view && newSize)
105 	{
106 		if (mAAXWrapper->_sizeWindow (newSize->getWidth (), newSize->getHeight ()))
107 		{
108 			result = view->onSize (newSize);
109 		}
110 	}
111 
112 	return result;
113 }
114 
115 //------------------------------------------------------------------------
116 //------------------------------------------------------------------------
AAXWrapper(BaseWrapper::SVST3Config & config,AAXWrapper_Parameters * p,AAX_Plugin_Desc * desc)117 AAXWrapper::AAXWrapper (BaseWrapper::SVST3Config& config, AAXWrapper_Parameters* p, AAX_Plugin_Desc* desc)
118 : BaseWrapper (config), aaxParams (p), pluginDesc (desc)
119 {
120 	HLOG (HAPI, "%s", __FUNCTION__);
121 
122 	mBlockSize = 1024; // never explicitly changed by Protools, so assume the maximum
123 	mUseExportedBypass = true;
124 	mUseIncIndex = false;
125 
126 	mainThread = getCurrentThread ();
127 
128 	// must be in lock step with DescribeAlgorithmComponent
129 	int32 idx = idxBufferSize + 1;
130 	if (desc->mInputChannels || desc->mOutputChannels)
131 		idxInputChannels = idx++;
132 	if (desc->mOutputChannels)
133 		idxOutputChannels = idx++;
134 	if (desc->mSideChainInputChannels)
135 		idxSideChainInputChannels = idx++;
136 
137 	if (desc->mMIDIports)
138 	{
139 		for (AAX_MIDI_Desc* mdesc = desc->mMIDIports; mdesc->mName; mdesc++)
140 			countMIDIports++;
141 
142 		if (countMIDIports > 0)
143 		{
144 			idxMidiPorts = idx;
145 			idx += countMIDIports;
146 		}
147 	}
148 
149 	int32 numAuxOutputs = 0;
150 	aaxOutputs = desc->mOutputChannels;
151 	if (desc->mAuxOutputChannels)
152 	{
153 		for (AAX_Aux_Desc *adesc = desc->mAuxOutputChannels; adesc->mName; adesc++, numAuxOutputs++)
154 			aaxOutputs += adesc->mChannels < 0 ? desc->mOutputChannels : adesc->mChannels;
155 
156 		if (numAuxOutputs > 0)
157 		{
158 			idxAuxOutputs = idx;
159 			idx += numAuxOutputs;
160 		}
161 	}
162 
163 	if (desc->mMeters)
164 	{
165 		idxMeters = idx++;
166 
167 		for (auto mdesc = desc->mMeters; mdesc->mName; mdesc++)
168 			cntMeters++;
169 		meterIds.reset (new Steinberg::int32 (cntMeters));
170 		cntMeters = 0;
171 		for (auto mdesc = desc->mMeters; mdesc->mName; mdesc++)
172 			meterIds[cntMeters++] = mdesc->mID;
173 	}
174 
175 	numDataPointers = idx;
176 }
177 
178 //------------------------------------------------------------------------
~AAXWrapper()179 AAXWrapper::~AAXWrapper ()
180 {
181 	HLOG (HAPI, "%s", __FUNCTION__);
182 }
183 
184 //------------------------------------------------------------------------
getName(String128 name)185 tresult PLUGIN_API AAXWrapper::getName (String128 name)
186 {
187 	String str ("AAXWrapper");
188 	str.copyTo16 (name, 0, 127);
189 	return kResultTrue;
190 }
191 
192 //------------------------------------------------------------------------
getVstParamID(AAX_CParamID aaxid)193 Vst::ParamID getVstParamID (AAX_CParamID aaxid)
194 {
195 	if (aaxid[0] != 'p')
196 		return -1;
197 	Vst::ParamID id;
198 	if (sscanf (aaxid + 1, "%x", &id) != 1)
199 		return -1;
200 	return id;
201 }
202 
203 //------------------------------------------------------------------------
204 // IComponentHandler
205 //------------------------------------------------------------------------
beginEdit(ParamID tag)206 tresult PLUGIN_API AAXWrapper::beginEdit (ParamID tag)
207 {
208 	HLOG (HAPI, "%s(tag=%x)", __FUNCTION__, tag);
209 
210 	AAX_CID aaxid (tag);
211 	aaxParams->TouchParameter (aaxid);
212 	return kResultTrue;
213 }
214 
215 //------------------------------------------------------------------------
performEdit(ParamID tag,ParamValue valueNormalized)216 tresult PLUGIN_API AAXWrapper::performEdit (ParamID tag, ParamValue valueNormalized)
217 {
218 	HLOG (HAPI, "%s(tag=%x, value=%lf)", __FUNCTION__, tag, valueNormalized);
219 
220 	AAX_CID aaxid (tag);
221 	aaxParams->SetParameterNormalizedValue (aaxid, valueNormalized);
222 	return kResultTrue;
223 }
224 
225 //------------------------------------------------------------------------
endEdit(ParamID tag)226 tresult PLUGIN_API AAXWrapper::endEdit (ParamID tag)
227 {
228 	HLOG (HAPI, "%s(tag=%x)", __FUNCTION__, tag);
229 
230 	AAX_CID aaxid (tag);
231 	aaxParams->ReleaseParameter (aaxid);
232 	return kResultTrue;
233 }
234 
235 //------------------------------------------------------------------------
setDirty(TBool state)236 tresult PLUGIN_API AAXWrapper::setDirty (TBool state)
237 {
238 	aaxParams->setDirty (state != 0);
239 	return kResultOk;
240 }
241 
242 //------------------------------------------------------------------------
requestOpenEditor(FIDString name)243 tresult PLUGIN_API AAXWrapper::requestOpenEditor (FIDString name)
244 {
245 	return kResultFalse;
246 }
247 
248 //------------------------------------------------------------------------
startGroupEdit()249 tresult PLUGIN_API AAXWrapper::startGroupEdit ()
250 {
251 	return kResultFalse;
252 }
253 
254 //------------------------------------------------------------------------
finishGroupEdit()255 tresult PLUGIN_API AAXWrapper::finishGroupEdit ()
256 {
257 	return kResultFalse;
258 }
259 
260 //------------------------------------------------------------------------
init()261 bool AAXWrapper::init ()
262 {
263 	bool res = BaseWrapper::init ();
264 
265 	if (mController && BaseEditorWrapper::hasEditor (mController))
266 		_setEditor (new AAXEditorWrapper (this, mController));
267 
268 	return res;
269 }
270 
271 //------------------------------------------------------------------------
setupProcessTimeInfo()272 void AAXWrapper::setupProcessTimeInfo ()
273 {
274 	mProcessContext.state = 0;
275 	mProcessContext.sampleRate = mSampleRate;
276 
277 	if (AAX_ITransport* transport = aaxParams->Transport ())
278 	{
279 		int64_t splPos, ppqPos, loopStart, loopEnd;
280 		bool playing, looping;
281 		if (transport->GetCurrentNativeSampleLocation (&splPos) == AAX_SUCCESS)
282 			mProcessContext.projectTimeSamples = (TSamples)splPos;
283 
284 		if (transport->GetCurrentTickPosition (&ppqPos) == AAX_SUCCESS)
285 		{
286 			mProcessContext.projectTimeMusic = ppqPos / 960000.0;
287 			mProcessContext.state |= ProcessContext::kProjectTimeMusicValid;
288 		}
289 		else
290 			mProcessContext.projectTimeMusic = 0;
291 
292 		if (transport->GetCurrentTempo (&mProcessContext.tempo) == AAX_SUCCESS)
293 			mProcessContext.state |= ProcessContext::kTempoValid;
294 
295 		if (transport->GetCurrentLoopPosition (&looping, &loopStart, &loopEnd) == AAX_SUCCESS)
296 		{
297 			mProcessContext.cycleStartMusic = loopStart / 960000.0;
298 			mProcessContext.cycleEndMusic = loopEnd / 960000.0;
299 			mProcessContext.state |= ProcessContext::kCycleValid;
300 			if (looping)
301 				mProcessContext.state |= ProcessContext::kCycleActive;
302 		}
303 
304 		if (transport->IsTransportPlaying (&playing) == AAX_SUCCESS)
305 		{
306 			mProcessContext.state |= (playing ? ProcessContext::kPlaying : 0);
307 		}
308 
309 		// workaround ppqPos not updating for every 2nd audio blocks @ 96 kHz (and more for higher frequencies)
310 		//  and while the UI is frozen, e.g. during save
311 		static const int32 playflags = ProcessContext::kPlaying | ProcessContext::kProjectTimeMusicValid | ProcessContext::kTempoValid;
312 		if ((playflags & mProcessContext.state) == playflags && mSampleRate != 0)
313 		{
314 			TQuarterNotes ppq = mProcessContext.projectTimeMusic;
315 			if (ppq == mLastPpqPos && mLastPpqPos != 0 && mNextPpqPos != 0)
316 			{
317 				TQuarterNotes nextppq = mNextPpqPos;
318 				if (mProcessContext.state & ProcessContext::kCycleActive)
319 					if (nextppq >= mProcessContext.cycleEndMusic)
320 						nextppq += mProcessContext.cycleStartMusic - mProcessContext.cycleEndMusic;
321 
322 				mProcessContext.projectTimeMusic = nextppq;
323 			}
324 			mLastPpqPos = ppq;
325 			mNextPpqPos = mProcessContext.projectTimeMusic + mProcessContext.tempo / 60 * mProcessData.numSamples / mSampleRate;
326 		}
327 		else
328 		{
329 			mLastPpqPos = mNextPpqPos = 0;
330 		}
331 
332 		int32_t num, den;
333 		if (transport->GetCurrentMeter (&num, &den) == AAX_SUCCESS)
334 		{
335 			mProcessContext.timeSigNumerator = num;
336 			mProcessContext.timeSigDenominator = den;
337 			mProcessContext.state |= ProcessContext::kTimeSigValid;
338 		}
339 		else
340 			mProcessContext.timeSigNumerator = mProcessContext.timeSigDenominator = 4;
341 
342 		mProcessData.processContext = &mProcessContext;
343 	}
344 	else
345 		mProcessData.processContext = nullptr;
346 }
347 
348 //------------------------------------------------------------------------
_sizeWindow(int32 width,int32 height)349 bool AAXWrapper::_sizeWindow (int32 width, int32 height)
350 {
351 	HLOG (HAPI, "%s(width=%x, height=%x)", __FUNCTION__, width, height);
352 
353 	AAX_ASSERT (mainThread == getCurrentThread ());
354 
355 	if (aaxGUI)
356 	{
357 		if (AAX_IViewContainer* vc = aaxGUI->GetViewContainer ())
358 		{
359 			AAX_Point inSize (height, width);
360 			if (vc->SetViewSize (inSize) == AAX_SUCCESS)
361 			{
362 				return true;
363 			}
364 		}
365 	}
366 	return false;
367 }
368 
369 //------------------------------------------------------------------------
370 struct AAXWrapper::GetChunkMessage : public FCondition
371 {
372 	void* mData = nullptr;
373 	int32 mDataSize = 0;
374 	int32 mResult = 0;
375 };
376 
377 //------------------------------------------------------------------------
_getChunk(void ** data,bool isPreset)378 int32 AAXWrapper::_getChunk (void** data, bool isPreset)
379 {
380 	if (wantsSetChunk)
381 	{
382 		// isPreset is always false for AAX, so we can ignore it
383 		*data = mChunk.getData ();
384 		return mChunk.getSize ();
385 	}
386 	if (mainThread == getCurrentThread ())
387 		return BaseWrapper::_getChunk (data, isPreset);
388 
389 	auto* msg = NEW GetChunkMessage;
390 	{
391 		FGuard guard (msgQueueLock);
392 		msgQueue.push_back (msg);
393 	}
394 	msg->wait ();
395 
396 	*data = msg->mData;
397 	return msg->mResult;
398 }
399 
400 //------------------------------------------------------------------------
_setChunk(void * data,int32 byteSize,bool isPreset)401 int32 AAXWrapper::_setChunk (void* data, int32 byteSize, bool isPreset)
402 {
403 	if (mainThread == getCurrentThread ())
404 		return BaseWrapper::_setChunk (data, byteSize, isPreset);
405 
406 	FGuard guard (msgQueueLock);
407 	mChunk.setSize (byteSize);
408 	memcpy (mChunk.getData (), data, byteSize);
409 	wantsSetChunk = true;
410 	return 0;
411 }
412 
413 //------------------------------------------------------------------------
onTimer(Timer * timer)414 void AAXWrapper::onTimer (Timer* timer)
415 {
416 	BaseWrapper::onTimer (timer);
417 
418 	AAX_ASSERT (mainThread == getCurrentThread ());
419 
420 	if (wantsSetChunk && !settingChunk)
421 	{
422 		settingChunk = true;
423 		FGuard guard (msgQueueLock);
424 		BaseWrapper::_setChunk (mChunk.getData (), mChunk.getSize (), false);
425 		wantsSetChunk = false;
426 		settingChunk = false;
427 
428 		if (mPresetChanged)
429 		{
430 			int32_t numParams;
431 			if (aaxParams->GetNumberOfParameters (&numParams) == AAX_SUCCESS)
432 			{
433 				for (auto i = 0; i < numParams; i++)
434 				{
435 					AAX_CString id;
436 					if (aaxParams->GetParameterIDFromIndex (i, &id) == AAX_SUCCESS)
437 					{
438 						double value;
439 						if (aaxParams->GetParameterNormalizedValue (id.CString (), &value) ==
440 						    AAX_SUCCESS)
441 						{
442 							aaxParams->SetParameterNormalizedValue (id.CString (), value);
443 						}
444 					}
445 				}
446 			}
447 			mPresetChanged = false;
448 		}
449 	}
450 
451 	updateActiveOutputState ();
452 
453 	while (!msgQueue.empty ())
454 	{
455 		GetChunkMessage* msg = nullptr;
456 		{
457 			FGuard guard (msgQueueLock);
458 			if (!msgQueue.empty ())
459 			{
460 				msg = msgQueue.front ();
461 				msgQueue.pop_front ();
462 			}
463 		}
464 		if (msg)
465 		{
466 			msg->mResult = BaseWrapper::_getChunk (&msg->mData, false);
467 			msg->signal ();
468 		}
469 	}
470 }
471 
472 //------------------------------------------------------------------------
getParameterInfo(AAX_CParamID aaxId,Vst::ParameterInfo & paramInfo)473 int32 AAXWrapper::getParameterInfo (AAX_CParamID aaxId, Vst::ParameterInfo& paramInfo)
474 {
475 	HLOG (HAPI, "%s(id=%s)", __FUNCTION__, aaxId);
476 
477 	Vst::ParamID id = getVstParamID (aaxId);
478 	if (id == -1)
479 		return AAX_ERROR_INVALID_PARAMETER_ID;
480 
481 	std::map<ParamID, int32>::iterator iter = mParamIndexMap.find (id);
482 	if (iter == mParamIndexMap.end ())
483 		return AAX_ERROR_INVALID_PARAMETER_ID;
484 
485 	if (mController->getParameterInfo (iter->second, paramInfo) != kResultTrue)
486 		return AAX_ERROR_INVALID_PARAMETER_ID;
487 
488 	return AAX_SUCCESS;
489 }
490 
491 //------------------------------------------------------------------------
generatePageTables(const char * outputFile)492 bool AAXWrapper::generatePageTables (const char* outputFile)
493 {
494 #if 0 && DEVELOPMENT
495 	FILE* fh = fopen (outputFile, "w");
496 	if (!fh)
497 		return false;
498 
499 	fprintf (fh, "<?xml version='1.0' encoding='US-ASCII' standalone='yes'?>\n"
500 				 "<PageTables vers='6.4.0.89'>\n"
501 					"\t<PageTableLayouts>\n");
502 
503 	AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription ();
504 	for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++)
505 	{
506 		uint32_t manId = effDesc->mManufacturerID; SWAP_32 (manId);
507 		uint32_t prodId = effDesc->mProductID; SWAP_32 (prodId);
508 		uint32_t plugId = pdesc->mPlugInIDNative; SWAP_32 (plugId);
509 		fprintf (fh,	"\t\t<Plugin manID='%4.4s' prodID='%4.4s' plugID='%4.4s'>\n"
510 							"\t\t\t<Desc>%s</Desc>\n"
511 							"\t\t\t<Layout>PageTable 1</Layout>\n"
512 							"\t\t</Plugin>",
513 				&manId, &prodId, &plugId, pdesc->mName);
514 		fprintf (fh,		"\t\t\t<PageTable type='PgTL' pgsz='1'>\n");
515 
516 		int32 cntParams = controller->getParameterCount ();
517 		for (int32 i = 0; i < cntParams; i++)
518 		{
519 			Vst::ParameterInfo info;
520 			if (controller->getParameterInfo (i, info) == kResultTrue &&
521 				(info.flags & ParameterInfo::kCanAutomate))
522 			{
523 				AAX_CID id (info.id);
524 				String stitle (info.shortTitle);
525 				fprintf (fh,	"\t\t\t\t<Page num='%d'><ID>%s</ID></Page><!-- %s -->\n", i + 1, (const char*) id, stitle.text8 ());
526 			}
527 		}
528 		fprintf (fh, "\t\t\t</PageTable>\n");
529 	}
530 
531 	fprintf (fh, "\t</PageTableLayouts>\n");
532 	fprintf (fh, "\t</PageTableLayouts>\n");
533 	fprintf (fh, "\t<Editor vers='1.1.0.1'>\n"
534 					"\t\t<PluginList>\n"
535 						"\t\t\t<RTAS>\n");
536 	for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++)
537 	{
538 		uint32_t manId = effDesc->mManufacturerID; SWAP_32 (manId);
539 		uint32_t prodId = effDesc->mProductID; SWAP_32 (prodId);
540 		uint32_t plugId = pdesc->mPlugInIDNative; SWAP_32 (plugId);
541 		fprintf (fh,	"\t\t\t\t<PluginID manID='%4.4s' prodID='%4.4s' plugID='%4.4s'>\n"
542 							"\t\t\t\t\t<MenuStr>%s</MenuStr>\n"
543 						"\t\t\t\t</PluginID>\n",
544 				&manId, &prodId, &plugId, pdesc->mName);
545 	}
546 	fprintf (fh,	"\t\t\t</RTAS>\n"
547 				"\t\t</PluginList>\n"
548 			"\t</Editor>\n");
549 	fprintf (fh, "</PageTables>\n");
550 
551 	fclose (fh);
552 #endif
553 	return true;
554 }
555 
556 //------------------------------------------------------------------------
getChannelsStem(int32 channels)557 static int32 getChannelsStem (int32 channels)
558 {
559 	switch (channels)
560 	{
561 		case 1: return AAX_eStemFormat_Mono;
562 		case 2: return AAX_eStemFormat_Stereo;
563 		case 3: return AAX_eStemFormat_LCR;
564 		case 4:
565 			return AAX_eStemFormat_Ambi_1_ACN; //  AAX_eStemFormat_Quad
566 		case 5: return AAX_eStemFormat_5_0;
567 		case 6:
568 			return AAX_eStemFormat_5_1; // AAX_eStemFormat_6_0
569 		case 7:
570 			return AAX_eStemFormat_6_1; // AAX_eStemFormat_7_0_DTS
571 		case 8: return AAX_eStemFormat_7_1_DTS;
572 		case 9:
573 			return AAX_eStemFormat_Ambi_2_ACN; // AAX_eStemFormat_7_0_2;
574 		case 10: return AAX_eStemFormat_7_1_2;
575 		case 16: return AAX_eStemFormat_Ambi_3_ACN;
576 	}
577 	return AAX_eStemFormat_None;
578 }
579 
580 //-------------------------------------------------------------------------------
numChannelsToSpeakerArrangement(int32 numChannels)581 static SpeakerArrangement numChannelsToSpeakerArrangement (int32 numChannels)
582 {
583 	switch (numChannels)
584 	{
585 		case 1: return SpeakerArr::kMono;
586 		case 2: return SpeakerArr::kStereo;
587 		case 3: return SpeakerArr::k30Cine;
588 		case 4: return SpeakerArr::kAmbi1stOrderACN;
589 		case 5: return SpeakerArr::k50;
590 		case 6: return SpeakerArr::k51;
591 		case 7: return SpeakerArr::k61Cine;
592 		case 8: return SpeakerArr::k71Cine;
593 		case 9: return SpeakerArr::kAmbi2cdOrderACN;
594 		case 10: return SpeakerArr::k71_2;
595 		case 16: return SpeakerArr::kAmbi3rdOrderACN;
596 	}
597 	return 0;
598 }
599 
600 //------------------------------------------------------------------------
601 // static
602 //------------------------------------------------------------------------
create(IPluginFactory * factory,const TUID vst3ComponentID,AAX_Plugin_Desc * desc,AAXWrapper_Parameters * params)603 AAXWrapper* AAXWrapper::create (IPluginFactory* factory, const TUID vst3ComponentID,
604                                 AAX_Plugin_Desc* desc, AAXWrapper_Parameters* params)
605 {
606 	// mostly a copy of BaseWrapper::create
607 	if (!factory)
608 		return nullptr;
609 
610 	BaseWrapper::SVST3Config config;
611 	config.processor = nullptr;
612 	config.factory = factory;
613 
614 	FReleaser factoryReleaser (factory);
615 
616 	factory->createInstance (vst3ComponentID, IAudioProcessor::iid, (void**)&config.processor);
617 	if (!config.processor)
618 		return nullptr;
619 
620 	config.controller = nullptr;
621 	if (config.processor->queryInterface (IEditController::iid, (void**)&config.controller) != kResultTrue)
622 	{
623 		FUnknownPtr<IComponent> component (config.processor);
624 		if (component)
625 		{
626 			TUID editorCID;
627 			if (component->getControllerClassId (editorCID) == kResultTrue)
628 			{
629 				factory->createInstance (editorCID, IEditController::iid, (void**)&config.controller);
630 			}
631 		}
632 	}
633 	config.vst3ComponentID = FUID::fromTUID (vst3ComponentID);
634 
635 	auto* wrapper = new AAXWrapper (config, params, desc);
636 	if (wrapper->init () == false || wrapper->setupBusArrangements (desc) != kResultOk)
637 	{
638 		wrapper->release ();
639 		return nullptr;
640 	}
641 	wrapper->setupBuses (); // again to adjust to changes done by setupBusArrangements
642 
643 	// vstwrapper ignores side chain channels, pretend they are main inputs
644 	uint64 scBusChannels;
645 	wrapper->countSidechainBusChannels (kInput, scBusChannels);
646 	wrapper->mMainAudioInputBuses |= scBusChannels;
647 
648 	FUnknownPtr<IPluginFactory2> factory2 (factory);
649 	if (factory2)
650 	{
651 		PFactoryInfo factoryInfo;
652 		if (factory2->getFactoryInfo (&factoryInfo) == kResultTrue)
653 			wrapper->setVendorName (factoryInfo.vendor);
654 
655 		for (int32 i = 0; i < factory2->countClasses (); i++)
656 		{
657 			Steinberg::PClassInfo2 classInfo2;
658 			if (factory2->getClassInfo2 (i, &classInfo2) == Steinberg::kResultTrue)
659 			{
660 				if (memcmp (classInfo2.cid, vst3ComponentID, sizeof (TUID)) == 0)
661 				{
662 					wrapper->setSubCategories (classInfo2.subCategories);
663 					wrapper->setEffectName (classInfo2.name);
664 
665 					if (classInfo2.vendor[0] != 0)
666 						wrapper->setVendorName (classInfo2.vendor);
667 
668 					break;
669 				}
670 			}
671 		}
672 	}
673 
674 	return wrapper;
675 }
676 
677 //-------------------------------------------------------------------------------
countSidechainBusChannels(BusDirection dir,uint64 & scBusBitset)678 int32 AAXWrapper::countSidechainBusChannels (BusDirection dir, uint64& scBusBitset)
679 {
680 	int32 result = 0;
681 	scBusBitset = 0;
682 
683 	int32 busCount = mComponent->getBusCount (kAudio, dir);
684 	for (int32 i = 0; i < busCount; i++)
685 	{
686 		BusInfo busInfo = {0};
687 		if (mComponent->getBusInfo (kAudio, dir, i, busInfo) == kResultTrue)
688 		{
689 			if (busInfo.busType == kAux)
690 			{
691 				result += busInfo.channelCount;
692 				scBusBitset |= (uint64 (1) << i);
693 				// no longer activate side chains by default, use the host
694 				// notifications instead
695 				// mComponent->activateBus (kAudio, dir, i, true);
696 			}
697 		}
698 	}
699 	return result;
700 }
701 
702 //------------------------------------------------------------------------
setupBusArrangements(AAX_Plugin_Desc * desc)703 tresult AAXWrapper::setupBusArrangements (AAX_Plugin_Desc* desc)
704 {
705 	int32 inputBusCount =
706 	    (desc->mInputChannels > 0 ? 1 : 0) + (desc->mSideChainInputChannels > 0 ? 1 : 0);
707 	int32 outputBusCount = (desc->mOutputChannels > 0 ? 1 : 0);
708 
709 	if (AAX_Aux_Desc* aux = desc->mAuxOutputChannels)
710 		while ((aux++)->mName)
711 			outputBusCount++;
712 
713 	std::vector<SpeakerArrangement> inputs (inputBusCount);
714 	std::vector<SpeakerArrangement> outputs (outputBusCount);
715 
716 	int32 in = 0;
717 	if (desc->mInputChannels)
718 		inputs[in++] = numChannelsToSpeakerArrangement (desc->mInputChannels);
719 	if (desc->mSideChainInputChannels)
720 		inputs[in++] = numChannelsToSpeakerArrangement (desc->mSideChainInputChannels);
721 
722 	if (desc->mOutputChannels)
723 		outputs[0] = numChannelsToSpeakerArrangement (desc->mOutputChannels);
724 
725 	if (AAX_Aux_Desc* aux = desc->mAuxOutputChannels)
726 		for (int32 i = 0; aux[i].mName; i++)
727 			outputs[i + 1] = numChannelsToSpeakerArrangement (aux[i].mChannels);
728 
729 	return mProcessor->setBusArrangements (inputs.data (), inputBusCount, outputs.data (),
730 	                                       outputBusCount);
731 }
732 
733 //------------------------------------------------------------------------
guessActiveOutputs(float ** out,int32 num)734 void AAXWrapper::guessActiveOutputs (float** out, int32 num)
735 {
736 	// a channel is considered inactive if the output pointer is to the
737 	// same location as one of it's neighboring channels (ProTools seems
738 	// to direct all inactive channels to the same output).
739 	// FIXME: this heuristic fails for mono outputs
740 	std::bitset<maxActiveChannels> active;
741 	for (int32 i = 0; i < num; i++)
742 	{
743 		auto prev = (i > 0 ? out[i - 1] : nullptr);
744 		auto next = (i + 1 < num ? out[i + 1] : nullptr);
745 		active[i] = (out[i] != prev && out[i] != next);
746 	}
747 	activeChannels = active;
748 }
749 
750 //------------------------------------------------------------------------
updateActiveOutputState()751 void AAXWrapper::updateActiveOutputState ()
752 {
753 	// some additional copying to avoid missing updates
754 	std::bitset<maxActiveChannels> channels = activeChannels;
755 	if (channels == propagatedChannels)
756 		return;
757 	propagatedChannels = channels;
758 
759 	int32 channelPos = 0;
760 	int32 busCount = mComponent->getBusCount (kAudio, kOutput);
761 	for (int32 i = 0; i < busCount; i++)
762 	{
763 		BusInfo busInfo = {0};
764 		if (mComponent->getBusInfo (kAudio, kOutput, i, busInfo) == kResultTrue)
765 		{
766 			bool active = false;
767 			for (int32 c = 0; c < busInfo.channelCount; c++)
768 				if (channels[channelPos + c])
769 					active = true;
770 
771 			channelPos += busInfo.channelCount;
772 			mComponent->activateBus (kAudio, kOutput, i, active);
773 		}
774 	}
775 }
776 
777 //------------------------------------------------------------------------
setSideChainEnable(bool enable)778 void AAXWrapper::setSideChainEnable (bool enable)
779 {
780 	int32 busCount = mComponent->getBusCount (kAudio, kInput);
781 	for (int32 i = 0; i < busCount; i++)
782 	{
783 		BusInfo busInfo = {0};
784 		if (mComponent->getBusInfo (kAudio, kInput, i, busInfo) == kResultTrue)
785 		{
786 			if (busInfo.busType == kAux)
787 			{
788 				mComponent->activateBus (kAudio, kInput, i, enable);
789 				break;
790 			}
791 		}
792 	}
793 }
794 
795 //------------------------------------------------------------------------
796 // Context structure
797 //------------------------------------------------------------------------
798 
799 //------------------------------------------------------------------------
800 using fnCreateParameters = AAX_IEffectParameters* AAX_CALLBACK ();
801 
802 template <int32_t pluginIndex>
803 struct CP
804 {
Create_ParametersCP805 	static AAX_IEffectParameters* AAX_CALLBACK Create_Parameters ()
806 	{
807 		auto p = new AAXWrapper_Parameters (pluginIndex);
808 		if (!p->getWrapper ())
809 		{
810 			delete p;
811 			p = nullptr;
812 		}
813 		return p;
814 	}
815 };
816 
817 //------------------------------------------------------------------------
Create_GUI()818 AAX_IEffectGUI* AAX_CALLBACK Create_GUI ()
819 {
820 	return new AAXWrapper_GUI ();
821 }
822 
823 //------------------------------------------------------------------------
AlgorithmInitFunction(const AAXWrapper_Context * inInstance,AAX_EComponentInstanceInitAction inAction)824 int32_t AAX_CALLBACK AlgorithmInitFunction (const AAXWrapper_Context* inInstance,
825                                             AAX_EComponentInstanceInitAction inAction)
826 {
827 	return AAX_SUCCESS;
828 }
829 
830 //------------------------------------------------------------------------
ResetFieldData(Steinberg::int32 index,void * inData,Steinberg::uint32 inDataSize)831 Steinberg::int32 AAXWrapper::ResetFieldData (Steinberg::int32 index, void* inData,
832                                              Steinberg::uint32 inDataSize)
833 {
834 	if (index == idxContext && inDataSize == sizeof (AAXWrapper*))
835 	{
836 		_suspend ();
837 		_resume ();
838 		*(AAXWrapper**)inData = this;
839 	}
840 	else
841 		// Default implementation is just to zero out all data.
842 		memset (inData, 0, inDataSize);
843 	return AAX_SUCCESS;
844 }
845 
846 //------------------------------------------------------------------------
Process(AAXWrapper_Context * instance)847 Steinberg::int32 AAXWrapper::Process (AAXWrapper_Context* instance)
848 {
849 	//--- ------ Retrieve instance-specific information ---------
850 	// Memory blocks
851 	const int32_t bufferSize = *static_cast<int32_t*> (instance->ptr[idxBufferSize]);
852 	AAX_ASSERT (bufferSize <= 1024);
853 
854 	int32 cntMidiPorts = getNumMIDIports ();
855 	for (int32 m = 0; m < cntMidiPorts; m++)
856 	{
857 		auto* midiNode = static_cast<AAX_IMIDINode*> (instance->ptr[idxMidiPorts + m]);
858 		AAX_CMidiStream* midiBuffer = midiNode->GetNodeBuffer ();
859 
860 		//- Check incoming MIDI packets ()
861 		//
862 		for (uint32_t i = 0; i < midiBuffer->mBufferSize; i++)
863 		{
864 			AAX_CMidiPacket& buf = midiBuffer->mBuffer[i];
865 			if (buf.mLength > 0)
866 			{
867 				Event toAdd = { m, static_cast<int32>(buf.mTimestamp), 0 };
868 				bool isLive = buf.mIsImmediate || buf.mTimestamp == 0;
869 				processMidiEvent (toAdd, (char*)&buf.mData[0], isLive);
870 			}
871 		}
872 	}
873 
874 	auto pdI = idxInputChannels < 0 ?
875 	               nullptr :
876 	               static_cast<float**> (instance->ptr[idxInputChannels]); // First input
877 	float* inputs[16] = {nullptr};
878 	if (pdI && idxSideChainInputChannels >= 0)
879 	{
880 		if (auto psc = instance->ptr[idxSideChainInputChannels])
881 		{
882 			int32 scChannel = *static_cast<int32_t*> (psc);
883 
884 			int32 idx = pluginDesc->mInputChannels;
885 			memcpy (inputs, pdI, idx * sizeof (inputs[0]));
886 			for (int32 i = 0; i < pluginDesc->mSideChainInputChannels; i++)
887 				inputs[idx + i] = pdI[scChannel];
888 			pdI = inputs;
889 		}
890 	}
891 
892 	// First output
893 	float** const AAX_RESTRICT pdO = static_cast<float**> (instance->ptr[idxOutputChannels]);
894 
895 	int32 cntOut = getNumOutputs ();
896 	int32 aaxOut = getNumAAXOutputs ();
897 	float* outputs[maxActiveChannels];
898 	int32 mainOuts = pluginDesc->mOutputChannels;
899 	if (mainOuts == 6)
900 	{
901 		// sort Surround channels from AAX (L C R Ls Rs LFE) to VST (L R C LFE Ls Rs)
902 		outputs[0] = pdO[0];
903 		outputs[1] = pdO[2];
904 		outputs[2] = pdO[1];
905 		outputs[3] = pdO[5];
906 		outputs[4] = pdO[3];
907 		outputs[5] = pdO[4];
908 	}
909 	else
910 		mainOuts = 0;
911 	for (int32 i = mainOuts; i < aaxOut; i++)
912 		outputs[i] = pdO[i];
913 	float buf[1024];
914 	for (int32 i = aaxOut; i < cntOut; i++)
915 		outputs[i] = buf;
916 	guessActiveOutputs (outputs, cntOut);
917 
918 	mMetersTmp = (cntMeters > 0) ? *static_cast<float**> (instance->ptr[idxMeters]) : nullptr;
919 
920 	_processReplacing (pdI, outputs, bufferSize);
921 
922 	mMetersTmp = nullptr;
923 
924 	// apply bypass if not supported ----------------------
925 	if (mSimulateBypass)
926 	{
927 		static const float kDiffGain = 0.001f;
928 		if (mBypass)
929 		{
930 			int32 bufPos = 0;
931 			while (mBypassGain > 0 && bufPos < bufferSize)
932 			{
933 				for (int32 i = 0; i < cntOut; i++)
934 					outputs[i][bufPos] *= mBypassGain;
935 				mBypassGain -= kDiffGain;
936 				bufPos++;
937 			}
938 			for (int32 i = 0; i < cntOut; i++)
939 				memset (&outputs[i][bufPos], 0, (bufferSize - bufPos) * sizeof (float));
940 		}
941 		else if (mBypassGain < 1)
942 		{
943 			int32 bufPos = 0;
944 			while (mBypassGain < 1 && bufPos < bufferSize)
945 			{
946 				for (int32 i = 0; i < cntOut; i++)
947 					outputs[i][bufPos] *= mBypassGain;
948 				mBypassGain += kDiffGain;
949 				bufPos++;
950 			}
951 		}
952 	}
953 
954 	return AAX_SUCCESS;
955 }
956 
957 //------------------------------------------------------------------------
processOutputParametersChanges()958 void AAXWrapper::processOutputParametersChanges ()
959 {
960 	if (!mMetersTmp)
961 		return;
962 
963 	uint32 found = 0;
964 
965 	// VU Meter readout
966 	for (uint32 i = 0, count = mOutputChanges.getParameterCount (); i < count; i++)
967 	{
968 		auto queue = mOutputChanges.getParameterData (i);
969 		if (!queue)
970 			break;
971 		for (int32 m = 0; m < cntMeters; m++)
972 		{
973 			if (meterIds[m] == queue->getParameterId ())
974 			{
975 				int32 sampleOffset;
976 				ParamValue value;
977 				queue->getPoint (queue->getPointCount () - 1, sampleOffset, value);
978 				mMetersTmp[m] = value;
979 				found++;
980 				break;
981 			}
982 		}
983 		if (found == cntMeters)
984 			break;
985 	}
986 }
987 
988 //-----------------------------------------------------------------------------
restartComponent(Steinberg::int32 flags)989 Steinberg::tresult PLUGIN_API AAXWrapper::restartComponent (Steinberg::int32 flags)
990 {
991 	tresult result = BaseWrapper::restartComponent (flags);
992 
993 	//--- ----------------------
994 	if (flags & kLatencyChanged)
995 	{
996 		if (aaxParams && mProcessor)
997 		{
998 			AAX_IController* ctrler = aaxParams->Controller ();
999 			if (ctrler)
1000 				ctrler->SetSignalLatency (mProcessor->getLatencySamples ());
1001 		}
1002 		result = kResultTrue;
1003 	}
1004 	return result;
1005 }
1006 
1007 //------------------------------------------------------------------------
AlgorithmProcessFunction(AAXWrapper_Context * const inInstancesBegin[],const void * inInstancesEnd)1008 static void AAX_CALLBACK AlgorithmProcessFunction (AAXWrapper_Context* const inInstancesBegin[],
1009                                                    const void* inInstancesEnd)
1010 {
1011 	//--- ------ Iterate over plug-in instances ---------//
1012 	for (AAXWrapper_Context* const* walk = inInstancesBegin; walk < inInstancesEnd; ++walk)
1013 	{
1014 		AAXWrapper_Context* AAX_RESTRICT instance = *walk;
1015 
1016 		// first element is Context
1017 		AAXWrapper* wrapper = *reinterpret_cast<AAXWrapper**> (instance->ptr[0]);
1018 		if (!wrapper)
1019 			continue;
1020 
1021 		wrapper->Process (instance);
1022 
1023 	} // End instance-iteration loop
1024 }
1025 
1026 //------------------------------------------------------------------------
vst3Category2AAXPlugInCategory(const char * cat)1027 static int32 vst3Category2AAXPlugInCategory (const char* cat)
1028 {
1029 	static const int32 PDA_ePlugInCategory_Effect =
1030 	    AAX_ePlugInCategory_None; // does not exist anymore?
1031 
1032 	int32 result = AAX_ePlugInCategory_None;
1033 
1034 	if (strstr (cat, "Fx") != nullptr)
1035 	{
1036 		result = PDA_ePlugInCategory_Effect;
1037 	}
1038 
1039 	if (strstr (cat, "Instrument") != nullptr || strstr (cat, "Generator") != nullptr)
1040 	{
1041 		if (strstr (cat, "External") != nullptr)
1042 			result |= AAX_ePlugInCategory_HWGenerators;
1043 		else
1044 			result |= AAX_ePlugInCategory_SWGenerators;
1045 	}
1046 
1047 	if (strstr (cat, "Delay") != nullptr)
1048 		result |= AAX_ePlugInCategory_Delay;
1049 
1050 	if (strstr (cat, "Distortion") != nullptr)
1051 		result |= AAX_ePlugInCategory_Harmonic;
1052 
1053 	if (strstr (cat, "Dynamics") != nullptr)
1054 		result |= AAX_ePlugInCategory_Dynamics;
1055 
1056 	if (strstr (cat, "EQ") != nullptr)
1057 		result |= AAX_ePlugInCategory_EQ;
1058 
1059 	if (strstr (cat, "Mastering") != nullptr)
1060 		result |= AAX_ePlugInCategory_Dither;
1061 
1062 	if (strstr (cat, "Modulation") != nullptr)
1063 		result |= AAX_ePlugInCategory_Modulation;
1064 
1065 	if (strstr (cat, "Pitch Shift") != nullptr)
1066 		result |= AAX_ePlugInCategory_PitchShift;
1067 
1068 	if (strstr (cat, "Restoration") != nullptr)
1069 		result |= AAX_ePlugInCategory_NoiseReduction;
1070 
1071 	if (strstr (cat, "Reverb") != nullptr)
1072 		result |= AAX_ePlugInCategory_Reverb;
1073 
1074 	if (strstr (cat, "Spatial") != nullptr || strstr (cat, "Surround") != nullptr ||
1075 	    strstr (cat, "Up-Downmix") != nullptr)
1076 	{
1077 		result |= AAX_ePlugInCategory_SoundField;
1078 	}
1079 
1080 	return result;
1081 }
1082 
1083 //------------------------------------------------------------------------
1084 // ROUTINE:	DescribeAlgorithmComponent
1085 // Algorithm component description
1086 //------------------------------------------------------------------------
DescribeAlgorithmComponent(AAX_IComponentDescriptor * outDesc,const AAX_Effect_Desc * desc,const AAX_Plugin_Desc * pdesc)1087 void AAXWrapper::DescribeAlgorithmComponent (AAX_IComponentDescriptor* outDesc,
1088                                              const AAX_Effect_Desc* desc,
1089                                              const AAX_Plugin_Desc* pdesc)
1090 {
1091 	// Describe algorithm's context structure
1092 	//
1093 	// Subscribe context fields to host-provided services or information
1094 	HLOG (HAPI, "%s", __FUNCTION__);
1095 
1096 	AAX_Result err = AAX_SUCCESS;
1097 
1098 	// must be in lock step with AAXWrapper ctor
1099 	int32 idx = idxBufferSize + 1;
1100 
1101 	// ProTools does not like instruments without inputs (maybe because they are treated as
1102 	// inserts?)
1103 	int32 inChannels = pdesc->mInputChannels;
1104 	if (inChannels == 0)
1105 		inChannels = pdesc->mOutputChannels;
1106 	if (inChannels)
1107 		err = outDesc->AddAudioIn (idx++);
1108 	AAX_ASSERT (err == AAX_SUCCESS);
1109 
1110 	if (pdesc->mOutputChannels)
1111 		err = outDesc->AddAudioOut (idx++);
1112 	AAX_ASSERT (err == AAX_SUCCESS);
1113 
1114 	err = outDesc->AddAudioBufferLength (AAXWrapper::idxBufferSize);
1115 	AAX_ASSERT (err == AAX_SUCCESS);
1116 
1117 	if (pdesc->mSideChainInputChannels)
1118 		err = outDesc->AddSideChainIn (idx++); // max 1 side chain!?
1119 	AAX_ASSERT (err == AAX_SUCCESS);
1120 
1121 	if (pdesc->mMIDIports)
1122 	{
1123 		for (AAX_MIDI_Desc* mdesc = pdesc->mMIDIports; mdesc->mName; mdesc++)
1124 		{
1125 			err = outDesc->AddMIDINode (idx++, AAX_eMIDINodeType_LocalInput, mdesc->mName,
1126 			                            mdesc->mMask);
1127 			AAX_ASSERT (err == 0);
1128 		}
1129 	}
1130 
1131 	if (pdesc->mAuxOutputChannels)
1132 	{
1133 		for (AAX_Aux_Desc* auxdesc = pdesc->mAuxOutputChannels; auxdesc->mName; auxdesc++)
1134 		{
1135 			int32 ch = (auxdesc->mChannels < 0 ? pdesc->mOutputChannels : auxdesc->mChannels);
1136 			err = outDesc->AddAuxOutputStem (idx++, getChannelsStem (ch), auxdesc->mName);
1137 			AAX_ASSERT (err == 0);
1138 		}
1139 	}
1140 	if (pdesc->mMeters)
1141 	{
1142 		uint32 cntMeters = 0;
1143 		for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++)
1144 			cntMeters++;
1145 		std::unique_ptr<Steinberg::int32[]> meterIds;
1146 		meterIds.reset (new Steinberg::int32 (cntMeters));
1147 		cntMeters = 0;
1148 		for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++)
1149 			meterIds[cntMeters++] = mdesc->mID;
1150 
1151 		err = outDesc->AddMeters (idx++, (AAX_CTypeID*)meterIds.get (), cntMeters);
1152 		AAX_ASSERT (err == AAX_SUCCESS);
1153 	}
1154 
1155 	// Register context fields as private data
1156 	err = outDesc->AddPrivateData (AAXWrapper::idxContext, sizeof (void*),
1157 	                               AAX_ePrivateDataOptions_DefaultOptions);
1158 	AAX_ASSERT (err == AAX_SUCCESS);
1159 
1160 	// Register processing callbacks
1161 	//
1162 	// Create a property map
1163 	AAX_IPropertyMap* properties = outDesc->NewPropertyMap ();
1164 	AAX_ASSERT (properties);
1165 	if (!properties)
1166 		return;
1167 	//
1168 	// Generic properties
1169 	properties->AddProperty (AAX_eProperty_ManufacturerID, desc->mManufacturerID);
1170 	properties->AddProperty (AAX_eProperty_ProductID, desc->mProductID);
1171 	properties->AddProperty (AAX_eProperty_CanBypass, true);
1172 	properties->AddProperty (AAX_eProperty_LatencyContribution, pdesc->mLatency);
1173 	// properties->AddProperty (AAX_eProperty_UsesClientGUI, true); // Uses auto-GUI
1174 
1175 	//
1176 	// Stem format -specific properties
1177 	//	AAX_IPropertyMap* propertiesMono = outDesc->NewPropertyMap();
1178 	//	AAX_ASSERT (propertiesMono);
1179 	//	if ( !propertiesMono ) return;
1180 	//	properties->Clone(propertiesMono);
1181 	//
1182 	if (pdesc->mInputChannels)
1183 		properties->AddProperty (AAX_eProperty_InputStemFormat,
1184 		                         getChannelsStem (pdesc->mInputChannels));
1185 	else if (pdesc->mOutputChannels)
1186 		properties->AddProperty (AAX_eProperty_InputStemFormat,
1187 		                         getChannelsStem (pdesc->mOutputChannels));
1188 
1189 	if (pdesc->mOutputChannels)
1190 		properties->AddProperty (AAX_eProperty_OutputStemFormat,
1191 		                         getChannelsStem (pdesc->mOutputChannels));
1192 	if (pdesc->mSideChainInputChannels)
1193 	{
1194 		properties->AddProperty (AAX_eProperty_SupportsSideChainInput, true);
1195 #if 0 // only mono supported, setting stem format causes plugin load failure
1196 		properties->AddProperty (AAX_eProperty_SideChainStemFormat,
1197 								 getChannelsStem (pdesc->mSideChainInputChannels));
1198 #endif
1199 	}
1200 
1201 	properties->AddProperty (AAX_eProperty_PlugInID_Native, pdesc->mPlugInIDNative);
1202 	properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite, pdesc->mPlugInIDAudioSuite);
1203 
1204 	// Register callbacks
1205 	// Native (Native and AudioSuite)
1206 	err = outDesc->AddProcessProc_Native<AAXWrapper_Context> (AlgorithmProcessFunction, properties,
1207 	                                                          AlgorithmInitFunction,
1208 	                                                          nullptr /*AlgorithmBackgroundFunction*/);
1209 	AAX_ASSERT (err == AAX_SUCCESS);
1210 }
1211 
1212 //------------------------------------------------------------------------
GetPlugInDescription(AAX_IEffectDescriptor * outDescriptor,const AAX_Effect_Desc * desc,const AAX_Plugin_Desc * pdesc)1213 static AAX_Result GetPlugInDescription (AAX_IEffectDescriptor* outDescriptor,
1214                                         const AAX_Effect_Desc* desc, const AAX_Plugin_Desc* pdesc)
1215 {
1216 	HLOG (HAPI, "%s", __FUNCTION__);
1217 
1218 	int err = AAX_SUCCESS;
1219 	AAX_IComponentDescriptor* compDesc = outDescriptor->NewComponentDescriptor ();
1220 	if (!compDesc)
1221 		return AAX_ERROR_NULL_OBJECT;
1222 
1223 	// Effect identifiers
1224 	outDescriptor->AddName (pdesc->mName);
1225 	outDescriptor->AddCategory (vst3Category2AAXPlugInCategory (desc->mCategory));
1226 
1227 	// Effect components
1228 	//
1229 	// Algorithm component
1230 	AAXWrapper::DescribeAlgorithmComponent (compDesc, desc, pdesc);
1231 	err = outDescriptor->AddComponent (compDesc);
1232 	AAX_ASSERT (err == AAX_SUCCESS);
1233 
1234 	// Data model
1235 	int32 plugIndex = pdesc - desc->mPluginDesc;
1236 	fnCreateParameters* fn = nullptr;
1237 	switch (plugIndex)
1238 	{
1239 #define ADDPROCPTR(N) \
1240 	case N: fn = &CP<N>::Create_Parameters; break;
1241 		ADDPROCPTR (0)	ADDPROCPTR (1)	ADDPROCPTR (2)	ADDPROCPTR (3)
1242 		ADDPROCPTR (4)	ADDPROCPTR (5)	ADDPROCPTR (6)	ADDPROCPTR (7)
1243 		ADDPROCPTR (8)	ADDPROCPTR (9)	ADDPROCPTR (10) ADDPROCPTR (11)
1244 		ADDPROCPTR (12)	ADDPROCPTR (13)	ADDPROCPTR (14) ADDPROCPTR (15)
1245 	}
1246 	AAX_ASSERT (fn);
1247 	err = outDescriptor->AddProcPtr ((void*)fn, kAAX_ProcPtrID_Create_EffectParameters);
1248 	AAX_ASSERT (err == AAX_SUCCESS);
1249 
1250 	if (desc->mPageFile)
1251 		outDescriptor->AddResourceInfo (AAX_eResourceType_PageTable, desc->mPageFile);
1252 
1253 	// Effect's meter display properties
1254 	if (pdesc->mMeters)
1255 	{
1256 		for (AAX_Meter_Desc* mdesc = pdesc->mMeters; mdesc->mName; mdesc++)
1257 		{
1258 			AAX_IPropertyMap* meterProperties = outDescriptor->NewPropertyMap ();
1259 			if (!meterProperties)
1260 				return AAX_ERROR_NULL_OBJECT;
1261 
1262 			// Support different meter types offered by AAX here
1263 			meterProperties->AddProperty (AAX_eProperty_Meter_Type, mdesc->mType);
1264 
1265 			meterProperties->AddProperty (AAX_eProperty_Meter_Orientation, mdesc->mOrientation);
1266 			outDescriptor->AddMeterDescription (mdesc->mID, mdesc->mName, meterProperties);
1267 		}
1268 	}
1269 
1270 	// plugin supplied GUI
1271 	err = outDescriptor->AddProcPtr ((void*)Create_GUI, kAAX_ProcPtrID_Create_EffectGUI);
1272 	AAX_ASSERT (err == AAX_SUCCESS);
1273 
1274 	return AAX_SUCCESS;
1275 }
1276 
1277 //------------------------------------------------------------------------
GetEffectDescriptions(AAX_ICollection * outCollection)1278 AAX_Result GetEffectDescriptions (AAX_ICollection* outCollection)
1279 {
1280 	HLOG (HAPI, "%s", __FUNCTION__);
1281 
1282 	AAX_Result result = AAX_ERROR_NULL_OBJECT;
1283 
1284 	AAX_Effect_Desc* effDesc = AAXWrapper_GetDescription ();
1285 	for (const AAX_Plugin_Desc* pdesc = effDesc->mPluginDesc; pdesc->mEffectID; pdesc++)
1286 	{
1287 		if (AAX_IEffectDescriptor* plugInDescriptor = outCollection->NewDescriptor ())
1288 		{
1289 			result = GetPlugInDescription (plugInDescriptor, effDesc, pdesc);
1290 			if (result == AAX_SUCCESS)
1291 				result = outCollection->AddEffect (pdesc->mEffectID, plugInDescriptor);
1292 			AAX_ASSERT (result == AAX_SUCCESS);
1293 		}
1294 	}
1295 
1296 	outCollection->SetManufacturerName (effDesc->mManufacturer);
1297 
1298 	/* needed ?
1299 	char packageName[512];
1300 	sprintf (packageName, "%s AAX Plug-In", effDesc->mProduct);
1301 	outCollection->AddPackageName (packageName);
1302 
1303 	sprintf (packageName, "%s Plug-In", effDesc->mProduct);
1304 	outCollection->AddPackageName (packageName);*/
1305 
1306 	outCollection->AddPackageName (effDesc->mProduct);
1307 
1308 	if (strlen (effDesc->mProduct) > 16)
1309 	{
1310 		char packageShortName[17] = { 0 };
1311 		strncpy (packageShortName, effDesc->mProduct, 16);
1312 		outCollection->AddPackageName (packageShortName);
1313 	}
1314 
1315 	outCollection->SetPackageVersion (effDesc->mVersion);
1316 
1317 	return result;
1318 }
1319