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