iPlug2 - C++ Audio Plug-in Framework
IPlugAU.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5 
6  See LICENSE.txt for more info.
7 
8  ==============================================================================
9 */
10 
11 #include <algorithm>
12 #include <CoreMIDI/CoreMIDI.h>
13 
14 #include "heapbuf.h"
15 
16 #include "dfx-au-utilities.h"
17 #include "IPlugAU.h"
18 #include "IPlugAU_ioconfig.h"
19 
20 using namespace iplug;
21 
22 #pragma mark - CFString and CString Utilities
23 
24 static inline CFStringRef MakeCFString(const char* cStr)
25 {
26  return CFStringCreateWithCString(0, cStr, kCFStringEncodingUTF8);
27 }
28 
29 class IPlugAU::CFStrLocal
30 {
31 public:
32  CFStrLocal(const char* cStr)
33  {
34  mCFStr = MakeCFString(cStr);
35  }
36 
37  ~CFStrLocal()
38  {
39  CFRelease(mCFStr);
40  }
41 
42  CFStrLocal(const CFStrLocal&) = delete;
43  CFStrLocal& operator=(const CFStrLocal&) = delete;
44 
45  CFStringRef Get() { return mCFStr; }
46 
47 private:
48  CFStringRef mCFStr;
49 };
50 
51 struct IPlugAU::CStrLocal : WDL_TypedBuf<char>
52 {
53  CStrLocal(CFStringRef cfStr)
54  {
55  if (cfStr)
56  {
57  Resize((int) CFStringGetLength(cfStr) + 1);
58  CFStringGetCString(cfStr, Get(), GetSize(), kCFStringEncodingUTF8);
59  }
60  }
61 };
62 
63 #pragma mark - Utilities
64 
65 inline void IPlugAU::PutNumberInDict(CFMutableDictionaryRef pDict, const char* key, void* pNumber, CFNumberType type)
66 {
67  CFStrLocal cfKey(key);
68  CFNumberRef pValue = CFNumberCreate(0, type, pNumber);
69  CFDictionarySetValue(pDict, cfKey.Get(), pValue);
70  CFRelease(pValue);
71 }
72 
73 inline void IPlugAU::PutStrInDict(CFMutableDictionaryRef pDict, const char* key, const char* value)
74 {
75  CFStrLocal cfKey(key);
76  CFStrLocal cfValue(value);
77  CFDictionarySetValue(pDict, cfKey.Get(), cfValue.Get());
78 }
79 
80 inline void IPlugAU::PutDataInDict(CFMutableDictionaryRef pDict, const char* key, IByteChunk* pChunk)
81 {
82  CFStrLocal cfKey(key);
83  CFDataRef pData = CFDataCreate(0, pChunk->GetData(), pChunk->Size());
84  CFDictionarySetValue(pDict, cfKey.Get(), pData);
85  CFRelease(pData);
86 }
87 
88 inline bool IPlugAU::GetNumberFromDict(CFDictionaryRef pDict, const char* key, void* pNumber, CFNumberType type)
89 {
90  CFStrLocal cfKey(key);
91  CFNumberRef pValue = (CFNumberRef) CFDictionaryGetValue(pDict, cfKey.Get());
92  if (pValue)
93  {
94  CFNumberGetValue(pValue, type, pNumber);
95  return true;
96  }
97  return false;
98 }
99 
100 inline bool IPlugAU::GetStrFromDict(CFDictionaryRef pDict, const char* key, char* value)
101 {
102  CFStrLocal cfKey(key);
103  CFStringRef pValue = (CFStringRef) CFDictionaryGetValue(pDict, cfKey.Get());
104  if (pValue)
105  {
106  CStrLocal cStr(pValue);
107  strcpy(value, cStr.Get());
108  return true;
109  }
110  value[0] = '\0';
111  return false;
112 }
113 
114 inline bool IPlugAU::GetDataFromDict(CFDictionaryRef pDict, const char* key, IByteChunk* pChunk)
115 {
116  CFStrLocal cfKey(key);
117  CFDataRef pData = (CFDataRef) CFDictionaryGetValue(pDict, cfKey.Get());
118  if (pData)
119  {
120  CFIndex n = CFDataGetLength(pData);
121  pChunk->Resize((int) n);
122  memcpy(pChunk->GetData(), CFDataGetBytePtr(pData), n);
123  return true;
124  }
125  return false;
126 }
127 
128 #define kAudioUnitRemovePropertyListenerWithUserDataSelect 0x0012
129 
130 typedef AudioStreamBasicDescription STREAM_DESC;
131 
132 static /* inline */ void MakeDefaultASBD(STREAM_DESC* pASBD, double sampleRate, int nChannels, bool interleaved)
133 {
134  memset(pASBD, 0, sizeof(STREAM_DESC));
135  pASBD->mSampleRate = sampleRate;
136  pASBD->mFormatID = kAudioFormatLinearPCM;
137  pASBD->mFormatFlags = kAudioFormatFlagsCanonical;
138  pASBD->mBitsPerChannel = 8 * sizeof(AudioSampleType);
139  pASBD->mChannelsPerFrame = nChannels;
140  pASBD->mFramesPerPacket = 1;
141  int nBytes = sizeof(AudioSampleType);
142  if (interleaved)
143  {
144  nBytes *= nChannels;
145  }
146  else
147  {
148  pASBD->mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
149  }
150  pASBD->mBytesPerPacket = pASBD->mBytesPerFrame = nBytes;
151 }
152 
153 template <class C>
154 static int PtrListAddFromStack(WDL_PtrList<C>* pList, C* pStackInstance)
155 {
156  C* pNew = new C;
157  memcpy(pNew, pStackInstance, sizeof(C));
158  pList->Add(pNew);
159  return pList->GetSize() - 1;
160 }
161 
162 template <class C>
163 static int PtrListInitialize(WDL_PtrList<C>* pList, int size)
164 {
165  for (int i = 0; i < size; ++i)
166  {
167  C* pNew = new C;
168  memset(pNew, 0, sizeof(C));
169  pList->Add(pNew);
170  }
171  return size;
172 }
173 
174 #if defined(__LP64__)
175  #define GET_COMP_PARAM(TYPE, IDX, NUM) *((TYPE*)&(params->params[NUM - IDX]))
176 #else
177  #define GET_COMP_PARAM(TYPE, IDX, NUM) *((TYPE*)&(params->params[IDX]))
178 #endif
179 
180 #define NO_OP(select) case select: return badComponentSelector;
181 
182 #ifndef AU_NO_COMPONENT_ENTRY
183 #pragma mark - COMPONENT MANAGER ENTRY POINT
184 // static
185 OSStatus IPlugAU::IPlugAUEntry(ComponentParameters *params, void* pPlug)
186 {
187  int select = params->what;
188 
189  Trace(TRACELOC, "(%d:%s)", select, AUSelectStr(select));
190 
191  if (select == kComponentOpenSelect)
192  {
193  // N.B. calling this with nullptr will call through to new (hence the delete below)
194  IPlugAU* _this = MakePlug(nullptr);
195 
196  _this->PruneUninitializedPresets();
197  _this->mCI = GET_COMP_PARAM(ComponentInstance, 0, 1);
198  SetComponentInstanceStorage(_this->mCI, (Handle) _this);
199  return noErr;
200  }
201 
202  IPlugAU* _this = (IPlugAU*) pPlug;
203 
204  if (select == kComponentCloseSelect)
205  {
206  _this->ClearConnections();
207  delete _this;
208  return noErr;
209  }
210 
211  switch (select)
212  {
213  case kComponentVersionSelect:
214  {
215  return _this->GetPluginVersion(false);
216  }
217  case kAudioUnitInitializeSelect:
218  {
219  return DoInitialize(_this);
220  }
221  case kAudioUnitUninitializeSelect:
222  {
223  return DoUninitialize(_this);
224  }
225  case kAudioUnitGetPropertyInfoSelect:
226  {
227  AudioUnitPropertyID propID = GET_COMP_PARAM(AudioUnitPropertyID, 4, 5);
228  AudioUnitScope scope = GET_COMP_PARAM(AudioUnitScope, 3, 5);
229  AudioUnitElement element = GET_COMP_PARAM(AudioUnitElement, 2, 5);
230  UInt32* pDataSize = GET_COMP_PARAM(UInt32*, 1, 5);
231  Boolean* pWriteable = GET_COMP_PARAM(Boolean*, 0, 5);
232 
233  return _this->DoGetPropertyInfo(_this, propID, scope, element, pDataSize, pWriteable);
234  }
235  case kAudioUnitGetPropertySelect:
236  {
237  AudioUnitPropertyID propID = GET_COMP_PARAM(AudioUnitPropertyID, 4, 5);
238  AudioUnitScope scope = GET_COMP_PARAM(AudioUnitScope, 3, 5);
239  AudioUnitElement element = GET_COMP_PARAM(AudioUnitElement, 2, 5);
240  void* pData = GET_COMP_PARAM(void*, 1, 5);
241  UInt32* pDataSize = GET_COMP_PARAM(UInt32*, 0, 5);
242 
243  return _this->DoGetProperty(_this, propID, scope, element, pData, pDataSize);
244  }
245  case kAudioUnitSetPropertySelect:
246  {
247  AudioUnitPropertyID propID = GET_COMP_PARAM(AudioUnitPropertyID, 4, 5);
248  AudioUnitScope scope = GET_COMP_PARAM(AudioUnitScope, 3, 5);
249  AudioUnitElement element = GET_COMP_PARAM(AudioUnitElement, 2, 5);
250  const void* pData = GET_COMP_PARAM(const void*, 1, 5);
251  UInt32* pDataSize = GET_COMP_PARAM(UInt32*, 0, 5);
252 
253  return _this->DoSetProperty(_this, propID, scope, element, pData, pDataSize);
254  }
255  case kAudioUnitAddPropertyListenerSelect:
256  {
257  AudioUnitPropertyID propID = GET_COMP_PARAM(AudioUnitPropertyID, 2, 3);
258  AudioUnitPropertyListenerProc proc = GET_COMP_PARAM(AudioUnitPropertyListenerProc, 1, 3);
259  void* userData = GET_COMP_PARAM(void*, 0, 3);
260 
261  return _this->DoAddPropertyListener(_this, propID, proc, userData);
262  }
263  case kAudioUnitRemovePropertyListenerSelect:
264  {
265  AudioUnitPropertyID propID = GET_COMP_PARAM(AudioUnitPropertyID, 1, 2);
266  AudioUnitPropertyListenerProc proc = GET_COMP_PARAM(AudioUnitPropertyListenerProc, 0, 2);
267 
268  return _this->DoRemovePropertyListener(_this, propID, proc);
269  }
270  case kAudioUnitRemovePropertyListenerWithUserDataSelect:
271  {
272  AudioUnitPropertyID propID = GET_COMP_PARAM(AudioUnitPropertyID, 2, 3);
273  AudioUnitPropertyListenerProc proc = GET_COMP_PARAM(AudioUnitPropertyListenerProc, 1, 3);
274  void* userData = GET_COMP_PARAM(void*, 0, 3);
275 
276  return _this->DoRemovePropertyListenerWithUserData(_this, propID, proc, userData);
277  }
278  case kAudioUnitAddRenderNotifySelect:
279  {
280  AURenderCallback proc = GET_COMP_PARAM(AURenderCallback, 1, 2);
281  void* userData = GET_COMP_PARAM(void*, 0, 2);
282  return _this->DoAddRenderNotify(_this, proc, userData);
283  }
284  case kAudioUnitRemoveRenderNotifySelect:
285  {
286  AURenderCallback proc = GET_COMP_PARAM(AURenderCallback, 1, 2);
287  void* userData = GET_COMP_PARAM(void*, 0, 2);
288  return _this->DoRemoveRenderNotify(_this, proc, userData);
289  }
290  case kAudioUnitGetParameterSelect:
291  {
292  AudioUnitParameterID paramID = GET_COMP_PARAM(AudioUnitParameterID, 3, 4);
293  AudioUnitScope scope = GET_COMP_PARAM(AudioUnitScope, 2, 4);
294  AudioUnitElement element = GET_COMP_PARAM(AudioUnitElement, 1, 4);
295  AudioUnitParameterValue* pValue = GET_COMP_PARAM(AudioUnitParameterValue*, 0, 4);
296  return _this->DoGetParameter(_this, paramID, scope, element, pValue);
297  }
298  case kAudioUnitSetParameterSelect:
299  {
300  AudioUnitParameterID paramID = GET_COMP_PARAM(AudioUnitParameterID, 4, 5);
301  AudioUnitScope scope = GET_COMP_PARAM(AudioUnitScope, 3, 5);
302  AudioUnitElement element = GET_COMP_PARAM(AudioUnitElement, 2, 5);
303  AudioUnitParameterValue value = GET_COMP_PARAM(AudioUnitParameterValue, 1, 5);
304  UInt32 offset = GET_COMP_PARAM(UInt32, 0, 5);
305  return _this->DoSetParameter(_this, paramID, scope, element, value, offset);
306  }
307  case kAudioUnitScheduleParametersSelect:
308  {
309  AudioUnitParameterEvent* pEvent = GET_COMP_PARAM(AudioUnitParameterEvent*, 1, 2);
310  UInt32 nEvents = GET_COMP_PARAM(UInt32, 0, 2);
311  return _this->DoScheduleParameters(_this, pEvent, nEvents);
312  }
313  case kAudioUnitRenderSelect:
314  {
315  AudioUnitRenderActionFlags* pFlags = GET_COMP_PARAM(AudioUnitRenderActionFlags*, 4, 5);
316  const AudioTimeStamp* pTimestamp = GET_COMP_PARAM(AudioTimeStamp*, 3, 5);
317  UInt32 outputBusIdx = GET_COMP_PARAM(UInt32, 2, 5);
318  UInt32 nFrames = GET_COMP_PARAM(UInt32, 1, 5);
319  AudioBufferList* pBufferList = GET_COMP_PARAM(AudioBufferList*, 0, 5);
320  return _this->DoRender(_this, pFlags, pTimestamp, outputBusIdx, nFrames, pBufferList);
321  }
322  case kAudioUnitResetSelect:
323  {
324  return _this->DoReset(_this);
325  }
326  case kMusicDeviceMIDIEventSelect:
327  {
328  return _this->DoMIDIEvent(_this, GET_COMP_PARAM(UInt32, 3, 4), GET_COMP_PARAM(UInt32, 2, 4), GET_COMP_PARAM(UInt32, 1, 4), GET_COMP_PARAM(UInt32, 0, 4));
329  }
330  case kMusicDeviceSysExSelect:
331  {
332  return _this->DoSysEx(_this, GET_COMP_PARAM(UInt8*, 1, 2), GET_COMP_PARAM(UInt32, 0, 2));
333  }
334  case kMusicDevicePrepareInstrumentSelect:
335  {
336  return noErr;
337  }
338  case kMusicDeviceReleaseInstrumentSelect:
339  {
340  return noErr;
341  }
342  case kMusicDeviceStartNoteSelect:
343  {
344 // MusicDeviceInstrumentID deviceID = GET_COMP_PARAM(MusicDeviceInstrumentID, 4, 5);
345 // MusicDeviceGroupID groupID = GET_COMP_PARAM(MusicDeviceGroupID, 3, 5);
346  NoteInstanceID* pNoteID = GET_COMP_PARAM(NoteInstanceID*, 2, 5);
347  UInt32 offset = GET_COMP_PARAM(UInt32, 1, 5);
348  MusicDeviceNoteParams* pNoteParams = GET_COMP_PARAM(MusicDeviceNoteParams*, 0, 5);
349  int note = (int) pNoteParams->mPitch;
350  *pNoteID = note;
351  IMidiMsg msg;
352  msg.MakeNoteOnMsg(note, (int) pNoteParams->mVelocity, offset);
353  return noErr;
354  }
355  case kMusicDeviceStopNoteSelect:
356  {
357 // MusicDeviceGroupID groupID = GET_COMP_PARAM(MusicDeviceGroupID, 2, 3);
358  NoteInstanceID noteID = GET_COMP_PARAM(NoteInstanceID, 1, 3);
359  UInt32 offset = GET_COMP_PARAM(UInt32, 0, 3);
360  // noteID is supposed to be some incremented unique ID, but we're just storing note number in it.
361  IMidiMsg msg;
362  msg.MakeNoteOffMsg(noteID, offset);
363  return noErr;
364  }
365  case kComponentCanDoSelect:
366  {
367  switch (params->params[0])
368  {
369  case kAudioUnitInitializeSelect:
370  case kAudioUnitUninitializeSelect:
371  case kAudioUnitGetPropertyInfoSelect:
372  case kAudioUnitGetPropertySelect:
373  case kAudioUnitSetPropertySelect:
374  case kAudioUnitAddPropertyListenerSelect:
375  case kAudioUnitRemovePropertyListenerSelect:
376  case kAudioUnitGetParameterSelect:
377  case kAudioUnitSetParameterSelect:
378  case kAudioUnitResetSelect:
379  case kAudioUnitRenderSelect:
380  case kAudioUnitAddRenderNotifySelect:
381  case kAudioUnitRemoveRenderNotifySelect:
382  case kAudioUnitScheduleParametersSelect:
383  return 1;
384  default:
385  return 0;
386  }
387  }
388  default: return badComponentSelector;
389  }
390 }
391 #endif //AU_NO_COMPONENT_ENTRY
392 
393 #pragma mark - GetChannelLayoutTags
394 
395 UInt32 IPlugAU::GetChannelLayoutTags(AudioUnitScope scope, AudioUnitElement element, AudioChannelLayoutTag* tags)
396 {
397  switch(scope)
398  {
399  case kAudioUnitScope_Input:
400  case kAudioUnitScope_Output:
401  {
402  ERoute dir = (ERoute) (scope - 1);
403 
404  WDL_TypedBuf<uint64_t> foundTags;
405 
406  for(auto configIdx = 0; configIdx < NIOConfigs(); configIdx++)
407  {
408  const IOConfig* pConfig = GetIOConfig(configIdx);
409 
410  for(auto busIdx = 0; busIdx < pConfig->NBuses(dir); busIdx++)
411  {
412  if(busIdx == element)
413  {
414  WDL_TypedBuf<uint64_t> busTypes;
415  GetAPIBusTypeForChannelIOConfig(configIdx, dir, busIdx, pConfig, &busTypes);
416  // DBGMSG("Found %i different tags for an %s bus with %i channels\n", busTypes.GetSize(), RoutingDirStrs[dir], pConfig->GetBusInfo(dir, busIdx)->NChans());
417 
418  for (auto tag = 0; tag < busTypes.GetSize(); tag++)
419  {
420  if(foundTags.Find(busTypes.Get()[tag]) == -1)
421  foundTags.Add(busTypes.Get()[tag]);
422  }
423  }
424  }
425  }
426 
427  if(tags)
428  {
429  for (auto v = 0; v < foundTags.GetSize(); v++)
430  {
431  tags[v] = (AudioChannelLayoutTag) foundTags.Get()[v];
432  }
433 
434 // DBGMSG("Adding %i tags\n", foundTags.GetSize());
435 
436  return 1; // success
437  }
438  else
439  return foundTags.GetSize();
440 
441 // TODO: what about wild cards?
442  }
443  default:
444  return 0;
445  }
446 }
447 
448 #define ASSERT_SCOPE(reqScope) if (scope != reqScope) { return kAudioUnitErr_InvalidProperty; }
449 #define ASSERT_ELEMENT(numElements) if (element >= numElements) { return kAudioUnitErr_InvalidElement; }
450 #define ASSERT_INPUT_OR_GLOBAL_SCOPE \
451  if (scope != kAudioUnitScope_Input && scope != kAudioUnitScope_Global) { \
452  return kAudioUnitErr_InvalidProperty; \
453  }
454 #undef NO_OP
455 #define NO_OP(propID) case propID: return kAudioUnitErr_InvalidProperty;
456 
457 // pData == 0 means return property info only.
458 OSStatus IPlugAU::GetProperty(AudioUnitPropertyID propID, AudioUnitScope scope, AudioUnitElement element,
459  UInt32* pDataSize, Boolean* pWriteable, void* pData)
460 {
461  Trace(TRACELOC, "%s(%d:%s):(%d:%s):%d", (pData ? "" : "info:"), propID, AUPropertyStr(propID), scope, AUScopeStr(scope), element);
462 
463  switch (propID)
464  {
465  case kIPlugObjectPropertyID:
466  {
467  *pDataSize = sizeof (void*);
468  if (pData)
469  {
470  ((void**) pData)[0] = (void*) static_cast<IPlugAPIBase*> (this);
471  }
472  else {
473  *pWriteable = false;
474  }
475  return noErr;
476  }
477  case kAudioUnitProperty_ClassInfo: // 0,
478  {
479  *pDataSize = sizeof(CFPropertyListRef);
480  if (pData)
481  {
482  CFPropertyListRef* pList = (CFPropertyListRef*) pData;
483  *pWriteable = true;
484  return GetState(pList);
485  }
486  return noErr;
487  }
488  case kAudioUnitProperty_MakeConnection: // 1,
489  {
490  ASSERT_INPUT_OR_GLOBAL_SCOPE;
491  *pDataSize = sizeof(AudioUnitConnection);
492  *pWriteable = true;
493  return noErr;
494  }
495  case kAudioUnitProperty_SampleRate: // 2,
496  {
497  *pDataSize = sizeof(Float64);
498  *pWriteable = true;
499  if (pData)
500  {
501  *((Float64*) pData) = GetSampleRate();
502  }
503  return noErr;
504  }
505  case kAudioUnitProperty_ParameterList: // 3, listenable
506  {
507  int n = (scope == kAudioUnitScope_Global ? NParams() : 0);
508  *pDataSize = n * sizeof(AudioUnitParameterID);
509  if (pData && n)
510  {
511  AudioUnitParameterID* pParamID = (AudioUnitParameterID*) pData;
512  for (int i = 0; i < n; ++i, ++pParamID)
513  {
514  *pParamID = (AudioUnitParameterID) i;
515  }
516  }
517  return noErr;
518  }
519  case kAudioUnitProperty_ParameterInfo: // 4, listenable
520  {
521  ASSERT_SCOPE(kAudioUnitScope_Global);
522  ASSERT_ELEMENT(NParams());
523  *pDataSize = sizeof(AudioUnitParameterInfo);
524  if (pData)
525  {
526  AudioUnitParameterInfo* pInfo = (AudioUnitParameterInfo*) pData;
527  memset(pInfo, 0, sizeof(AudioUnitParameterInfo));
528  pInfo->flags = kAudioUnitParameterFlag_CFNameRelease |
529  kAudioUnitParameterFlag_HasCFNameString |
530  kAudioUnitParameterFlag_IsWritable |
531  kAudioUnitParameterFlag_IsReadable ;
532 
533  #ifndef IPLUG1_COMPATIBILITY
534  pInfo->flags |= kAudioUnitParameterFlag_IsHighResolution;
535  #endif
536 
537  ENTER_PARAMS_MUTEX
538  IParam* pParam = GetParam(element);
539 
540  if (!pParam->GetCanAutomate()) pInfo->flags |= kAudioUnitParameterFlag_NonRealTime;
541  if (pParam->GetMeta()) pInfo->flags |= kAudioUnitParameterFlag_IsElementMeta;
542  if (pParam->NDisplayTexts()) pInfo->flags |= kAudioUnitParameterFlag_ValuesHaveStrings;
543 
544  const char* paramName = pParam->GetName();
545  pInfo->cfNameString = CFStringCreateWithCString(0, pParam->GetName(), kCFStringEncodingUTF8);
546  strcpy(pInfo->name, paramName); // Max 52.
547 
548  switch (pParam->Type())
549  {
550  case IParam::kTypeBool:
551  pInfo->unit = kAudioUnitParameterUnit_Boolean;
552  break;
553  case IParam::kTypeEnum:
554  //fall through
555  case IParam::kTypeInt:
556  pInfo->unit = kAudioUnitParameterUnit_Indexed;
557  break;
558  default:
559  {
560  switch (pParam->Unit())
561  {
562  case IParam::kUnitPercentage: pInfo->unit = kAudioUnitParameterUnit_Percent; break;
563  case IParam::kUnitSeconds: pInfo->unit = kAudioUnitParameterUnit_Seconds; break;
564  case IParam::kUnitMilliseconds: pInfo->unit = kAudioUnitParameterUnit_Milliseconds; break;
565  case IParam::kUnitSamples: pInfo->unit = kAudioUnitParameterUnit_SampleFrames; break;
566  case IParam::kUnitDB: pInfo->unit = kAudioUnitParameterUnit_Decibels; break;
567  case IParam::kUnitLinearGain: pInfo->unit = kAudioUnitParameterUnit_LinearGain; break;
568  case IParam::kUnitPan: pInfo->unit = kAudioUnitParameterUnit_Pan; break;
569  case IParam::kUnitPhase: pInfo->unit = kAudioUnitParameterUnit_Phase; break;
570  case IParam::kUnitDegrees: pInfo->unit = kAudioUnitParameterUnit_Degrees; break;
571  case IParam::kUnitMeters: pInfo->unit = kAudioUnitParameterUnit_Meters; break;
572  case IParam::kUnitRate: pInfo->unit = kAudioUnitParameterUnit_Rate; break;
573  case IParam::kUnitRatio: pInfo->unit = kAudioUnitParameterUnit_Ratio; break;
574  case IParam::kUnitFrequency: pInfo->unit = kAudioUnitParameterUnit_Hertz; break;
575  case IParam::kUnitOctaves: pInfo->unit = kAudioUnitParameterUnit_Octaves; break;
576  case IParam::kUnitCents: pInfo->unit = kAudioUnitParameterUnit_Cents; break;
577  case IParam::kUnitAbsCents: pInfo->unit = kAudioUnitParameterUnit_AbsoluteCents; break;
578  case IParam::kUnitSemitones: pInfo->unit = kAudioUnitParameterUnit_RelativeSemiTones; break;
579  case IParam::kUnitMIDINote: pInfo->unit = kAudioUnitParameterUnit_MIDINoteNumber; break;
580  case IParam::kUnitMIDICtrlNum: pInfo->unit = kAudioUnitParameterUnit_MIDIController; break;
581  case IParam::kUnitBPM: pInfo->unit = kAudioUnitParameterUnit_BPM; break;
582  case IParam::kUnitBeats: pInfo->unit = kAudioUnitParameterUnit_Beats; break;
583 
584  case IParam::kUnitCustom:
585  {
586  if (CStringHasContents(pParam->GetCustomUnit()))
587  {
588  pInfo->unit = kAudioUnitParameterUnit_CustomUnit;
589  pInfo->unitName = CFStringCreateWithCString(0, pParam->GetCustomUnit(), kCFStringEncodingUTF8);
590  }
591  else
592  {
593  pInfo->unit = kAudioUnitParameterUnit_Generic;
594  }
595  break;
596  }
597  }
598  }
599  }
600 
601  switch (pParam->DisplayType())
602  {
603  case IParam::kDisplayLinear:
604  break;
605  case IParam::kDisplaySquared:
606  pInfo->flags |= kAudioUnitParameterFlag_DisplaySquared;
607  break;
608  case IParam::kDisplaySquareRoot:
609  pInfo->flags |= kAudioUnitParameterFlag_DisplaySquareRoot;
610  break;
611  case IParam::kDisplayCubed:
612  pInfo->flags |= kAudioUnitParameterFlag_DisplayCubed;
613  break;
614  case IParam::kDisplayCubeRoot:
615  pInfo->flags |= kAudioUnitParameterFlag_DisplayCubeRoot;
616  break;
617  case IParam::kDisplayExp:
618  pInfo->flags |= kAudioUnitParameterFlag_DisplayExponential;
619  break;
620  case IParam::kDisplayLog:
621  pInfo->flags |= kAudioUnitParameterFlag_DisplayLogarithmic;
622  break;
623  }
624 
625  pInfo->minValue = pParam->GetMin();
626  pInfo->maxValue = pParam->GetMax();
627  pInfo->defaultValue = pParam->GetDefault();
628 
629  const char* paramGroupName = pParam->GetGroup();
630 
631  if (CStringHasContents(paramGroupName))
632  {
633  int clumpID = 0;
634 
635  for(int i = 0; i< NParamGroups(); i++)
636  {
637  if(strcmp(paramGroupName, GetParamGroupName(i)) == 0)
638  clumpID = i+1;
639  }
640 
641  if (clumpID == 0) // new clump
642  clumpID = AddParamGroup(paramGroupName);
643 
644  pInfo->flags = pInfo->flags | kAudioUnitParameterFlag_HasClump;
645  pInfo->clumpID = clumpID;
646  }
647  LEAVE_PARAMS_MUTEX
648  }
649  return noErr;
650  }
651  case kAudioUnitProperty_FastDispatch: // 5,
652  {
653  return GetProc(element, pDataSize, pData);
654  }
655  NO_OP(kAudioUnitProperty_CPULoad); // 6,
656  case kAudioUnitProperty_StreamFormat: // 8,
657  {
658  BusChannels* pBus = GetBus(scope, element);
659  if (!pBus)
660  {
661  return kAudioUnitErr_InvalidProperty;
662  }
663  *pDataSize = sizeof(STREAM_DESC);
664  *pWriteable = true;
665  if (pData)
666  {
667  int nChannels = pBus->mNHostChannels; // Report how many channels the host has connected.
668  if (nChannels < 0) // Unless the host hasn't connected any yet, in which case report the default.
669  {
670  nChannels = pBus->mNPlugChannels;
671  }
672  STREAM_DESC* pASBD = (STREAM_DESC*) pData;
673  MakeDefaultASBD(pASBD, GetSampleRate(), nChannels, false);
674  }
675  return noErr;
676  }
677  case kAudioUnitProperty_ElementCount: // 11,
678  {
679  *pDataSize = sizeof(UInt32);
680  if (pData)
681  {
682  int n = 0;
683 
684  if (scope == kAudioUnitScope_Input)
685  n = mInBuses.GetSize();
686  else if (scope == kAudioUnitScope_Output)
687  n = mOutBuses.GetSize();
688  else if (scope == kAudioUnitScope_Global)
689  n = 1;
690 
691  *((UInt32*) pData) = n;
692  }
693  return noErr;
694  }
695  case kAudioUnitProperty_Latency: // 12, // listenable
696  {
697  ASSERT_SCOPE(kAudioUnitScope_Global);
698  *pDataSize = sizeof(Float64);
699  if (pData)
700  {
701  *((Float64*) pData) = (double) GetLatency() / GetSampleRate();
702  }
703  return noErr;
704  }
705  case kAudioUnitProperty_SupportedNumChannels: // 13,
706  {
707  ASSERT_SCOPE(kAudioUnitScope_Global);
708  int n = NIOConfigs(); //TODO: THIS IS INCORRECT!
709  *pDataSize = n * sizeof(AUChannelInfo);
710  if (pData)
711  {
712  AUChannelInfo* pChInfo = (AUChannelInfo*) pData;
713  for (int i = 0; i < n; ++i, ++pChInfo)
714  {
715  const IOConfig* pIO = GetIOConfig(i);
716 
717  if(pIO->ContainsWildcard(ERoute::kInput))
718  pChInfo->inChannels = -1;
719  else
720  pChInfo->inChannels = pIO->GetTotalNChannels(kInput);
721 
722  if(pIO->ContainsWildcard(ERoute::kOutput))
723  pChInfo->outChannels = -1;
724  else
725  pChInfo->outChannels = pIO->GetTotalNChannels(kOutput);
726 
727  Trace(TRACELOC, "IO:%d:%d", pChInfo->inChannels, pChInfo->outChannels);
728 
729  }
730  }
731  return noErr;
732  }
733  case kAudioUnitProperty_MaximumFramesPerSlice: // 14,
734  {
735  ASSERT_SCOPE(kAudioUnitScope_Global);
736  *pDataSize = sizeof(UInt32);
737  *pWriteable = true;
738  if (pData)
739  {
740  *((UInt32*) pData) = GetBlockSize();
741  }
742  return noErr;
743  }
744  NO_OP(kAudioUnitProperty_SetExternalBuffer); // 15,
745  case kAudioUnitProperty_ParameterValueStrings: // 16,
746  {
747  ASSERT_SCOPE(kAudioUnitScope_Global);
748  ASSERT_ELEMENT(NParams());
749  ENTER_PARAMS_MUTEX
750  IParam* pParam = GetParam(element);
751  int n = pParam->NDisplayTexts();
752  if (!n)
753  {
754  LEAVE_PARAMS_MUTEX
755  *pDataSize = 0;
756  return kAudioUnitErr_InvalidProperty;
757  }
758  *pDataSize = sizeof(CFArrayRef);
759  if (pData)
760  {
761  CFMutableArrayRef nameArray = CFArrayCreateMutable(kCFAllocatorDefault, n, &kCFTypeArrayCallBacks);
762  for (int i = 0; i < n; ++i)
763  {
764  const char* str = pParam->GetDisplayText(i);
765  CFStrLocal cfstr(str);
766  CFArrayAppendValue(nameArray, cfstr.Get());
767  }
768  *((CFArrayRef*) pData) = nameArray;
769  }
770  LEAVE_PARAMS_MUTEX
771  return noErr;
772  }
773  case kAudioUnitProperty_GetUIComponentList: // 18,
774  {
775  return kAudioUnitErr_InvalidProperty;
776  }
777  case kAudioUnitProperty_AudioChannelLayout:
778  {
779  return kAudioUnitErr_InvalidPropertyValue;
780  }
781  case kAudioUnitProperty_TailTime: // 20, // listenable
782  {
783  ASSERT_SCOPE(kAudioUnitScope_Global);
784  *pWriteable = GetTailSize() > 0;
785  *pDataSize = sizeof(Float64);
786 
787  if (pData)
788  {
789  *((Float64*) pData) = (double) GetTailSize() / GetSampleRate();
790  }
791  return noErr;
792  }
793  case kAudioUnitProperty_BypassEffect: // 21,
794  {
795  ASSERT_SCOPE(kAudioUnitScope_Global);
796  *pWriteable = true;
797  *pDataSize = sizeof(UInt32);
798  if (pData)
799  {
800  *((UInt32*) pData) = (GetBypassed() ? 1 : 0);
801  }
802  return noErr;
803  }
804  case kAudioUnitProperty_LastRenderError: // 22,
805  {
806  ASSERT_SCOPE(kAudioUnitScope_Global);
807  *pDataSize = sizeof(OSStatus);
808  if (pData)
809  {
810  *((OSStatus*) pData) = noErr;
811  }
812  return noErr;
813  }
814  case kAudioUnitProperty_SetRenderCallback: // 23,
815  {
816  ASSERT_INPUT_OR_GLOBAL_SCOPE;
817  if (element >= mInBuses.GetSize())
818  {
819  return kAudioUnitErr_InvalidProperty;
820  }
821  *pDataSize = sizeof(AURenderCallbackStruct);
822  *pWriteable = true;
823  return noErr;
824  }
825  case kAudioUnitProperty_FactoryPresets: // 24, // listenable
826  {
827  *pDataSize = sizeof(CFArrayRef);
828  if (pData)
829  {
830  #ifdef AU_NO_PRESETS
831  int i, n = 0;
832  #else
833  int i, n = NPresets();
834  #endif
835 
836  CFMutableArrayRef presetArray = CFArrayCreateMutable(kCFAllocatorDefault, n, &kCFAUPresetArrayCallBacks);
837 
838  if (presetArray == NULL)
839  return coreFoundationUnknownErr;
840 
841  for (i = 0; i < n; ++i)
842  {
843  CFStrLocal presetName(GetPresetName(i));
844  CFAUPresetRef newPreset = CFAUPresetCreate(kCFAllocatorDefault, i, presetName.Get()); // todo should i be 0 based?
845 
846  if (newPreset != NULL)
847  {
848  CFArrayAppendValue(presetArray, newPreset);
849  CFAUPresetRelease(newPreset);
850  }
851  }
852 
853  *((CFMutableArrayRef*) pData) = presetArray;
854  }
855  return noErr;
856  }
857  NO_OP(kAudioUnitProperty_ContextName); // 25,
858  NO_OP(kAudioUnitProperty_RenderQuality); // 26,
859  case kAudioUnitProperty_HostCallbacks: // 27,
860  {
861  ASSERT_SCOPE(kAudioUnitScope_Global);
862  *pDataSize = sizeof(HostCallbackInfo);
863  *pWriteable = true;
864  return noErr;
865  }
866  NO_OP(kAudioUnitProperty_InPlaceProcessing); // 29,
867  case kAudioUnitProperty_ElementName:
868  {
869  *pDataSize = sizeof(CFStringRef);
870  *pWriteable = false;
871  if (pData)
872  {
873  const int nIn = MaxNBuses(kInput);
874  const int nOut = MaxNBuses(kOutput);
875 
876  switch(scope)
877  {
878  case kAudioUnitScope_Input:
879  {
880  if (element < nIn)
881  {
882  WDL_String busName;
883  GetBusName(ERoute::kInput, (int) element, nIn, busName);
884  *(CFStringRef*) pData = MakeCFString(busName.Get());
885  return noErr;
886  }
887  else
888  return kAudioUnitErr_InvalidElement;
889  }
890  case kAudioUnitScope_Output:
891  {
892  if (element < nOut)
893  {
894  //TODO: live 5.1 crash?
895  WDL_String busName;
896  GetBusName(ERoute::kOutput, (int) element, nOut, busName);
897  *(CFStringRef*) pData = MakeCFString(busName.Get());
898  return noErr;
899  }
900  else
901  return kAudioUnitErr_InvalidElement;
902  }
903  default:
904  return kAudioUnitErr_InvalidScope;
905  }
906  }
907  return kAudioUnitErr_InvalidProperty;
908  }
909  case kAudioUnitProperty_CocoaUI: // 31,
910  {
911  if (HasUI()) // this won't work < 10.5 SDK but barely anyone will use that these days
912  {
913  *pDataSize = sizeof(AudioUnitCocoaViewInfo); // Just one view.
914  if (pData)
915  {
916  AudioUnitCocoaViewInfo* pViewInfo = (AudioUnitCocoaViewInfo*) pData;
917  CFStrLocal bundleID(mBundleID.Get());
918  CFBundleRef pBundle = CFBundleGetBundleWithIdentifier(bundleID.Get());
919  CFURLRef url = CFBundleCopyBundleURL(pBundle);
920  pViewInfo->mCocoaAUViewBundleLocation = url;
921  pViewInfo->mCocoaAUViewClass[0] = CFStringCreateWithCString(0, mCocoaViewFactoryClassName.Get(), kCFStringEncodingUTF8);
922  }
923  return noErr;
924  }
925  return kAudioUnitErr_InvalidProperty;
926  }
927 #pragma mark - kAudioUnitProperty_SupportedChannelLayoutTags
928  case kAudioUnitProperty_SupportedChannelLayoutTags:
929  {
930  if (!pData) // GetPropertyInfo
931  {
932  UInt32 numLayouts = GetChannelLayoutTags(scope, element, NULL); // 0 = invalid scope, 1 = input
933 
934  if (numLayouts)
935  {
936  *pDataSize = numLayouts * sizeof(AudioChannelLayoutTag);
937  *pWriteable = true;
938  return noErr;
939  }
940  else
941  {
942  *pDataSize = 0;
943  *pWriteable = false;
944  return noErr;
945  }
946  }
947  else // GetProperty
948  {
949  AudioChannelLayoutTag* ptr = pData ? static_cast<AudioChannelLayoutTag*>(pData) : NULL;
950 
951  if (GetChannelLayoutTags(scope, element, ptr))
952  {
953  return noErr;
954  }
955  }
956 
957  return kAudioUnitErr_InvalidProperty;
958  }
959  case kAudioUnitProperty_ParameterIDName: // 34,
960  {
961  *pDataSize = sizeof(AudioUnitParameterIDName);
962  if (pData && scope == kAudioUnitScope_Global)
963  {
964  AudioUnitParameterIDName* pIDName = (AudioUnitParameterIDName*) pData;
965  char cStr[MAX_PARAM_NAME_LEN];
966  ENTER_PARAMS_MUTEX
967  strcpy(cStr, GetParam(pIDName->inID)->GetName());
968  LEAVE_PARAMS_MUTEX
969  if (pIDName->inDesiredLength != kAudioUnitParameterName_Full)
970  {
971  int n = std::min<int>(MAX_PARAM_NAME_LEN - 1, pIDName->inDesiredLength);
972  cStr[n] = '\0';
973  }
974  pIDName->outName = CFStringCreateWithCString(0, cStr, kCFStringEncodingUTF8);
975  }
976  return noErr;
977  }
978  case kAudioUnitProperty_ParameterClumpName: // 35,
979  {
980  *pDataSize = sizeof (AudioUnitParameterNameInfo);
981  if (pData && scope == kAudioUnitScope_Global)
982  {
983  AudioUnitParameterNameInfo* parameterNameInfo = (AudioUnitParameterNameInfo *) pData;
984  int clumpId = parameterNameInfo->inID;
985 
986  if (clumpId < 1)
987  return kAudioUnitErr_PropertyNotInUse;
988 
989  parameterNameInfo->outName = CFStringCreateWithCString(0, GetParamGroupName(clumpId-1), kCFStringEncodingUTF8);
990  }
991  return noErr;
992  }
993  case kAudioUnitProperty_CurrentPreset: // 28,
994  case kAudioUnitProperty_PresentPreset: // 36, // listenable
995  {
996  *pDataSize = sizeof(AUPreset);
997  *pWriteable = true;
998  if (pData)
999  {
1000  AUPreset* pAUPreset = (AUPreset*) pData;
1001  pAUPreset->presetNumber = GetCurrentPresetIdx();
1002  const char* name = GetPresetName(pAUPreset->presetNumber);
1003  pAUPreset->presetName = CFStringCreateWithCString(0, name, kCFStringEncodingUTF8);
1004  }
1005  return noErr;
1006  }
1007  NO_OP(kAudioUnitProperty_OfflineRender); // 37,
1008  case kAudioUnitProperty_ParameterStringFromValue: // 33,
1009  {
1010  *pDataSize = sizeof(AudioUnitParameterStringFromValue);
1011  if (pData && scope == kAudioUnitScope_Global)
1012  {
1013  AudioUnitParameterStringFromValue* pSFV = (AudioUnitParameterStringFromValue*) pData;
1014  ENTER_PARAMS_MUTEX
1015  GetParam(pSFV->inParamID)->GetDisplay(*(pSFV->inValue), false, mParamDisplayStr);
1016  LEAVE_PARAMS_MUTEX
1017  pSFV->outString = MakeCFString((const char*) mParamDisplayStr.Get());
1018  }
1019  return noErr;
1020  }
1021  case kAudioUnitProperty_ParameterValueFromString: // 38,
1022  {
1023  *pDataSize = sizeof(AudioUnitParameterValueFromString);
1024  if (pData)
1025  {
1026  AudioUnitParameterValueFromString* pVFS = (AudioUnitParameterValueFromString*) pData;
1027  if (scope == kAudioUnitScope_Global)
1028  {
1029  CStrLocal cStr(pVFS->inString);
1030  ENTER_PARAMS_MUTEX
1031  const double v = GetParam(pVFS->inParamID)->StringToValue(cStr.Get());
1032  LEAVE_PARAMS_MUTEX
1033  pVFS->outValue = (AudioUnitParameterValue) v;
1034  }
1035  }
1036  return noErr;
1037  }
1038  NO_OP(kAudioUnitProperty_IconLocation); // 39,
1039  NO_OP(kAudioUnitProperty_PresentationLatency); // 40,
1040  NO_OP(kAudioUnitProperty_DependentParameters); // 45,
1041  case kMusicDeviceProperty_InstrumentCount:
1042  {
1043  ASSERT_SCOPE(kAudioUnitScope_Global);
1044  if (IsInstrument())
1045  {
1046  *pDataSize = sizeof(UInt32);
1047  if (pData)
1048  {
1049  *((UInt32*) pData) = 1; //(mIsBypassed ? 1 : 0);
1050  }
1051  return noErr;
1052  }
1053  else
1054  {
1055  return kAudioUnitErr_InvalidProperty;
1056  }
1057  }
1058 
1059  NO_OP(kAudioUnitProperty_AUHostIdentifier); // 46,
1060  case kAudioUnitProperty_MIDIOutputCallbackInfo: // 47,
1061  {
1062  if(pData == 0)
1063  {
1064  *pWriteable = false;
1065  *pDataSize = sizeof(CFArrayRef);
1066  }
1067  else
1068  {
1069  CFStringRef outputName = CFSTR("midiOut");
1070  CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void**) &outputName, 1, nullptr);
1071  CFRelease(outputName);
1072  *((CFArrayRef*) pData) = array;
1073  }
1074  return noErr;
1075  }
1076  case kAudioUnitProperty_MIDIOutputCallback: // 48,
1077  {
1078  if(pData == 0)
1079  {
1080  *pWriteable = true;
1081  *pDataSize = sizeof(AUMIDIOutputCallbackStruct);
1082 
1083  return noErr;
1084  }
1085  }
1086  NO_OP(kAudioUnitProperty_InputSamplesInOutput); // 49,
1087  NO_OP(kAudioUnitProperty_ClassInfoFromDocument); // 50
1088  case kAudioUnitProperty_SupportsMPE: // 58
1089  {
1090  if(pData == 0)
1091  {
1092  *pWriteable = false;
1093  *pDataSize = sizeof(UInt32);
1094  }
1095  else
1096  {
1097  *((UInt32*) pData) = (DoesMPE() ? 1 : 0);
1098  }
1099  return noErr;
1100  }
1101  default:
1102  {
1103  return kAudioUnitErr_InvalidProperty;
1104  }
1105  }
1106 }
1107 
1108 OSStatus IPlugAU::SetProperty(AudioUnitPropertyID propID, AudioUnitScope scope, AudioUnitElement element,
1109  UInt32* pDataSize, const void* pData)
1110 {
1111  Trace(TRACELOC, "(%d:%s):(%d:%s):%d", propID, AUPropertyStr(propID), scope, AUScopeStr(scope), element);
1112 
1113  InformListeners(propID, scope);
1114 
1115  switch (propID)
1116  {
1117  case kAudioUnitProperty_ClassInfo: // 0,
1118  {
1119  return SetState(*((CFPropertyListRef*) pData));
1120  }
1121  case kAudioUnitProperty_MakeConnection: // 1,
1122  {
1123  ASSERT_INPUT_OR_GLOBAL_SCOPE;
1124  AudioUnitConnection* pAUC = (AudioUnitConnection*) pData;
1125  if (pAUC->destInputNumber >= mInBusConnections.GetSize())
1126  {
1127  return kAudioUnitErr_InvalidProperty;
1128  }
1129  InputBusConnection* pInBusConn = mInBusConnections.Get(pAUC->destInputNumber);
1130  memset(pInBusConn, 0, sizeof(InputBusConnection));
1131  bool negotiatedOK = true;
1132  if (pAUC->sourceAudioUnit) // Opening connection.
1133  {
1134  AudioStreamBasicDescription srcASBD;
1135  UInt32 size = sizeof(AudioStreamBasicDescription);
1136  negotiatedOK = // Ask whoever is sending us audio what the format is.
1137  (AudioUnitGetProperty(pAUC->sourceAudioUnit, kAudioUnitProperty_StreamFormat,
1138  kAudioUnitScope_Output, pAUC->sourceOutputNumber, &srcASBD, &size) == noErr);
1139  negotiatedOK &= // Try to set our own format to match.
1140  (SetProperty(kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1141  pAUC->destInputNumber, &size, &srcASBD) == noErr);
1142  if (negotiatedOK) // Connection terms successfully negotiated.
1143  {
1144  pInBusConn->mUpstreamUnit = pAUC->sourceAudioUnit;
1145  pInBusConn->mUpstreamBusIdx = pAUC->sourceOutputNumber;
1146  // Will the upstream unit give us a fast render proc for input?
1147  AudioUnitRenderProc srcRenderProc;
1148  size = sizeof(AudioUnitRenderProc);
1149  if (AudioUnitGetProperty(pAUC->sourceAudioUnit, kAudioUnitProperty_FastDispatch, kAudioUnitScope_Global, kAudioUnitRenderSelect,
1150  &srcRenderProc, &size) == noErr)
1151  {
1152  // Yes, we got a fast render proc, and we also need to store the pointer to the upstream audio unit object.
1153  pInBusConn->mUpstreamRenderProc = srcRenderProc;
1154  pInBusConn->mUpstreamObj = GetComponentInstanceStorage(pAUC->sourceAudioUnit);
1155  }
1156  // Else no fast render proc, so leave the input bus connection struct's upstream render proc and upstream object empty,
1157  // and we will need to make a component call through the component manager to get input data.
1158  }
1159  // Else this is a call to close the connection, which we effectively did by clearing the InputBusConnection struct,
1160  // which counts as a successful negotiation.
1161  }
1162  AssessInputConnections();
1163  return (negotiatedOK ? noErr : (int) kAudioUnitErr_InvalidProperty); // casting to int avoids gcc error
1164  }
1165  case kAudioUnitProperty_SampleRate: // 2,
1166  {
1167  SetSampleRate(*((Float64*) pData));
1168  OnReset();
1169  return noErr;
1170  }
1171  NO_OP(kAudioUnitProperty_ParameterList); // 3,
1172  NO_OP(kAudioUnitProperty_ParameterInfo); // 4,
1173  NO_OP(kAudioUnitProperty_FastDispatch); // 5,
1174  NO_OP(kAudioUnitProperty_CPULoad); // 6,
1175  case kAudioUnitProperty_StreamFormat: // 8,
1176  {
1177  AudioStreamBasicDescription* pASBD = (AudioStreamBasicDescription*) pData;
1178  int nHostChannels = pASBD->mChannelsPerFrame;
1179  BusChannels* pBus = GetBus(scope, element);
1180  if (!pBus)
1181  {
1182  return kAudioUnitErr_InvalidProperty;
1183  }
1184  pBus->mNHostChannels = 0;
1185  // The connection is OK if the plugin expects the same number of channels as the host is attempting to connect,
1186  // or if the plugin supports mono channels (meaning it's flexible about how many inputs to expect)
1187  // and the plugin supports at least as many channels as the host is attempting to connect.
1188  bool connectionOK = (nHostChannels > 0);
1189  connectionOK &= CheckLegalIO(scope, element, nHostChannels);
1190  connectionOK &= (pASBD->mFormatID == kAudioFormatLinearPCM && pASBD->mFormatFlags & kAudioFormatFlagsCanonical);
1191 
1192  Trace(TRACELOC, "%d:%d:%s:%s:%s",
1193  nHostChannels, pBus->mNPlugChannels,
1194  (pASBD->mFormatID == kAudioFormatLinearPCM ? "linearPCM" : "notLinearPCM"),
1195  (pASBD->mFormatFlags & kAudioFormatFlagsCanonical ? "canonicalFormat" : "notCanonicalFormat"),
1196  (connectionOK ? "connectionOK" : "connectionNotOK"));
1197 
1198  // bool interleaved = !(pASBD->mFormatFlags & kAudioFormatFlagIsNonInterleaved);
1199  if (connectionOK)
1200  {
1201  pBus->mNHostChannels = nHostChannels;
1202  if (pASBD->mSampleRate > 0.0)
1203  {
1204  SetSampleRate(pASBD->mSampleRate);
1205  }
1206  }
1207  AssessInputConnections();
1208  return (connectionOK ? noErr : (int) kAudioUnitErr_InvalidProperty); // casting to int avoids gcc error
1209  }
1210  NO_OP(kAudioUnitProperty_ElementCount); // 11,
1211  NO_OP(kAudioUnitProperty_Latency); // 12,
1212  NO_OP(kAudioUnitProperty_SupportedNumChannels); // 13,
1213  case kAudioUnitProperty_MaximumFramesPerSlice: // 14,
1214  {
1215  SetBlockSize(*((UInt32*) pData));
1216  ResizeScratchBuffers();
1217  OnReset();
1218  return noErr;
1219  }
1220  NO_OP(kAudioUnitProperty_SetExternalBuffer); // 15,
1221  NO_OP(kAudioUnitProperty_ParameterValueStrings); // 16,
1222  NO_OP(kAudioUnitProperty_GetUIComponentList); // 18,
1223  NO_OP(kAudioUnitProperty_AudioChannelLayout); // 19, //TODO?: Set kAudioUnitProperty_AudioChannelLayout
1224  NO_OP(kAudioUnitProperty_TailTime); // 20,
1225  case kAudioUnitProperty_BypassEffect: // 21,
1226  {
1227  const bool bypassed = *((UInt32*) pData) != 0;
1228  SetBypassed(bypassed);
1229 
1230  // TODO: should the following be called here?
1231  OnActivate(!bypassed);
1232  OnReset();
1233  return noErr;
1234  }
1235  NO_OP(kAudioUnitProperty_LastRenderError); // 22,
1236  case kAudioUnitProperty_SetRenderCallback: // 23,
1237  {
1238  ASSERT_SCOPE(kAudioUnitScope_Input); // if global scope, set all
1239  if (element >= mInBusConnections.GetSize())
1240  {
1241  return kAudioUnitErr_InvalidProperty;
1242  }
1243  InputBusConnection* pInBusConn = mInBusConnections.Get(element);
1244  memset(pInBusConn, 0, sizeof(InputBusConnection));
1245  AURenderCallbackStruct* pCS = (AURenderCallbackStruct*) pData;
1246  if (pCS->inputProc != 0)
1247  {
1248  pInBusConn->mUpstreamRenderCallback = *pCS;
1249  }
1250  AssessInputConnections();
1251  return noErr;
1252  }
1253  NO_OP(kAudioUnitProperty_FactoryPresets); // 24,
1254  //NO_OP(kAudioUnitProperty_ContextName); // 25,
1255  case kAudioUnitProperty_ContextName:
1256  {
1257  CFStringRef inStr = *(CFStringRef *)pData;
1258  CFIndex bufferSize = CFStringGetLength(inStr) + 1; // The +1 is for having space for the string to be NUL terminated
1259  char buffer[bufferSize];
1260  if (CFStringGetCString(inStr, buffer, bufferSize, kCFStringEncodingUTF8))
1261  {
1262  mTrackName.Set(buffer);
1263  }
1264  return noErr;
1265  }
1266  NO_OP(kAudioUnitProperty_RenderQuality); // 26,
1267  case kAudioUnitProperty_HostCallbacks: // 27,
1268  {
1269  ASSERT_SCOPE(kAudioUnitScope_Global);
1270  memcpy(&mHostCallbacks, pData, sizeof(HostCallbackInfo));
1271  return noErr;
1272  }
1273  NO_OP(kAudioUnitProperty_InPlaceProcessing); // 29,
1274  NO_OP(kAudioUnitProperty_ElementName); // 30,
1275  NO_OP(kAudioUnitProperty_CocoaUI); // 31,
1276  NO_OP(kAudioUnitProperty_SupportedChannelLayoutTags); // 32,
1277  NO_OP(kAudioUnitProperty_ParameterIDName); // 34,
1278  NO_OP(kAudioUnitProperty_ParameterClumpName); // 35,
1279  case kAudioUnitProperty_CurrentPreset: // 28,
1280  case kAudioUnitProperty_PresentPreset: // 36,
1281  {
1282  int presetIdx = ((AUPreset*) pData)->presetNumber;
1283  RestorePreset(presetIdx);
1284  return noErr;
1285  }
1286  case kAudioUnitProperty_OfflineRender: // 37,
1287  {
1288  const bool renderingOffline = (*((UInt32*) pData) != 0);
1289  SetRenderingOffline(renderingOffline);
1290  return noErr;
1291  }
1292  NO_OP(kAudioUnitProperty_ParameterStringFromValue); // 33,
1293  NO_OP(kAudioUnitProperty_ParameterValueFromString); // 38,
1294  NO_OP(kAudioUnitProperty_IconLocation); // 39,
1295  NO_OP(kAudioUnitProperty_PresentationLatency); // 40,
1296  NO_OP(kAudioUnitProperty_DependentParameters); // 45,
1297  case kAudioUnitProperty_AUHostIdentifier: // 46,
1298  {
1299  if (GetHost() == kHostUninit)
1300  {
1301  AUHostIdentifier* pHostID = (AUHostIdentifier*) pData;
1302  CStrLocal hostStr(pHostID->hostName);
1303  int version = (pHostID->hostVersion.majorRev << 16)
1304  + ((pHostID->hostVersion.minorAndBugRev & 0xF0) << 4)
1305  + ((pHostID->hostVersion.minorAndBugRev & 0x0F));
1306  SetHost(hostStr.Get(), version);
1307  }
1308  return noErr;
1309  }
1310  NO_OP(kAudioUnitProperty_MIDIOutputCallbackInfo); // 47,
1311  case kAudioUnitProperty_MIDIOutputCallback: // 48,
1312  {
1313  mMidiCallback = *((AUMIDIOutputCallbackStruct*) pData);
1314  return noErr;
1315  }
1316  NO_OP(kAudioUnitProperty_InputSamplesInOutput); // 49,
1317  NO_OP(kAudioUnitProperty_ClassInfoFromDocument) // 50
1318  default:
1319  {
1320  return kAudioUnitErr_InvalidProperty;
1321  }
1322  }
1323 }
1324 
1325 //static
1326 const char* IPlugAU::AUInputTypeStr(int type)
1327 {
1328  switch ((IPlugAU::EAUInputType) type)
1329  {
1330  case IPlugAU::eDirectFastProc: return "DirectFastProc";
1331  case IPlugAU::eDirectNoFastProc: return "DirectNoFastProc";
1332  case IPlugAU::eRenderCallback: return "RenderCallback";
1333  case IPlugAU::eNotConnected:
1334  default: return "NotConnected";
1335  }
1336 }
1337 
1338 int IPlugAU::NHostChannelsConnected(WDL_PtrList<BusChannels>* pBuses, int excludeIdx)
1339 {
1340  bool init = false;
1341  int nCh = 0, n = pBuses->GetSize();
1342 
1343  for (int i = 0; i < n; ++i)
1344  {
1345  if (i != excludeIdx)
1346  {
1347  int nHostChannels = pBuses->Get(i)->mNHostChannels;
1348  if (nHostChannels >= 0)
1349  {
1350  nCh += nHostChannels;
1351  init = true;
1352  }
1353  }
1354  }
1355 
1356  if (init)
1357  {
1358  return nCh;
1359  }
1360 
1361  return -1;
1362 }
1363 
1364 bool IPlugAU::CheckLegalIO(AudioUnitScope scope, int busIdx, int nChannels)
1365 {
1366  if (scope == kAudioUnitScope_Input)
1367  {
1368  int nIn = std::max(NHostChannelsConnected(&mInBuses, busIdx), 0);
1369  int nOut = (mActive ? NHostChannelsConnected(&mOutBuses) : -1);
1370  return LegalIO(nIn + nChannels, nOut);
1371  }
1372  else
1373  {
1374  int nIn = (mActive ? NHostChannelsConnected(&mInBuses) : -1);
1375  int nOut = std::max(NHostChannelsConnected(&mOutBuses, busIdx), 0);
1376  return LegalIO(nIn, nOut + nChannels);
1377  }
1378 }
1379 
1380 bool IPlugAU::CheckLegalIO()
1381 {
1382  int nIn = NHostChannelsConnected(&mInBuses);
1383  int nOut = NHostChannelsConnected(&mOutBuses);
1384  return ((!nIn && !nOut) || LegalIO(nIn, nOut));
1385 }
1386 
1387 void IPlugAU::AssessInputConnections()
1388 {
1389  TRACE
1390  SetChannelConnections(ERoute::kInput, 0, MaxNChannels(ERoute::kInput), false);
1391 
1392  int nIn = mInBuses.GetSize();
1393  for (int i = 0; i < nIn; ++i)
1394  {
1395  BusChannels* pInBus = mInBuses.Get(i);
1396  InputBusConnection* pInBusConn = mInBusConnections.Get(i);
1397 
1398  // AU supports 3 ways to get input from the host (or whoever is upstream).
1399  if (pInBusConn->mUpstreamRenderProc && pInBusConn->mUpstreamObj)
1400  {
1401  // 1: direct input connection with fast render proc (and buffers) supplied by the upstream unit.
1402  pInBusConn->mInputType = eDirectFastProc;
1403  }
1404  else if (pInBusConn->mUpstreamUnit)
1405  {
1406  // 2: direct input connection with no render proc, buffers supplied by the upstream unit.
1407  pInBusConn->mInputType = eDirectNoFastProc;
1408  }
1409  else if (pInBusConn->mUpstreamRenderCallback.inputProc)
1410  {
1411  // 3: no direct connection, render callback, buffers supplied by us.
1412  pInBusConn->mInputType = eRenderCallback;
1413  }
1414  else
1415  {
1416  pInBusConn->mInputType = eNotConnected;
1417  }
1418  pInBus->mConnected = (pInBusConn->mInputType != eNotConnected);
1419 
1420  int startChannelIdx = pInBus->mPlugChannelStartIdx;
1421  if (pInBus->mConnected)
1422  {
1423  // There's an input connection, so we need to tell the plug to expect however many channels
1424  // are in the negotiated host stream format.
1425  if (pInBus->mNHostChannels < 0)
1426  {
1427  // The host set up a connection without specifying how many channels in the stream.
1428  // Assume the host will send all the channels the plugin asks for, and hope for the best.
1429  Trace(TRACELOC, "AssumeChannels:%d", pInBus->mNPlugChannels);
1430  pInBus->mNHostChannels = pInBus->mNPlugChannels;
1431  }
1432  int nConnected = pInBus->mNHostChannels;
1433  int nUnconnected = std::max(pInBus->mNPlugChannels - nConnected, 0);
1434  SetChannelConnections(ERoute::kInput, startChannelIdx, nConnected, true);
1435  SetChannelConnections(ERoute::kInput, startChannelIdx + nConnected, nUnconnected, false);
1436  }
1437 
1438  Trace(TRACELOC, "%d:%s:%d:%d:%d", i, AUInputTypeStr(pInBusConn->mInputType), startChannelIdx, pInBus->mNPlugChannels, pInBus->mNHostChannels);
1439  }
1440 }
1441 
1442 OSStatus IPlugAU::GetState(CFPropertyListRef* ppPropList)
1443 {
1444  int plugType = GetAUPluginType();
1445  int plugSubType = GetUniqueID();
1446  int plugManID = GetMfrID();
1447 
1448  CFMutableDictionaryRef pDict = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1449  int version = GetPluginVersion(false);
1450  PutNumberInDict(pDict, kAUPresetVersionKey, &version, kCFNumberSInt32Type);
1451  PutNumberInDict(pDict, kAUPresetTypeKey, &(plugType), kCFNumberSInt32Type);
1452  PutNumberInDict(pDict, kAUPresetSubtypeKey, &(plugSubType), kCFNumberSInt32Type);
1453  PutNumberInDict(pDict, kAUPresetManufacturerKey, &(plugManID), kCFNumberSInt32Type);
1454  PutStrInDict(pDict, kAUPresetNameKey, GetPresetName(GetCurrentPresetIdx()));
1455 
1456  IByteChunk chunk;
1457  //InitChunkWithIPlugVer(&IPlugChunk); // TODO: IPlugVer should be in chunk!
1458 
1459  if (SerializeState(chunk))
1460  {
1461  PutDataInDict(pDict, kAUPresetDataKey, &chunk);
1462  }
1463 
1464  *ppPropList = pDict;
1465  TRACE
1466  return noErr;
1467 }
1468 
1469 OSStatus IPlugAU::SetState(CFPropertyListRef pPropList)
1470 {
1471  CFDictionaryRef pDict = (CFDictionaryRef) pPropList;
1472  int version, type, subtype, mfr;
1473  char presetName[64];
1474  if (!GetNumberFromDict(pDict, kAUPresetVersionKey, &version, kCFNumberSInt32Type) ||
1475  !GetNumberFromDict(pDict, kAUPresetTypeKey, &type, kCFNumberSInt32Type) ||
1476  !GetNumberFromDict(pDict, kAUPresetSubtypeKey, &subtype, kCFNumberSInt32Type) ||
1477  !GetNumberFromDict(pDict, kAUPresetManufacturerKey, &mfr, kCFNumberSInt32Type) ||
1478  !GetStrFromDict(pDict, kAUPresetNameKey, presetName) ||
1479  //version != GetPluginVersion(false) ||
1480  type != GetAUPluginType() ||
1481  subtype != GetUniqueID() ||
1482  mfr != GetMfrID())
1483  {
1484  return kAudioUnitErr_InvalidPropertyValue;
1485  }
1486 
1487  RestorePreset(presetName);
1488 
1489  IByteChunk chunk;
1490 
1491  if (!GetDataFromDict(pDict, kAUPresetDataKey, &chunk))
1492  {
1493  return kAudioUnitErr_InvalidPropertyValue;
1494  }
1495 
1496  // TODO: IPlugVer should be in chunk!
1497  // int pos;
1498  // IByteChunk::GetIPlugVerFromChunk(chunk, pos)
1499 
1500  if (!UnserializeState(chunk, 0))
1501  {
1502  return kAudioUnitErr_InvalidPropertyValue;
1503  }
1504 
1505  OnRestoreState();
1506  return noErr;
1507 }
1508 
1509 // pData == 0 means return property info only.
1510 OSStatus IPlugAU::GetProc(AudioUnitElement element, UInt32* pDataSize, void* pData)
1511 {
1512  Trace(TRACELOC, "%s:(%d:%s)", (pData ? "" : "Info"), element, AUSelectStr(element));
1513 
1514  switch (element)
1515  {
1516  //TODO: WHAT ABOUT THESE!!!
1517 // case kAudioUnitGetParameterSelect:
1518 // {
1519 // *pDataSize = sizeof(AudioUnitGetParameterProc);
1520 // if (pData)
1521 // {
1522 // *((AudioUnitGetParameterProc*) pData) = (AudioUnitGetParameterProc) IPlugAU::GetParamProc;
1523 // }
1524 // return noErr;
1525 // }
1526 // case kAudioUnitSetParameterSelect:
1527 // {
1528 // *pDataSize = sizeof(AudioUnitSetParameterProc);
1529 // if (pData)
1530 // {
1531 // *((AudioUnitSetParameterProc*) pData) = (AudioUnitSetParameterProc) IPlugAU::SetParamProc;
1532 // }
1533 // return noErr;
1534 // }
1535 // case kAudioUnitRenderSelect:
1536 // {
1537 // *pDataSize = sizeof(AudioUnitRenderProc);
1538 // if (pData)
1539 // {
1540 // *((AudioUnitRenderProc*) pData) = (AudioUnitRenderProc) IPlugAU::RenderProc;
1541 // }
1542 // return noErr;
1543 // }
1544  default:
1545  return kAudioUnitErr_InvalidElement;
1546  }
1547 }
1548 
1549 // static
1550 OSStatus IPlugAU::GetParamProc(void* pPlug, AudioUnitParameterID paramID, AudioUnitScope scope, AudioUnitElement element, AudioUnitParameterValue* pValue)
1551 {
1552  Trace(TRACELOC, "%d:(%d:%s):%d", paramID, scope, AUScopeStr(scope), element);
1553 
1554  ASSERT_SCOPE(kAudioUnitScope_Global);
1555  IPlugAU* _this = (IPlugAU*) pPlug;
1556  assert(_this != NULL);
1557  ENTER_PARAMS_MUTEX_STATIC
1558  *pValue = _this->GetParam(paramID)->Value();
1559  LEAVE_PARAMS_MUTEX_STATIC
1560  return noErr;
1561 }
1562 
1563 // static
1564 OSStatus IPlugAU::SetParamProc(void* pPlug, AudioUnitParameterID paramID, AudioUnitScope scope, AudioUnitElement element, AudioUnitParameterValue value, UInt32 offsetFrames)
1565 {
1566  Trace(TRACELOC, "%d:(%d:%s):%d", paramID, scope, AUScopeStr(scope), element);
1567 
1568  // In the SDK, offset frames is only looked at in group scope.
1569  ASSERT_SCOPE(kAudioUnitScope_Global);
1570  IPlugAU* _this = (IPlugAU*) pPlug;
1571  ENTER_PARAMS_MUTEX_STATIC
1572  _this->GetParam(paramID)->Set(value);
1573  _this->SendParameterValueFromAPI(paramID, value, false);
1574  _this->OnParamChange(paramID, kHost);
1575  LEAVE_PARAMS_MUTEX_STATIC
1576  return noErr;
1577 }
1578 
1579 static inline OSStatus RenderCallback(AURenderCallbackStruct* pCB, AudioUnitRenderActionFlags* pFlags, const AudioTimeStamp* pTimestamp, UInt32 inputBusIdx, UInt32 nFrames, AudioBufferList* pOutBufList)
1580 {
1581  TRACE
1582  return pCB->inputProc(pCB->inputProcRefCon, pFlags, pTimestamp, inputBusIdx, nFrames, pOutBufList);
1583 }
1584 
1585 // static
1586 OSStatus IPlugAU::RenderProc(void* pPlug, AudioUnitRenderActionFlags* pFlags, const AudioTimeStamp* pTimestamp,
1587  UInt32 outputBusIdx, UInt32 nFrames, AudioBufferList* pOutBufList)
1588 {
1589  Trace(TRACELOC, "%d:%d:%d", outputBusIdx, pOutBufList->mNumberBuffers, nFrames);
1590 
1591  IPlugAU* _this = (IPlugAU*) pPlug;
1592 
1593  _this->mLastRenderTimeStamp = *pTimestamp;
1594 
1595  if (!(pTimestamp->mFlags & kAudioTimeStampSampleTimeValid) /*|| outputBusIdx >= _this->mOutBuses.GetSize()*/ || nFrames > _this->GetBlockSize())
1596  {
1597  return kAudioUnitErr_InvalidPropertyValue;
1598  }
1599 
1600  int nRenderNotify = _this->mRenderNotify.GetSize();
1601 
1602  if (nRenderNotify)
1603  {
1604  for (int i = 0; i < nRenderNotify; ++i)
1605  {
1606  AURenderCallbackStruct* pRN = _this->mRenderNotify.Get(i);
1607  AudioUnitRenderActionFlags flags = kAudioUnitRenderAction_PreRender;
1608  RenderCallback(pRN, &flags, pTimestamp, outputBusIdx, nFrames, pOutBufList);
1609  }
1610  }
1611 
1612  int lastConnectedOutputBus = -1;
1613 
1614  if(!_this->IsMidiEffect())
1615  {
1616  double renderSampleTime = pTimestamp->mSampleTime;
1617 
1618  // Pull input buffers.
1619  if (renderSampleTime != _this->mLastRenderSampleTime)
1620  {
1621  BufferList bufList;
1622  AudioBufferList* pInBufList = (AudioBufferList*) &bufList;
1623 
1624  int nIn = _this->mInBuses.GetSize();
1625 
1626  for (int i = 0; i < nIn; ++i)
1627  {
1628  BusChannels* pInBus = _this->mInBuses.Get(i);
1629  InputBusConnection* pInBusConn = _this->mInBusConnections.Get(i);
1630 
1631  if (pInBus->mConnected)
1632  {
1633  pInBufList->mNumberBuffers = pInBus->mNHostChannels;
1634 
1635  for (int b = 0; b < pInBufList->mNumberBuffers; ++b)
1636  {
1637  AudioBuffer* pBuffer = &(pInBufList->mBuffers[b]);
1638  pBuffer->mNumberChannels = 1;
1639  pBuffer->mDataByteSize = nFrames * sizeof(AudioSampleType);
1640  pBuffer->mData = 0;
1641  }
1642 
1643  AudioUnitRenderActionFlags flags = 0;
1644  OSStatus r = noErr;
1645 
1646  switch (pInBusConn->mInputType)
1647  {
1648  case eDirectFastProc:
1649  {
1650  r = pInBusConn->mUpstreamRenderProc(pInBusConn->mUpstreamObj, &flags, pTimestamp, pInBusConn->mUpstreamBusIdx, nFrames, pInBufList);
1651  break;
1652  }
1653  case eDirectNoFastProc:
1654  {
1655  r = AudioUnitRender(pInBusConn->mUpstreamUnit, &flags, pTimestamp, pInBusConn->mUpstreamBusIdx, nFrames, pInBufList);
1656  break;
1657  }
1658  case eRenderCallback:
1659  {
1660  AudioSampleType* pScratchInput = _this->mInScratchBuf.Get() + pInBus->mPlugChannelStartIdx * nFrames;
1661 
1662  for (int b = 0; b < pInBufList->mNumberBuffers; ++b, pScratchInput += nFrames)
1663  {
1664  pInBufList->mBuffers[b].mData = pScratchInput;
1665  }
1666 
1667  r = RenderCallback(&(pInBusConn->mUpstreamRenderCallback), &flags, pTimestamp, i, nFrames, pInBufList);
1668  break;
1669  }
1670  default:
1671  break;
1672  }
1673 
1674  if (r != noErr)
1675  {
1676  return r; // Something went wrong upstream.
1677  }
1678 
1679  for (int c = 0, chIdx = pInBus->mPlugChannelStartIdx; c < pInBus->mNHostChannels; ++c, ++chIdx)
1680  {
1681  _this->AttachBuffers(ERoute::kInput, chIdx, 1, (AudioSampleType**) &(pInBufList->mBuffers[c].mData), nFrames);
1682  }
1683  }
1684  }
1685  _this->mLastRenderSampleTime = renderSampleTime;
1686  }
1687 
1688  BusChannels* pOutBus = _this->mOutBuses.Get(outputBusIdx);
1689 
1690  // if this bus is not connected OR the number of buffers that the host has given are not equal to the number the bus expects
1691  if (!(pOutBus->mConnected) || pOutBus->mNHostChannels != pOutBufList->mNumberBuffers)
1692  {
1693  const int startChannelIdx = pOutBus->mPlugChannelStartIdx;
1694  const int nConnected = std::max(pOutBus->mNHostChannels, static_cast<int>(pOutBufList->mNumberBuffers));
1695  const int nUnconnected = std::max(pOutBus->mNPlugChannels - nConnected, 0);
1696 
1697  assert(nConnected > -1);
1698  _this->SetChannelConnections(ERoute::kOutput, startChannelIdx, nConnected, true);
1699  _this->SetChannelConnections(ERoute::kOutput, startChannelIdx + nConnected, nUnconnected, false); // This will disconnect the right hand channel on a single stereo bus
1700  pOutBus->mConnected = true;
1701  }
1702 
1703  for (int c = 0, chIdx = pOutBus->mPlugChannelStartIdx; c < pOutBufList->mNumberBuffers; ++c, ++chIdx)
1704  {
1705  if (!(pOutBufList->mBuffers[c].mData)) // Downstream unit didn't give us buffers.
1706  pOutBufList->mBuffers[c].mData = _this->mOutScratchBuf.Get() + chIdx * nFrames;
1707 
1708  _this->AttachBuffers(ERoute::kOutput, chIdx, 1, (AudioSampleType**) &(pOutBufList->mBuffers[c].mData), nFrames);
1709  }
1710 
1711  for(int i = 0; i < _this->mOutBuses.GetSize(); i++)
1712  {
1713  if(!_this->mOutBuses.Get(i)->mConnected)
1714  {
1715  break;
1716  }
1717  else
1718  {
1719  lastConnectedOutputBus++;
1720  }
1721  }
1722  }
1723 
1724  if (_this->IsMidiEffect() || outputBusIdx == lastConnectedOutputBus)
1725  {
1726  int busIdx1based = outputBusIdx+1;
1727 
1728  if (busIdx1based < _this->mOutBuses.GetSize() /*&& (_this->GetHost() != kHostAbletonLive)*/)
1729  {
1730  int totalNumChans = _this->mOutBuses.GetSize() * 2; // stereo only for the time being
1731  int nConnected = busIdx1based * 2;
1732  _this->SetChannelConnections(ERoute::kOutput, nConnected, totalNumChans - nConnected, false); // this will disconnect the channels that are on the unconnected buses
1733  }
1734 
1735  if (_this->GetBypassed())
1736  {
1737  _this->PassThroughBuffers((AudioSampleType) 0, nFrames);
1738  }
1739  else
1740  {
1741  if(_this->mMidiMsgsFromEditor.ElementsAvailable())
1742  {
1743  IMidiMsg msg;
1744 
1745  while (_this->mMidiMsgsFromEditor.Pop(msg))
1746  {
1747  _this->ProcessMidiMsg(msg);
1748  }
1749  }
1750 
1751  _this->PreProcess();
1752  ENTER_PARAMS_MUTEX
1753  _this->ProcessBuffers((AudioSampleType) 0, nFrames);
1754  LEAVE_PARAMS_MUTEX
1755  }
1756  }
1757 
1758  if (nRenderNotify)
1759  {
1760  for (int i = 0; i < nRenderNotify; ++i)
1761  {
1762  AURenderCallbackStruct* pRN = _this->mRenderNotify.Get(i);
1763  AudioUnitRenderActionFlags flags = kAudioUnitRenderAction_PostRender;
1764  RenderCallback(pRN, &flags, pTimestamp, outputBusIdx, nFrames, pOutBufList);
1765  }
1766  }
1767 
1768  _this->OutputSysexFromEditor();
1769 
1770  return noErr;
1771 }
1772 
1773 IPlugAU::BusChannels* IPlugAU::GetBus(AudioUnitScope scope, AudioUnitElement busIdx)
1774 {
1775  if (scope == kAudioUnitScope_Input && busIdx < mInBuses.GetSize())
1776  {
1777  return mInBuses.Get(busIdx);
1778  }
1779  if (scope == kAudioUnitScope_Output && busIdx < mOutBuses.GetSize())
1780  {
1781  return mOutBuses.Get(busIdx);
1782  }
1783  // Global bus is an alias for output bus zero.
1784  if (scope == kAudioUnitScope_Global && mOutBuses.GetSize())
1785  {
1786  return mOutBuses.Get(busIdx);
1787  }
1788  return 0;
1789 }
1790 
1791 void IPlugAU::ClearConnections()
1792 {
1793  int nInBuses = mInBuses.GetSize();
1794  for (int i = 0; i < nInBuses; ++i)
1795  {
1796  BusChannels* pInBus = mInBuses.Get(i);
1797  pInBus->mConnected = false;
1798  pInBus->mNHostChannels = -1;
1799  InputBusConnection* pInBusConn = mInBusConnections.Get(i);
1800  memset(pInBusConn, 0, sizeof(InputBusConnection));
1801  }
1802  int nOutBuses = mOutBuses.GetSize();
1803  for (int i = 0; i < nOutBuses; ++i)
1804  {
1805  BusChannels* pOutBus = mOutBuses.Get(i);
1806  pOutBus->mConnected = false;
1807  pOutBus->mNHostChannels = -1;
1808  }
1809 }
1810 
1811 #pragma mark - IPlugAU Constructor
1812 
1813 IPlugAU::IPlugAU(const InstanceInfo& info, const Config& config)
1814 : IPlugAPIBase(config, kAPIAU)
1815 , IPlugProcessor(config, kAPIAU)
1816 {
1817  Trace(TRACELOC, "%s", config.pluginName);
1818 
1819  memset(&mHostCallbacks, 0, sizeof(HostCallbackInfo));
1820  memset(&mMidiCallback, 0, sizeof(AUMIDIOutputCallbackStruct));
1821 
1822  mCocoaViewFactoryClassName.Set(info.mCocoaViewFactoryClassName.Get());
1823 
1824  int maxNIBuses = MaxNBuses(ERoute::kInput);
1825 
1826  // for an instrument with no sidechain input, we'll have 1 bus with 0 channels in the channel configs, but we don't want input bus here
1827  if(maxNIBuses == 1 && MaxNChannelsForBus(kInput, 0) == 0)
1828  {
1829  maxNIBuses = 0;
1830  }
1831 
1832  const int maxNOBuses = MaxNBuses(ERoute::kOutput);
1833 
1834  if(maxNIBuses)
1835  {
1836  PtrListInitialize(&mInBusConnections, maxNIBuses);
1837  PtrListInitialize(&mInBuses, maxNIBuses);
1838  }
1839 
1840  int chansSoFar = 0;
1841 
1842  for (auto bus = 0; bus < maxNIBuses; bus++)
1843  {
1844  BusChannels* pInBus = mInBuses.Get(bus);
1845  pInBus->mNHostChannels = -1;
1846  pInBus->mPlugChannelStartIdx = chansSoFar;
1847  pInBus->mNPlugChannels = std::abs(MaxNChannelsForBus(ERoute::kInput, bus));
1848 
1849  chansSoFar += pInBus->mNPlugChannels;
1850  }
1851 
1852  PtrListInitialize(&mOutBuses, maxNOBuses);
1853 
1854  chansSoFar = 0;
1855 
1856  for (auto bus = 0; bus < maxNOBuses; bus++)
1857  {
1858  BusChannels* pOutBus = mOutBuses.Get(bus);
1859  pOutBus->mNHostChannels = -1;
1860  pOutBus->mPlugChannelStartIdx = chansSoFar;
1861  pOutBus->mNPlugChannels = std::abs(MaxNChannelsForBus(ERoute::kOutput, bus));
1862 
1863  chansSoFar += pOutBus->mNPlugChannels;
1864  }
1865 
1866  AssessInputConnections();
1867 
1868  SetBlockSize(DEFAULT_BLOCK_SIZE);
1869  ResizeScratchBuffers();
1870 
1871  CreateTimer();
1872 }
1873 
1874 IPlugAU::~IPlugAU()
1875 {
1876  mRenderNotify.Empty(true);
1877  mInBuses.Empty(true);
1878  mOutBuses.Empty(true);
1879  mInBusConnections.Empty(true);
1880  mPropertyListeners.Empty(true);
1881 }
1882 
1883 void IPlugAU::SendAUEvent(AudioUnitEventType type, AudioComponentInstance ci, int idx)
1884 {
1885  AudioUnitEvent auEvent;
1886  memset(&auEvent, 0, sizeof(AudioUnitEvent));
1887  auEvent.mEventType = type;
1888  auEvent.mArgument.mParameter.mAudioUnit = ci;
1889  auEvent.mArgument.mParameter.mParameterID = idx;
1890  auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
1891  auEvent.mArgument.mParameter.mElement = 0;
1892  AUEventListenerNotify(0, 0, &auEvent);
1893 }
1894 
1896 {
1897  Trace(TRACELOC, "%d", idx);
1898  SendAUEvent(kAudioUnitEvent_BeginParameterChangeGesture, mCI, idx);
1899 }
1900 
1901 void IPlugAU::InformHostOfParamChange(int idx, double normalizedValue)
1902 {
1903  Trace(TRACELOC, "%d:%f", idx, normalizedValue);
1904  SendAUEvent(kAudioUnitEvent_ParameterValueChange, mCI, idx);
1905 }
1906 
1908 {
1909  Trace(TRACELOC, "%d", idx);
1910  SendAUEvent(kAudioUnitEvent_EndParameterChangeGesture, mCI, idx);
1911 }
1912 
1914 {
1915  //InformListeners(kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global);
1916  InformListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global);
1917 }
1918 
1920 {
1921  InformListeners(kAudioUnitProperty_ParameterList, kAudioUnitScope_Global);
1922  InformListeners(kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global);
1923 }
1924 
1925 void IPlugAU::PreProcess()
1926 {
1927  ITimeInfo timeInfo;
1928 
1929  if (mHostCallbacks.beatAndTempoProc)
1930  {
1931  double currentBeat = 0.0, tempo = 0.0;
1932  mHostCallbacks.beatAndTempoProc(mHostCallbacks.hostUserData, &currentBeat, &tempo);
1933 
1934  if (tempo > 0.0)
1935  timeInfo.mTempo = tempo;
1936 
1937  if (currentBeat >= 0.0)
1938  timeInfo.mPPQPos = currentBeat;
1939  }
1940 
1941  if (mHostCallbacks.transportStateProc)
1942  {
1943  double samplePos = 0.0, loopStartBeat=0.0, loopEndBeat=0.0;
1944  Boolean playing, changed, looping;
1945  mHostCallbacks.transportStateProc(mHostCallbacks.hostUserData, &playing, &changed, &samplePos, &looping, &loopStartBeat, &loopEndBeat);
1946 
1947  if (samplePos > 0.0)
1948  timeInfo.mSamplePos = samplePos;
1949 
1950  if (loopStartBeat > 0.0)
1951  timeInfo.mCycleStart = loopStartBeat;
1952 
1953  if (loopEndBeat > 0.0)
1954  timeInfo.mCycleEnd = loopEndBeat;
1955 
1956  timeInfo.mTransportIsRunning = playing;
1957  timeInfo.mTransportLoopEnabled = looping;
1958  }
1959 
1960  UInt32 sampleOffsetToNextBeat = 0, tsDenom = 0;
1961  float tsNum = 0.0f;
1962  double currentMeasureDownBeat = 0.0;
1963 
1964  if (mHostCallbacks.musicalTimeLocationProc)
1965  {
1966  mHostCallbacks.musicalTimeLocationProc(mHostCallbacks.hostUserData, &sampleOffsetToNextBeat, &tsNum, &tsDenom, &currentMeasureDownBeat);
1967 
1968  timeInfo.mNumerator = (int) tsNum;
1969  timeInfo.mDenominator = (int) tsDenom;
1970  if (currentMeasureDownBeat>0.0)
1971  timeInfo.mLastBar=currentMeasureDownBeat;
1972  }
1973 
1974  SetTimeInfo(timeInfo);
1975 }
1976 
1977 void IPlugAU::ResizeScratchBuffers()
1978 {
1979  TRACE
1980  int NInputs = MaxNChannels(ERoute::kInput) * GetBlockSize();
1981  int NOutputs = MaxNChannels(ERoute::kOutput) * GetBlockSize();
1982  mInScratchBuf.Resize(NInputs);
1983  mOutScratchBuf.Resize(NOutputs);
1984  memset(mInScratchBuf.Get(), 0, NInputs * sizeof(AudioSampleType));
1985  memset(mOutScratchBuf.Get(), 0, NOutputs * sizeof(AudioSampleType));
1986 }
1987 
1988 void IPlugAU::InformListeners(AudioUnitPropertyID propID, AudioUnitScope scope)
1989 {
1990  TRACE
1991  int i, n = mPropertyListeners.GetSize();
1992 
1993  for (i = 0; i < n; ++i)
1994  {
1995  PropertyListener* pListener = mPropertyListeners.Get(i);
1996 
1997  if (pListener->mPropID == propID)
1998  {
1999  pListener->mListenerProc(pListener->mProcArgs, mCI, propID, scope, 0);
2000  }
2001  }
2002 }
2003 
2004 void IPlugAU::SetLatency(int samples)
2005 {
2006  TRACE
2007  int i, n = mPropertyListeners.GetSize();
2008 
2009  for (i = 0; i < n; ++i)
2010  {
2011  PropertyListener* pListener = mPropertyListeners.Get(i);
2012  if (pListener->mPropID == kAudioUnitProperty_Latency)
2013  {
2014  pListener->mListenerProc(pListener->mProcArgs, mCI, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
2015  }
2016  }
2017 
2018  IPlugProcessor::SetLatency(samples);
2019 }
2020 
2022 {
2023  if(mMidiCallback.midiOutputCallback == nullptr)
2024  return false;
2025 
2026  MIDIPacketList packetList;
2027 
2028  packetList.packet[0].data[0] = msg.mStatus;
2029  packetList.packet[0].data[1] = msg.mData1;
2030  packetList.packet[0].data[2] = msg.mData2;
2031  packetList.packet[0].length = 3;
2032  packetList.packet[0].timeStamp = msg.mOffset;
2033  packetList.numPackets = 1;
2034 
2035  if(mMidiCallback.midiOutputCallback)
2036  {
2037  OSStatus status = mMidiCallback.midiOutputCallback(mMidiCallback.userData, &mLastRenderTimeStamp, 0, &packetList);
2038 
2039  if (status == noErr)
2040  return true;
2041  }
2042 
2043  return false;
2044 }
2045 
2046 bool IPlugAU::SendMidiMsgs(WDL_TypedBuf<IMidiMsg>& msgs)
2047 {
2048  bool result = false;
2049 
2050  if(mMidiCallback.midiOutputCallback == nullptr)
2051  return false;
2052 
2053  WDL_HeapBuf heapBuf;
2054  MIDIPacketList* pPktlist = (MIDIPacketList*) heapBuf.ResizeOK(msgs.GetSize() * 3);
2055  MIDIPacket* pPkt = MIDIPacketListInit(pPktlist);
2056  ByteCount listSize = heapBuf.GetSize();
2057 
2058  IMidiMsg* pMsg = msgs.Get();
2059  for (int i = 0; i < msgs.GetSize(); ++i, ++pMsg)
2060  {
2061  pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, pMsg->mOffset /* TODO: is this correct? */, 1, &pMsg->mStatus);
2062  pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, pMsg->mOffset /* TODO: is this correct? */, 1, &pMsg->mData1);
2063  pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, pMsg->mOffset /* TODO: is this correct? */, 1, &pMsg->mData2);
2064  }
2065 
2066  if(mMidiCallback.midiOutputCallback)
2067  {
2068  OSStatus status = mMidiCallback.midiOutputCallback(mMidiCallback.userData, &mLastRenderTimeStamp, 0, pPktlist);
2069 
2070  if (status == noErr)
2071  result = true;
2072  }
2073 
2074  return result;
2075 }
2076 
2077 bool IPlugAU::SendSysEx(const ISysEx& sysEx)
2078 {
2079  bool result = false;
2080 
2081  if(mMidiCallback.midiOutputCallback == nullptr)
2082  return false;
2083 
2084  assert(sysEx.mSize <= 65536); // maximum packet list size
2085 
2086  WDL_HeapBuf heapBuf;
2087  MIDIPacketList* pPktlist = (MIDIPacketList*) heapBuf.ResizeOK(sysEx.mSize);
2088  MIDIPacket* pPkt = MIDIPacketListInit(pPktlist);
2089 
2090  ByteCount listSize = heapBuf.GetSize();
2091  ByteCount bytesLeft = listSize;
2092 
2093  while (bytesLeft) {
2094  ByteCount packetSize = listSize < 256 ? listSize : 256;
2095  pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, sysEx.mOffset /* TODO: is this correct? */, packetSize, sysEx.mData);
2096  bytesLeft -= packetSize;
2097  }
2098 
2099  assert(pPkt != nullptr);
2100 
2101  if(mMidiCallback.midiOutputCallback)
2102  {
2103  OSStatus status = mMidiCallback.midiOutputCallback(mMidiCallback.userData, &mLastRenderTimeStamp, 0, pPktlist);
2104 
2105  if (status == noErr)
2106  result = true;
2107  }
2108 
2109  return result;
2110 }
2111 
2112 void IPlugAU::OutputSysexFromEditor()
2113 {
2114  //Output SYSEX from the editor, which has bypassed ProcessSysEx()
2115  if(mSysExDataFromEditor.ElementsAvailable())
2116  {
2117  while (mSysExDataFromEditor.Pop(mSysexBuf))
2118  {
2119  ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
2120  SendSysEx(smsg);
2121  }
2122  }
2123 }
2124 
2125 #pragma mark - IPlugAU Dispatch
2126 
2127 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
2128 
2129 static IPlugAU* GetPlug(void *x)
2130 {
2131  return (IPlugAU*) &((AudioComponentPlugInInstance *) x)->mInstanceStorage;
2132 }
2133 
2134 //static
2135 OSStatus IPlugAU::AUMethodInitialize(void* pSelf)
2136 {
2137  return DoInitialize(GetPlug(pSelf));
2138 }
2139 
2140 //static
2141 OSStatus IPlugAU::AUMethodUninitialize(void* pSelf)
2142 {
2143  return DoUninitialize(GetPlug(pSelf));
2144 }
2145 
2146 //static
2147 OSStatus IPlugAU::AUMethodGetPropertyInfo(void* pSelf, AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, UInt32* outDataSize, Boolean* outWritable)
2148 {
2149  return DoGetPropertyInfo(GetPlug(pSelf), prop, scope, elem, outDataSize, outWritable);
2150 }
2151 
2152 //static
2153 OSStatus IPlugAU::AUMethodGetProperty(void* pSelf, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize)
2154 {
2155  return DoGetProperty(GetPlug(pSelf), inID, inScope, inElement, outData, ioDataSize);
2156 }
2157 
2158 //static
2159 OSStatus IPlugAU::AUMethodSetProperty(void* pSelf, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32* inDataSize)
2160 {
2161  return DoSetProperty(GetPlug(pSelf), inID, inScope, inElement, inData, inDataSize);
2162 }
2163 
2164 //static
2165 OSStatus IPlugAU::AUMethodAddPropertyListener(void* pSelf, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2166 {
2167  return DoAddPropertyListener(GetPlug(pSelf), prop, proc, userData);
2168 }
2169 
2170 //static
2171 OSStatus IPlugAU::AUMethodRemovePropertyListener(void* pSelf, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc)
2172 {
2173  return DoRemovePropertyListener(GetPlug(pSelf), prop, proc);
2174 }
2175 
2176 //static
2177 OSStatus IPlugAU::AUMethodRemovePropertyListenerWithUserData(void* pSelf, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2178 {
2179  return DoRemovePropertyListenerWithUserData(GetPlug(pSelf), prop, proc, userData);
2180 }
2181 
2182 //static
2183 OSStatus IPlugAU::AUMethodAddRenderNotify(void* pSelf, AURenderCallback proc, void* userData)
2184 {
2185  return DoAddRenderNotify(GetPlug(pSelf), proc, userData);
2186 }
2187 
2188 //static
2189 OSStatus IPlugAU::AUMethodRemoveRenderNotify(void* pSelf, AURenderCallback proc, void* userData)
2190 {
2191  return DoRemoveRenderNotify(GetPlug(pSelf), proc, userData);
2192 }
2193 
2194 //static
2195 OSStatus IPlugAU::AUMethodGetParameter(void* pSelf, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue* value)
2196 {
2197  return DoGetParameter(GetPlug(pSelf), param, scope, elem, value);
2198 }
2199 
2200 //static
2201 OSStatus IPlugAU::AUMethodSetParameter(void* pSelf, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue value, UInt32 bufferOffset)
2202 {
2203  return DoSetParameter(GetPlug(pSelf), param, scope, elem, value, bufferOffset);
2204 }
2205 
2206 //static
2207 OSStatus IPlugAU::AUMethodScheduleParameters(void* pSelf, const AudioUnitParameterEvent *pEvent, UInt32 nEvents)
2208 {
2209  return DoScheduleParameters(GetPlug(pSelf), pEvent, nEvents);
2210 }
2211 
2212 //static
2213 OSStatus IPlugAU::AUMethodRender(void* pSelf, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
2214 {
2215  return DoRender(GetPlug(pSelf), ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
2216 }
2217 
2218 //static
2219 OSStatus IPlugAU::AUMethodReset(void* pSelf, AudioUnitScope scope, AudioUnitElement elem)
2220 {
2221  return DoReset(GetPlug(pSelf));
2222 }
2223 
2224 //static
2225 OSStatus IPlugAU::AUMethodMIDIEvent(void* pSelf, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
2226 {
2227  return DoMIDIEvent(GetPlug(pSelf), inStatus, inData1, inData2, inOffsetSampleFrame);
2228 }
2229 
2230 //static
2231 OSStatus IPlugAU::AUMethodSysEx(void* pSelf, const UInt8* inData, UInt32 inLength)
2232 {
2233  return DoSysEx(GetPlug(pSelf), inData, inLength);
2234 }
2235 #endif
2236 
2237 //static
2238 OSStatus IPlugAU::DoInitialize(IPlugAU* _this)
2239 {
2240  if (_this->GetHost() == kHostUninit)
2241  {
2242  CFBundleRef mainBundle = CFBundleGetMainBundle();
2243  CFStringRef id = nullptr;
2244  int version = 0;
2245 
2246  if (mainBundle)
2247  {
2248  id = CFBundleGetIdentifier(mainBundle);
2249  CStrLocal versionStr((CFStringRef) CFBundleGetValueForInfoDictionaryKey(mainBundle, kCFBundleVersionKey));
2250 
2251  char *pStr;
2252  long ver = versionStr.Get() ? strtol(versionStr.Get(), &pStr, 10) : 0;
2253  long verRevMaj = versionStr.Get() && *pStr ? strtol(pStr + 1, &pStr, 10) : 0;
2254  long verRevMin = versionStr.Get() && *pStr ? strtol(pStr + 1, &pStr, 10) : 0;
2255 
2256  version = (int) (((ver & 0xFFFF) << 16) | ((verRevMaj & 0xFF) << 8) | (verRevMin & 0xFF));
2257  }
2258 
2259  _this->SetHost(id ? CStrLocal(id).Get() : "", version);
2260  }
2261 
2262  if (!(_this->CheckLegalIO()))
2263  {
2264  return badComponentSelector;
2265  }
2266 
2267  _this->mActive = true;
2268  _this->OnParamReset(kReset);
2269  _this->OnActivate(true);
2270 
2271  return noErr;
2272 }
2273 
2274 //static
2275 OSStatus IPlugAU::DoUninitialize(IPlugAU* _this)
2276 {
2277  _this->mActive = false;
2278  _this->OnActivate(false);
2279  return noErr;
2280 }
2281 
2282 //static
2283 OSStatus IPlugAU::DoGetPropertyInfo(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, UInt32* outDataSize, Boolean* outWritable)
2284 {
2285  UInt32 dataSize = 0;
2286 
2287  if (!outDataSize)
2288  outDataSize = &dataSize;
2289 
2290  Boolean writeable;
2291 
2292  if (!outWritable)
2293  outWritable = &writeable;
2294 
2295  *outWritable = false;
2296 
2297  return _this->GetProperty(prop, scope, elem, outDataSize, outWritable, 0 /* indicates info */);
2298 }
2299 
2300 //static
2301 OSStatus IPlugAU::DoGetProperty(IPlugAU* _this, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32 *ioDataSize)
2302 {
2303  UInt32 dataSize = 0;
2304 
2305  if (!ioDataSize)
2306  ioDataSize = &dataSize;
2307 
2308  Boolean writeable = false;
2309 
2310  return _this->GetProperty(inID, inScope, inElement, ioDataSize, &writeable, outData);
2311 }
2312 
2313 //static
2314 OSStatus IPlugAU::DoSetProperty(IPlugAU* _this, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32* inDataSize)
2315 {
2316  return _this->SetProperty(inID, inScope, inElement, inDataSize, inData);
2317 }
2318 
2319 //static
2320 OSStatus IPlugAU::DoAddPropertyListener(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2321 {
2322  PropertyListener listener;
2323  listener.mPropID = prop;
2324  listener.mListenerProc = proc;
2325  listener.mProcArgs = userData;
2326  int i, n = _this->mPropertyListeners.GetSize();
2327  for (i = 0; i < n; ++i)
2328  {
2329  PropertyListener* pListener = _this->mPropertyListeners.Get(i);
2330  if (listener.mPropID == pListener->mPropID && listener.mListenerProc == pListener->mListenerProc)
2331  {
2332  return noErr;
2333  }
2334  }
2335  PtrListAddFromStack(&(_this->mPropertyListeners), &listener);
2336  return noErr;
2337 }
2338 
2339 //static
2340 OSStatus IPlugAU::DoRemovePropertyListener(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc)
2341 {
2342  PropertyListener listener;
2343  listener.mPropID = prop;
2344  listener.mListenerProc = proc;
2345  int i, n = _this->mPropertyListeners.GetSize();
2346  for (i = 0; i < n; ++i)
2347  {
2348  PropertyListener* pListener = _this->mPropertyListeners.Get(i);
2349  if (listener.mPropID == pListener->mPropID && listener.mListenerProc == pListener->mListenerProc)
2350  {
2351  _this->mPropertyListeners.Delete(i, true);
2352  break;
2353  }
2354  }
2355  return noErr;
2356 }
2357 
2358 //static
2359 OSStatus IPlugAU::DoRemovePropertyListenerWithUserData(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2360 {
2361  PropertyListener listener;
2362  listener.mPropID = prop;
2363  listener.mListenerProc = proc;
2364  listener.mProcArgs = userData;
2365  int i, n = _this->mPropertyListeners.GetSize();
2366  for (i = 0; i < n; ++i)
2367  {
2368  PropertyListener* pListener = _this->mPropertyListeners.Get(i);
2369  if (listener.mPropID == pListener->mPropID &&
2370  listener.mListenerProc == pListener->mListenerProc && listener.mProcArgs == pListener->mProcArgs)
2371  {
2372  _this->mPropertyListeners.Delete(i, true);
2373  break;
2374  }
2375  }
2376  return noErr;
2377 }
2378 
2379 //static
2380 OSStatus IPlugAU::DoAddRenderNotify(IPlugAU* _this, AURenderCallback proc, void* userData)
2381 {
2382  AURenderCallbackStruct acs;
2383  acs.inputProc = proc;
2384  acs.inputProcRefCon = userData;
2385 
2386  PtrListAddFromStack(&(_this->mRenderNotify), &acs);
2387  return noErr;
2388 }
2389 
2390 //static
2391 OSStatus IPlugAU::DoRemoveRenderNotify(IPlugAU* _this, AURenderCallback proc, void* userData)
2392 {
2393 
2394  AURenderCallbackStruct acs;
2395  acs.inputProc = proc;
2396  acs.inputProcRefCon = userData;
2397 
2398  int i, n = _this->mRenderNotify.GetSize();
2399  for (i = 0; i < n; ++i)
2400  {
2401  AURenderCallbackStruct* pACS = _this->mRenderNotify.Get(i);
2402  if (acs.inputProc == pACS->inputProc)
2403  {
2404  _this->mRenderNotify.Delete(i, true);
2405  break;
2406  }
2407  }
2408  return noErr;
2409 }
2410 
2411 //static
2412 OSStatus IPlugAU::DoGetParameter(IPlugAU* _this, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue *value)
2413 {
2414  //mutex locked below
2415  return _this->GetParamProc(_this, param, scope, elem, value);
2416 }
2417 
2418 //static
2419 OSStatus IPlugAU::DoSetParameter(IPlugAU* _this, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue value, UInt32 bufferOffset)
2420 {
2421  //mutex locked below
2422  return _this->SetParamProc(_this, param, scope, elem, value, bufferOffset);
2423 }
2424 
2425 //static
2426 OSStatus IPlugAU::DoScheduleParameters(IPlugAU* _this, const AudioUnitParameterEvent *pEvent, UInt32 nEvents)
2427 {
2428  //mutex locked below
2429  for (int i = 0; i < nEvents; ++i, ++pEvent)
2430  {
2431  if (pEvent->eventType == kParameterEvent_Immediate)
2432  {
2433  OSStatus r = SetParamProc(_this, pEvent->parameter, pEvent->scope, pEvent->element,
2434  pEvent->eventValues.immediate.value, pEvent->eventValues.immediate.bufferOffset);
2435  if (r != noErr)
2436  {
2437  return r;
2438  }
2439  }
2440  }
2441  return noErr;
2442 }
2443 
2444 //static
2445 OSStatus IPlugAU::DoRender(IPlugAU* _this, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
2446 {
2447  return RenderProc(_this, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
2448 }
2449 
2450 //static
2451 OSStatus IPlugAU::DoReset(IPlugAU* _this)
2452 {
2453  _this->OnReset();
2454  return noErr;
2455 }
2456 
2457 //static
2458 OSStatus IPlugAU::DoMIDIEvent(IPlugAU* _this, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
2459 {
2460  if(_this->DoesMIDIIn())
2461  {
2462  IMidiMsg msg;
2463  msg.mStatus = inStatus;
2464  msg.mData1 = inData1;
2465  msg.mData2 = inData2;
2466  msg.mOffset = inOffsetSampleFrame;
2467  _this->ProcessMidiMsg(msg);
2468  _this->mMidiMsgsFromProcessor.Push(msg);
2469  return noErr;
2470  }
2471  else
2472  return badComponentSelector;
2473 }
2474 
2475 //static
2476 OSStatus IPlugAU::DoSysEx(IPlugAU* _this, const UInt8* inData, UInt32 inLength)
2477 {
2478  if(_this->DoesMIDIIn())
2479  {
2480  ISysEx sysex;
2481  sysex.mData = inData;
2482  sysex.mSize = inLength;
2483  sysex.mOffset = 0;
2484  _this->ProcessSysEx(sysex);
2485  return noErr;
2486  }
2487  else
2488  return badComponentSelector;
2489 }
2490 
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:42
void InformHostOfPresetChange() override
Implemented by the API class, called by the UI (etc) when the plug-in initiates a program/preset chan...
Definition: IPlugAU.cpp:1913
int GetBlockSize() const
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:30
virtual void SendParameterValueFromAPI(int paramIdx, double value, bool normalized)
This is called from the plug-in API class in order to update UI controls linked to plug-in parameters...
int GetTotalNChannels(ERoute direction) const
Get the total number of channels across all direction buses for this IOConfig.
Definition: IPlugStructs.h:554
IPlug&#39;s parameter class.
virtual void SetLatency(int latency)
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
void PruneUninitializedPresets()
[AUV2 only] Removes any presets that weren&#39;t initialized
void EndInformHostOfParamChange(int idx) override
Implemented by the API class, called by the UI (or by a delegate) at the end of a parameter change ge...
Definition: IPlugAU.cpp:1907
An IOConfig is used to store bus info for each input/output configuration defined in the channel io s...
Definition: IPlugStructs.h:500
Manages a block of memory, for plug-in settings store/recall.
Definition: IPlugStructs.h:111
AudioUnit v2 API base class for an IPlug plug-in.
bool GetBypassed() const
int NBuses(ERoute direction) const
Definition: IPlugStructs.h:546
ERoute
Used to identify whether a bus/channel connection is an input or an output.
void MakeNoteOffMsg(int noteNumber, int offset, int channel=0)
Make a Note Off message.
Definition: IPlugMidi.h:158
int Size() const
Returns the current size of the chunk.
Definition: IPlugStructs.h:221
bool SendMidiMsg(const IMidiMsg &msg) override
Send a single MIDI message // TODO: info about what thread should this be called on or not called on!...
Definition: IPlugAU.cpp:2021
virtual void ProcessSysEx(ISysEx &msg)
Override this method to handle incoming MIDI System Exclusive (SysEx) messages.
int Resize(int newSize)
Resizes the chunk.
Definition: IPlugStructs.h:229
void InformHostOfParameterDetailsChange() override
Implemented by the API class, call this if you update parameter labels and hopefully the host should ...
Definition: IPlugAU.cpp:1919
EHost GetHost() const
bool DoesMIDIIn() const
void SetLatency(int samples) override
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
Definition: IPlugAU.cpp:2004
bool ContainsWildcard(ERoute direction) const
Definition: IPlugStructs.h:568
bool SendMidiMsgs(WDL_TypedBuf< IMidiMsg > &msgs) override
Send a collection of MIDI messages // TODO: info about what thread should this be called on or not ca...
Definition: IPlugAU.cpp:2046
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
The base class for IPlug Audio Processing.
bool SendSysEx(const ISysEx &msg) override
Send a single MIDI System Exclusive (SysEx) message // TODO: info about what thread should this be ca...
Definition: IPlugAU.cpp:2077
virtual void OnReset()
Override this method in your plug-in class to do something prior to playback etc. ...
bool IsMidiEffect() const
void BeginInformHostOfParamChange(int idx) override
Implemented by the API class, called by the UI (or by a delegate) at the beginning of a parameter cha...
Definition: IPlugAU.cpp:1895
AudioUnit v2 API base class for an IPlug plug-in.
Definition: IPlugAU.h:54
uint8_t * GetData()
Gets a ptr to the chunk data.
Definition: IPlugStructs.h:242
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:538
int GetPluginVersion(bool decimal) const
Get the plug-in version number.
void MakeNoteOnMsg(int noteNumber, int velocity, int offset, int channel=0)
Make a Note On message.
Definition: IPlugMidi.h:145
Encapsulates information about the host transport state.
Definition: IPlugStructs.h:581
void SetHost(const char *host, int version)
Called to set the name of the current host, if known (calls on to HostSpecificInit() and OnHostIdenti...
virtual void OnActivate(bool active)
Override OnActivate() which should be called by the API class when a plug-in is "switched on" by the ...
void InformHostOfParamChange(int idx, double normalizedValue) override
Implemented by the API class, called by the UI via SetParameterValue() with the value of a parameter ...
Definition: IPlugAU.cpp:1901