iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
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
20using namespace iplug;
21
22#pragma mark - CFString and CString Utilities
23
24static inline CFStringRef MakeCFString(const char* cStr)
25{
26 return CFStringCreateWithCString(0, cStr, kCFStringEncodingUTF8);
27}
28
29class IPlugAU::CFStrLocal
30{
31public:
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
47private:
48 CFStringRef mCFStr;
49};
50
51struct 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
65inline 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
73inline 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
80inline 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
88inline 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
100inline 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
114inline 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
130typedef AudioStreamBasicDescription STREAM_DESC;
131
132static /* 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
153template <class C>
154static 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
162template <class C>
163static 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
185OSStatus 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
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
395UInt32 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.
458OSStatus 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 *pDataSize = sizeof(Float64);
785
786 if (pData)
787 {
788 *((Float64*) pData) = (double) GetTailSize() / GetSampleRate();
789 }
790 return noErr;
791 }
792 case kAudioUnitProperty_BypassEffect: // 21,
793 {
794 ASSERT_SCOPE(kAudioUnitScope_Global);
795 *pWriteable = true;
796 *pDataSize = sizeof(UInt32);
797 if (pData)
798 {
799 *((UInt32*) pData) = (GetBypassed() ? 1 : 0);
800 }
801 return noErr;
802 }
803 case kAudioUnitProperty_LastRenderError: // 22,
804 {
805 ASSERT_SCOPE(kAudioUnitScope_Global);
806 *pDataSize = sizeof(OSStatus);
807 if (pData)
808 {
809 *((OSStatus*) pData) = noErr;
810 }
811 return noErr;
812 }
813 case kAudioUnitProperty_SetRenderCallback: // 23,
814 {
815 ASSERT_INPUT_OR_GLOBAL_SCOPE;
816 if (element >= mInBuses.GetSize())
817 {
818 return kAudioUnitErr_InvalidProperty;
819 }
820 *pDataSize = sizeof(AURenderCallbackStruct);
821 *pWriteable = true;
822 return noErr;
823 }
824 case kAudioUnitProperty_FactoryPresets: // 24, // listenable
825 {
826 *pDataSize = sizeof(CFArrayRef);
827 if (pData)
828 {
829 #ifdef AU_NO_PRESETS
830 int i, n = 0;
831 #else
832 int i, n = NPresets();
833 #endif
834
835 CFMutableArrayRef presetArray = CFArrayCreateMutable(kCFAllocatorDefault, n, &kCFAUPresetArrayCallBacks);
836
837 if (presetArray == NULL)
838 return coreFoundationUnknownErr;
839
840 for (i = 0; i < n; ++i)
841 {
842 CFStrLocal presetName(GetPresetName(i));
843 CFAUPresetRef newPreset = CFAUPresetCreate(kCFAllocatorDefault, i, presetName.Get()); // todo should i be 0 based?
844
845 if (newPreset != NULL)
846 {
847 CFArrayAppendValue(presetArray, newPreset);
848 CFAUPresetRelease(newPreset);
849 }
850 }
851
852 *((CFMutableArrayRef*) pData) = presetArray;
853 }
854 return noErr;
855 }
856 NO_OP(kAudioUnitProperty_ContextName); // 25,
857 NO_OP(kAudioUnitProperty_RenderQuality); // 26,
858 case kAudioUnitProperty_HostCallbacks: // 27,
859 {
860 ASSERT_SCOPE(kAudioUnitScope_Global);
861 *pDataSize = sizeof(HostCallbackInfo);
862 *pWriteable = true;
863 return noErr;
864 }
865 NO_OP(kAudioUnitProperty_InPlaceProcessing); // 29,
866 case kAudioUnitProperty_ElementName:
867 {
868 *pDataSize = sizeof(CFStringRef);
869 *pWriteable = false;
870 if (pData)
871 {
872 const int nIn = MaxNBuses(kInput);
873 const int nOut = MaxNBuses(kOutput);
874
875 switch(scope)
876 {
877 case kAudioUnitScope_Input:
878 {
879 if (element < nIn)
880 {
881 WDL_String busName;
882 GetBusName(ERoute::kInput, (int) element, nIn, busName);
883 *(CFStringRef*) pData = MakeCFString(busName.Get());
884 return noErr;
885 }
886 else
887 return kAudioUnitErr_InvalidElement;
888 }
889 case kAudioUnitScope_Output:
890 {
891 if (element < nOut)
892 {
893 WDL_String busName;
894 GetBusName(ERoute::kOutput, (int) element, nOut, busName);
895 *(CFStringRef*) pData = MakeCFString(busName.Get());
896 return noErr;
897 }
898 else
899 return kAudioUnitErr_InvalidElement;
900 }
901 default:
902 return kAudioUnitErr_InvalidScope;
903 }
904 }
905 return kAudioUnitErr_InvalidProperty;
906 }
907 case kAudioUnitProperty_CocoaUI: // 31,
908 {
909 if (HasUI()) // this won't work < 10.5 SDK but barely anyone will use that these days
910 {
911 *pDataSize = sizeof(AudioUnitCocoaViewInfo); // Just one view.
912 if (pData)
913 {
914 AudioUnitCocoaViewInfo* pViewInfo = (AudioUnitCocoaViewInfo*) pData;
915 CFStrLocal bundleID(mBundleID.Get());
916 CFBundleRef pBundle = CFBundleGetBundleWithIdentifier(bundleID.Get());
917 CFURLRef url = CFBundleCopyBundleURL(pBundle);
918 pViewInfo->mCocoaAUViewBundleLocation = url;
919 pViewInfo->mCocoaAUViewClass[0] = CFStringCreateWithCString(0, mCocoaViewFactoryClassName.Get(), kCFStringEncodingUTF8);
920 }
921 return noErr;
922 }
923 return kAudioUnitErr_InvalidProperty;
924 }
925#pragma mark - kAudioUnitProperty_SupportedChannelLayoutTags
926 case kAudioUnitProperty_SupportedChannelLayoutTags:
927 {
928 if (!pData) // GetPropertyInfo
929 {
930 UInt32 numLayouts = GetChannelLayoutTags(scope, element, NULL); // 0 = invalid scope, 1 = input
931
932 if (numLayouts)
933 {
934 *pDataSize = numLayouts * sizeof(AudioChannelLayoutTag);
935 *pWriteable = true;
936 return noErr;
937 }
938 else
939 {
940 *pDataSize = 0;
941 *pWriteable = false;
942 return noErr;
943 }
944 }
945 else // GetProperty
946 {
947 AudioChannelLayoutTag* ptr = pData ? static_cast<AudioChannelLayoutTag*>(pData) : NULL;
948
949 if (GetChannelLayoutTags(scope, element, ptr))
950 {
951 return noErr;
952 }
953 }
954
955 return kAudioUnitErr_InvalidProperty;
956 }
957 case kAudioUnitProperty_ParameterIDName: // 34,
958 {
959 *pDataSize = sizeof(AudioUnitParameterIDName);
960 if (pData && scope == kAudioUnitScope_Global)
961 {
962 AudioUnitParameterIDName* pIDName = (AudioUnitParameterIDName*) pData;
963 char cStr[MAX_PARAM_NAME_LEN];
964 ENTER_PARAMS_MUTEX
965 strcpy(cStr, GetParam(pIDName->inID)->GetName());
966 LEAVE_PARAMS_MUTEX
967 if (pIDName->inDesiredLength != kAudioUnitParameterName_Full)
968 {
969 int n = std::min<int>(MAX_PARAM_NAME_LEN - 1, pIDName->inDesiredLength);
970 cStr[n] = '\0';
971 }
972 pIDName->outName = CFStringCreateWithCString(0, cStr, kCFStringEncodingUTF8);
973 }
974 return noErr;
975 }
976 case kAudioUnitProperty_ParameterClumpName: // 35,
977 {
978 *pDataSize = sizeof (AudioUnitParameterNameInfo);
979 if (pData && scope == kAudioUnitScope_Global)
980 {
981 AudioUnitParameterNameInfo* parameterNameInfo = (AudioUnitParameterNameInfo*) pData;
982 int clumpId = parameterNameInfo->inID;
983
984 if (clumpId < 1)
985 return kAudioUnitErr_PropertyNotInUse;
986
987 parameterNameInfo->outName = CFStringCreateWithCString(0, GetParamGroupName(clumpId-1), kCFStringEncodingUTF8);
988 }
989 return noErr;
990 }
991 case kAudioUnitProperty_CurrentPreset: // 28,
992 case kAudioUnitProperty_PresentPreset: // 36, // listenable
993 {
994 *pDataSize = sizeof(AUPreset);
995 *pWriteable = true;
996 if (pData)
997 {
998 AUPreset* pAUPreset = (AUPreset*) pData;
999 pAUPreset->presetNumber = GetCurrentPresetIdx();
1000 const char* name = GetPresetName(pAUPreset->presetNumber);
1001 pAUPreset->presetName = CFStringCreateWithCString(0, name, kCFStringEncodingUTF8);
1002 }
1003 return noErr;
1004 }
1005 NO_OP(kAudioUnitProperty_OfflineRender); // 37,
1006 case kAudioUnitProperty_ParameterStringFromValue: // 33,
1007 {
1008 *pDataSize = sizeof(AudioUnitParameterStringFromValue);
1009 if (pData && scope == kAudioUnitScope_Global)
1010 {
1011 AudioUnitParameterStringFromValue* pSFV = (AudioUnitParameterStringFromValue*) pData;
1012 ENTER_PARAMS_MUTEX
1013 GetParam(pSFV->inParamID)->GetDisplay(*(pSFV->inValue), false, mParamDisplayStr);
1014 LEAVE_PARAMS_MUTEX
1015 pSFV->outString = MakeCFString((const char*) mParamDisplayStr.Get());
1016 }
1017 return noErr;
1018 }
1019 case kAudioUnitProperty_ParameterValueFromString: // 38,
1020 {
1021 *pDataSize = sizeof(AudioUnitParameterValueFromString);
1022 if (pData)
1023 {
1024 AudioUnitParameterValueFromString* pVFS = (AudioUnitParameterValueFromString*) pData;
1025 if (scope == kAudioUnitScope_Global)
1026 {
1027 CStrLocal cStr(pVFS->inString);
1028 ENTER_PARAMS_MUTEX
1029 const double v = GetParam(pVFS->inParamID)->StringToValue(cStr.Get());
1030 LEAVE_PARAMS_MUTEX
1031 pVFS->outValue = (AudioUnitParameterValue) v;
1032 }
1033 }
1034 return noErr;
1035 }
1036 NO_OP(kAudioUnitProperty_IconLocation); // 39,
1037 NO_OP(kAudioUnitProperty_PresentationLatency); // 40,
1038 NO_OP(kAudioUnitProperty_DependentParameters); // 45,
1039 case kMusicDeviceProperty_InstrumentCount:
1040 {
1041 ASSERT_SCOPE(kAudioUnitScope_Global);
1042 if (IsInstrument())
1043 {
1044 *pDataSize = sizeof(UInt32);
1045 if (pData)
1046 {
1047 *((UInt32*) pData) = 1; //(mIsBypassed ? 1 : 0);
1048 }
1049 return noErr;
1050 }
1051 else
1052 {
1053 return kAudioUnitErr_InvalidProperty;
1054 }
1055 }
1056
1057 NO_OP(kAudioUnitProperty_AUHostIdentifier); // 46,
1058 case kAudioUnitProperty_MIDIOutputCallbackInfo: // 47,
1059 {
1060 if(pData == 0)
1061 {
1062 *pWriteable = false;
1063 *pDataSize = sizeof(CFArrayRef);
1064 }
1065 else
1066 {
1067 CFStringRef outputName = CFSTR("midiOut");
1068 CFArrayRef array = CFArrayCreate(kCFAllocatorDefault, (const void**) &outputName, 1, nullptr);
1069 CFRelease(outputName);
1070 *((CFArrayRef*) pData) = array;
1071 }
1072 return noErr;
1073 }
1074 case kAudioUnitProperty_MIDIOutputCallback: // 48,
1075 {
1076 if(pData == 0)
1077 {
1078 *pWriteable = true;
1079 *pDataSize = sizeof(AUMIDIOutputCallbackStruct);
1080
1081 return noErr;
1082 }
1083 }
1084 NO_OP(kAudioUnitProperty_InputSamplesInOutput); // 49,
1085 NO_OP(kAudioUnitProperty_ClassInfoFromDocument); // 50
1086 case kAudioUnitProperty_SupportsMPE: // 58
1087 {
1088 if(pData == 0)
1089 {
1090 *pWriteable = false;
1091 *pDataSize = sizeof(UInt32);
1092 }
1093 else
1094 {
1095 *((UInt32*) pData) = (DoesMPE() ? 1 : 0);
1096 }
1097 return noErr;
1098 }
1099 default:
1100 {
1101 return kAudioUnitErr_InvalidProperty;
1102 }
1103 }
1104}
1105
1106OSStatus IPlugAU::SetProperty(AudioUnitPropertyID propID, AudioUnitScope scope, AudioUnitElement element,
1107 UInt32* pDataSize, const void* pData)
1108{
1109 Trace(TRACELOC, "(%d:%s):(%d:%s):%d", propID, AUPropertyStr(propID), scope, AUScopeStr(scope), element);
1110
1111 InformListeners(propID, scope);
1112
1113 switch (propID)
1114 {
1115 case kAudioUnitProperty_ClassInfo: // 0,
1116 {
1117 return SetState(*((CFPropertyListRef*) pData));
1118 }
1119 case kAudioUnitProperty_MakeConnection: // 1,
1120 {
1121 ASSERT_INPUT_OR_GLOBAL_SCOPE;
1122 AudioUnitConnection* pAUC = (AudioUnitConnection*) pData;
1123 if (pAUC->destInputNumber >= mInBusConnections.GetSize())
1124 {
1125 return kAudioUnitErr_InvalidProperty;
1126 }
1127 InputBusConnection* pInBusConn = mInBusConnections.Get(pAUC->destInputNumber);
1128 memset(pInBusConn, 0, sizeof(InputBusConnection));
1129 bool negotiatedOK = true;
1130 if (pAUC->sourceAudioUnit) // Opening connection.
1131 {
1132 AudioStreamBasicDescription srcASBD;
1133 UInt32 size = sizeof(AudioStreamBasicDescription);
1134 negotiatedOK = // Ask whoever is sending us audio what the format is.
1135 (AudioUnitGetProperty(pAUC->sourceAudioUnit, kAudioUnitProperty_StreamFormat,
1136 kAudioUnitScope_Output, pAUC->sourceOutputNumber, &srcASBD, &size) == noErr);
1137 negotiatedOK &= // Try to set our own format to match.
1138 (SetProperty(kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
1139 pAUC->destInputNumber, &size, &srcASBD) == noErr);
1140 if (negotiatedOK) // Connection terms successfully negotiated.
1141 {
1142 pInBusConn->mUpstreamUnit = pAUC->sourceAudioUnit;
1143 pInBusConn->mUpstreamBusIdx = pAUC->sourceOutputNumber;
1144 // Will the upstream unit give us a fast render proc for input?
1145 AudioUnitRenderProc srcRenderProc;
1146 size = sizeof(AudioUnitRenderProc);
1147 if (AudioUnitGetProperty(pAUC->sourceAudioUnit, kAudioUnitProperty_FastDispatch, kAudioUnitScope_Global, kAudioUnitRenderSelect,
1148 &srcRenderProc, &size) == noErr)
1149 {
1150 // Yes, we got a fast render proc, and we also need to store the pointer to the upstream audio unit object.
1151 pInBusConn->mUpstreamRenderProc = srcRenderProc;
1152 pInBusConn->mUpstreamObj = GetComponentInstanceStorage(pAUC->sourceAudioUnit);
1153 }
1154 // Else no fast render proc, so leave the input bus connection struct's upstream render proc and upstream object empty,
1155 // and we will need to make a component call through the component manager to get input data.
1156 }
1157 // Else this is a call to close the connection, which we effectively did by clearing the InputBusConnection struct,
1158 // which counts as a successful negotiation.
1159 }
1160 AssessInputConnections();
1161 return (negotiatedOK ? noErr : (int) kAudioUnitErr_InvalidProperty); // casting to int avoids gcc error
1162 }
1163 case kAudioUnitProperty_SampleRate: // 2,
1164 {
1165 SetSampleRate(*((Float64*) pData));
1166 OnReset();
1167 return noErr;
1168 }
1169 NO_OP(kAudioUnitProperty_ParameterList); // 3,
1170 NO_OP(kAudioUnitProperty_ParameterInfo); // 4,
1171 NO_OP(kAudioUnitProperty_FastDispatch); // 5,
1172 NO_OP(kAudioUnitProperty_CPULoad); // 6,
1173 case kAudioUnitProperty_StreamFormat: // 8,
1174 {
1175 AudioStreamBasicDescription* pASBD = (AudioStreamBasicDescription*) pData;
1176 int nHostChannels = pASBD->mChannelsPerFrame;
1177 BusChannels* pBus = GetBus(scope, element);
1178 if (!pBus)
1179 {
1180 return kAudioUnitErr_InvalidProperty;
1181 }
1182 pBus->mNHostChannels = 0;
1183 // The connection is OK if the plugin expects the same number of channels as the host is attempting to connect,
1184 // or if the plugin supports mono channels (meaning it's flexible about how many inputs to expect)
1185 // and the plugin supports at least as many channels as the host is attempting to connect.
1186 bool connectionOK = (nHostChannels > 0);
1187 connectionOK &= CheckLegalIO(scope, element, nHostChannels);
1188 connectionOK &= (pASBD->mFormatID == kAudioFormatLinearPCM && pASBD->mFormatFlags & kAudioFormatFlagsCanonical);
1189
1190 Trace(TRACELOC, "%d:%d:%s:%s:%s",
1191 nHostChannels, pBus->mNPlugChannels,
1192 (pASBD->mFormatID == kAudioFormatLinearPCM ? "linearPCM" : "notLinearPCM"),
1193 (pASBD->mFormatFlags & kAudioFormatFlagsCanonical ? "canonicalFormat" : "notCanonicalFormat"),
1194 (connectionOK ? "connectionOK" : "connectionNotOK"));
1195
1196 // bool interleaved = !(pASBD->mFormatFlags & kAudioFormatFlagIsNonInterleaved);
1197 if (connectionOK)
1198 {
1199 pBus->mNHostChannels = nHostChannels;
1200 if (pASBD->mSampleRate > 0.0)
1201 {
1202 SetSampleRate(pASBD->mSampleRate);
1203 }
1204 }
1205 else
1206 {
1207 pBus->mNHostChannels = pBus->mNPlugChannels;
1208 }
1209 AssessInputConnections();
1210 return (connectionOK ? noErr : (int) kAudioUnitErr_InvalidProperty); // casting to int avoids gcc error
1211 }
1212 NO_OP(kAudioUnitProperty_ElementCount); // 11,
1213 NO_OP(kAudioUnitProperty_Latency); // 12,
1214 NO_OP(kAudioUnitProperty_SupportedNumChannels); // 13,
1215 case kAudioUnitProperty_MaximumFramesPerSlice: // 14,
1216 {
1217 SetBlockSize(*((UInt32*) pData));
1218 ResizeScratchBuffers();
1219 OnReset();
1220 return noErr;
1221 }
1222 NO_OP(kAudioUnitProperty_SetExternalBuffer); // 15,
1223 NO_OP(kAudioUnitProperty_ParameterValueStrings); // 16,
1224 NO_OP(kAudioUnitProperty_GetUIComponentList); // 18,
1225 NO_OP(kAudioUnitProperty_AudioChannelLayout); // 19, //TODO?: Set kAudioUnitProperty_AudioChannelLayout
1226 NO_OP(kAudioUnitProperty_TailTime); // 20,
1227 case kAudioUnitProperty_BypassEffect: // 21,
1228 {
1229 const bool bypassed = *((UInt32*) pData) != 0;
1230 SetBypassed(bypassed);
1231
1232 // TODO: should the following be called here?
1233 OnActivate(!bypassed);
1234 OnReset();
1235 return noErr;
1236 }
1237 NO_OP(kAudioUnitProperty_LastRenderError); // 22,
1238 case kAudioUnitProperty_SetRenderCallback: // 23,
1239 {
1240 ASSERT_SCOPE(kAudioUnitScope_Input); // if global scope, set all
1241 if (element >= mInBusConnections.GetSize())
1242 {
1243 return kAudioUnitErr_InvalidProperty;
1244 }
1245 InputBusConnection* pInBusConn = mInBusConnections.Get(element);
1246 memset(pInBusConn, 0, sizeof(InputBusConnection));
1247 AURenderCallbackStruct* pCS = (AURenderCallbackStruct*) pData;
1248 if (pCS->inputProc != 0)
1249 {
1250 pInBusConn->mUpstreamRenderCallback = *pCS;
1251 }
1252 AssessInputConnections();
1253 return noErr;
1254 }
1255 NO_OP(kAudioUnitProperty_FactoryPresets); // 24,
1256 //NO_OP(kAudioUnitProperty_ContextName); // 25,
1257 case kAudioUnitProperty_ContextName:
1258 {
1259 CFStringRef inStr = *(CFStringRef*) pData;
1260 CFIndex bufferSize = CFStringGetLength(inStr) + 1; // The +1 is for having space for the string to be NUL terminated
1261 char buffer[bufferSize];
1262 if (CFStringGetCString(inStr, buffer, bufferSize, kCFStringEncodingUTF8))
1263 {
1264 mTrackName.Set(buffer);
1265 }
1266 return noErr;
1267 }
1268 NO_OP(kAudioUnitProperty_RenderQuality); // 26,
1269 case kAudioUnitProperty_HostCallbacks: // 27,
1270 {
1271 ASSERT_SCOPE(kAudioUnitScope_Global);
1272 memcpy(&mHostCallbacks, pData, sizeof(HostCallbackInfo));
1273 return noErr;
1274 }
1275 NO_OP(kAudioUnitProperty_InPlaceProcessing); // 29,
1276 NO_OP(kAudioUnitProperty_ElementName); // 30,
1277 NO_OP(kAudioUnitProperty_CocoaUI); // 31,
1278 NO_OP(kAudioUnitProperty_SupportedChannelLayoutTags); // 32,
1279 NO_OP(kAudioUnitProperty_ParameterIDName); // 34,
1280 NO_OP(kAudioUnitProperty_ParameterClumpName); // 35,
1281 case kAudioUnitProperty_CurrentPreset: // 28,
1282 case kAudioUnitProperty_PresentPreset: // 36,
1283 {
1284 int presetIdx = ((AUPreset*) pData)->presetNumber;
1285 RestorePreset(presetIdx);
1286 return noErr;
1287 }
1288 case kAudioUnitProperty_OfflineRender: // 37,
1289 {
1290 const bool renderingOffline = (*((UInt32*) pData) != 0);
1291 SetRenderingOffline(renderingOffline);
1292 return noErr;
1293 }
1294 NO_OP(kAudioUnitProperty_ParameterStringFromValue); // 33,
1295 NO_OP(kAudioUnitProperty_ParameterValueFromString); // 38,
1296 NO_OP(kAudioUnitProperty_IconLocation); // 39,
1297 NO_OP(kAudioUnitProperty_PresentationLatency); // 40,
1298 NO_OP(kAudioUnitProperty_DependentParameters); // 45,
1299 case kAudioUnitProperty_AUHostIdentifier: // 46,
1300 {
1301 if (GetHost() == kHostUninit)
1302 {
1303 AUHostIdentifier* pHostID = (AUHostIdentifier*) pData;
1304 CStrLocal hostStr(pHostID->hostName);
1305 int version = (pHostID->hostVersion.majorRev << 16)
1306 + ((pHostID->hostVersion.minorAndBugRev & 0xF0) << 4)
1307 + ((pHostID->hostVersion.minorAndBugRev & 0x0F));
1308 SetHost(hostStr.Get(), version);
1309 }
1310 return noErr;
1311 }
1312 NO_OP(kAudioUnitProperty_MIDIOutputCallbackInfo); // 47,
1313 case kAudioUnitProperty_MIDIOutputCallback: // 48,
1314 {
1315 mMidiCallback = *((AUMIDIOutputCallbackStruct*) pData);
1316 return noErr;
1317 }
1318 NO_OP(kAudioUnitProperty_InputSamplesInOutput); // 49,
1319 NO_OP(kAudioUnitProperty_ClassInfoFromDocument) // 50
1320 default:
1321 {
1322 return kAudioUnitErr_InvalidProperty;
1323 }
1324 }
1325}
1326
1327//static
1328const char* IPlugAU::AUInputTypeStr(int type)
1329{
1330 switch ((IPlugAU::EAUInputType) type)
1331 {
1332 case IPlugAU::eDirectFastProc: return "DirectFastProc";
1333 case IPlugAU::eDirectNoFastProc: return "DirectNoFastProc";
1334 case IPlugAU::eRenderCallback: return "RenderCallback";
1335 case IPlugAU::eNotConnected:
1336 default: return "NotConnected";
1337 }
1338}
1339
1340int IPlugAU::NHostChannelsConnected(WDL_PtrList<BusChannels>* pBuses, int excludeIdx)
1341{
1342 bool init = false;
1343 int nCh = 0, n = pBuses->GetSize();
1344
1345 for (int i = 0; i < n; ++i)
1346 {
1347 if (i != excludeIdx)
1348 {
1349 int nHostChannels = pBuses->Get(i)->mNHostChannels;
1350 if (nHostChannels >= 0)
1351 {
1352 nCh += nHostChannels;
1353 init = true;
1354 }
1355 }
1356 }
1357
1358 if (init)
1359 {
1360 return nCh;
1361 }
1362
1363 return -1;
1364}
1365
1366bool IPlugAU::CheckLegalIO(AudioUnitScope scope, int busIdx, int nChannels)
1367{
1368 if (scope == kAudioUnitScope_Input)
1369 {
1370 int nIn = std::max(NHostChannelsConnected(&mInBuses, busIdx), 0);
1371 int nOut = (mActive ? NHostChannelsConnected(&mOutBuses) : -1);
1372 return LegalIO(nIn + nChannels, nOut);
1373 }
1374 else
1375 {
1376 int nIn = (mActive ? NHostChannelsConnected(&mInBuses) : -1);
1377 int nOut = std::max(NHostChannelsConnected(&mOutBuses, busIdx), 0);
1378 return LegalIO(nIn, nOut + nChannels);
1379 }
1380}
1381
1382bool IPlugAU::CheckLegalIO()
1383{
1384 int nIn = NHostChannelsConnected(&mInBuses);
1385 int nOut = NHostChannelsConnected(&mOutBuses);
1386 return ((!nIn && !nOut) || LegalIO(nIn, nOut));
1387}
1388
1389void IPlugAU::AssessInputConnections()
1390{
1391 TRACE
1392 SetChannelConnections(ERoute::kInput, 0, MaxNChannels(ERoute::kInput), false);
1393
1394 int nIn = mInBuses.GetSize();
1395 for (int i = 0; i < nIn; ++i)
1396 {
1397 BusChannels* pInBus = mInBuses.Get(i);
1398 InputBusConnection* pInBusConn = mInBusConnections.Get(i);
1399
1400 // AU supports 3 ways to get input from the host (or whoever is upstream).
1401 if (pInBusConn->mUpstreamRenderProc && pInBusConn->mUpstreamObj)
1402 {
1403 // 1: direct input connection with fast render proc (and buffers) supplied by the upstream unit.
1404 pInBusConn->mInputType = eDirectFastProc;
1405 }
1406 else if (pInBusConn->mUpstreamUnit)
1407 {
1408 // 2: direct input connection with no render proc, buffers supplied by the upstream unit.
1409 pInBusConn->mInputType = eDirectNoFastProc;
1410 }
1411 else if (pInBusConn->mUpstreamRenderCallback.inputProc)
1412 {
1413 // 3: no direct connection, render callback, buffers supplied by us.
1414 pInBusConn->mInputType = eRenderCallback;
1415 }
1416 else
1417 {
1418 pInBusConn->mInputType = eNotConnected;
1419 }
1420 pInBus->mConnected = (pInBusConn->mInputType != eNotConnected);
1421
1422 int startChannelIdx = pInBus->mPlugChannelStartIdx;
1423 if (pInBus->mConnected)
1424 {
1425 // There's an input connection, so we need to tell the plug to expect however many channels
1426 // are in the negotiated host stream format.
1427 if (pInBus->mNHostChannels < 0)
1428 {
1429 // The host set up a connection without specifying how many channels in the stream.
1430 // Assume the host will send all the channels the plugin asks for, and hope for the best.
1431 Trace(TRACELOC, "AssumeChannels:%d", pInBus->mNPlugChannels);
1432 pInBus->mNHostChannels = pInBus->mNPlugChannels;
1433 }
1434 int nConnected = pInBus->mNHostChannels;
1435 int nUnconnected = std::max(pInBus->mNPlugChannels - nConnected, 0);
1436 SetChannelConnections(ERoute::kInput, startChannelIdx, nConnected, true);
1437 SetChannelConnections(ERoute::kInput, startChannelIdx + nConnected, nUnconnected, false);
1438 }
1439
1440 Trace(TRACELOC, "%d:%s:%d:%d:%d", i, AUInputTypeStr(pInBusConn->mInputType), startChannelIdx, pInBus->mNPlugChannels, pInBus->mNHostChannels);
1441 }
1442}
1443
1444OSStatus IPlugAU::GetState(CFPropertyListRef* ppPropList)
1445{
1446 int plugType = GetAUPluginType();
1447 int plugSubType = GetUniqueID();
1448 int plugManID = GetMfrID();
1449
1450 CFMutableDictionaryRef pDict = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1451 int version = GetPluginVersion(false);
1452 PutNumberInDict(pDict, kAUPresetVersionKey, &version, kCFNumberSInt32Type);
1453 PutNumberInDict(pDict, kAUPresetTypeKey, &(plugType), kCFNumberSInt32Type);
1454 PutNumberInDict(pDict, kAUPresetSubtypeKey, &(plugSubType), kCFNumberSInt32Type);
1455 PutNumberInDict(pDict, kAUPresetManufacturerKey, &(plugManID), kCFNumberSInt32Type);
1456 PutStrInDict(pDict, kAUPresetNameKey, GetPresetName(GetCurrentPresetIdx()));
1457
1458 IByteChunk chunk;
1459 //InitChunkWithIPlugVer(&IPlugChunk); // TODO: IPlugVer should be in chunk!
1460
1461 if (SerializeState(chunk))
1462 {
1463 PutDataInDict(pDict, kAUPresetDataKey, &chunk);
1464 }
1465
1466 *ppPropList = pDict;
1467 TRACE
1468 return noErr;
1469}
1470
1471OSStatus IPlugAU::SetState(CFPropertyListRef pPropList)
1472{
1473 CFDictionaryRef pDict = (CFDictionaryRef) pPropList;
1474 int version, type, subtype, mfr;
1475 char presetName[64];
1476 if (!GetNumberFromDict(pDict, kAUPresetVersionKey, &version, kCFNumberSInt32Type) ||
1477 !GetNumberFromDict(pDict, kAUPresetTypeKey, &type, kCFNumberSInt32Type) ||
1478 !GetNumberFromDict(pDict, kAUPresetSubtypeKey, &subtype, kCFNumberSInt32Type) ||
1479 !GetNumberFromDict(pDict, kAUPresetManufacturerKey, &mfr, kCFNumberSInt32Type) ||
1480 !GetStrFromDict(pDict, kAUPresetNameKey, presetName) ||
1481 //version != GetPluginVersion(false) ||
1482 type != GetAUPluginType() ||
1483 subtype != GetUniqueID() ||
1484 mfr != GetMfrID())
1485 {
1486 return kAudioUnitErr_InvalidPropertyValue;
1487 }
1488
1489 RestorePreset(presetName);
1490
1491 IByteChunk chunk;
1492
1493 if (!GetDataFromDict(pDict, kAUPresetDataKey, &chunk))
1494 {
1495 return kAudioUnitErr_InvalidPropertyValue;
1496 }
1497
1498 // TODO: IPlugVer should be in chunk!
1499 // int pos;
1500 // IByteChunk::GetIPlugVerFromChunk(chunk, pos)
1501
1502 if (!UnserializeState(chunk, 0))
1503 {
1504 return kAudioUnitErr_InvalidPropertyValue;
1505 }
1506
1507 OnRestoreState();
1508 return noErr;
1509}
1510
1511// pData == 0 means return property info only.
1512OSStatus IPlugAU::GetProc(AudioUnitElement element, UInt32* pDataSize, void* pData)
1513{
1514 Trace(TRACELOC, "%s:(%d:%s)", (pData ? "" : "Info"), element, AUSelectStr(element));
1515
1516 switch (element)
1517 {
1518 //TODO: WHAT ABOUT THESE!!!
1519// case kAudioUnitGetParameterSelect:
1520// {
1521// *pDataSize = sizeof(AudioUnitGetParameterProc);
1522// if (pData)
1523// {
1524// *((AudioUnitGetParameterProc*) pData) = (AudioUnitGetParameterProc) IPlugAU::GetParamProc;
1525// }
1526// return noErr;
1527// }
1528// case kAudioUnitSetParameterSelect:
1529// {
1530// *pDataSize = sizeof(AudioUnitSetParameterProc);
1531// if (pData)
1532// {
1533// *((AudioUnitSetParameterProc*) pData) = (AudioUnitSetParameterProc) IPlugAU::SetParamProc;
1534// }
1535// return noErr;
1536// }
1537// case kAudioUnitRenderSelect:
1538// {
1539// *pDataSize = sizeof(AudioUnitRenderProc);
1540// if (pData)
1541// {
1542// *((AudioUnitRenderProc*) pData) = (AudioUnitRenderProc) IPlugAU::RenderProc;
1543// }
1544// return noErr;
1545// }
1546 default:
1547 return kAudioUnitErr_InvalidElement;
1548 }
1549}
1550
1551// static
1552OSStatus IPlugAU::GetParamProc(void* pPlug, AudioUnitParameterID paramID, AudioUnitScope scope, AudioUnitElement element, AudioUnitParameterValue* pValue)
1553{
1554 Trace(TRACELOC, "%d:(%d:%s):%d", paramID, scope, AUScopeStr(scope), element);
1555
1556 ASSERT_SCOPE(kAudioUnitScope_Global);
1557 IPlugAU* _this = (IPlugAU*) pPlug;
1558 assert(_this != NULL);
1559 ENTER_PARAMS_MUTEX_STATIC
1560 *pValue = _this->GetParam(paramID)->Value();
1561 LEAVE_PARAMS_MUTEX_STATIC
1562 return noErr;
1563}
1564
1565// static
1566OSStatus IPlugAU::SetParamProc(void* pPlug, AudioUnitParameterID paramID, AudioUnitScope scope, AudioUnitElement element, AudioUnitParameterValue value, UInt32 offsetFrames)
1567{
1568 Trace(TRACELOC, "%d:(%d:%s):%d", paramID, scope, AUScopeStr(scope), element);
1569
1570 // In the SDK, offset frames is only looked at in group scope.
1571 ASSERT_SCOPE(kAudioUnitScope_Global);
1572 IPlugAU* _this = (IPlugAU*) pPlug;
1573 ENTER_PARAMS_MUTEX_STATIC
1574 _this->GetParam(paramID)->Set(value);
1575 _this->SendParameterValueFromAPI(paramID, value, false);
1576 _this->OnParamChange(paramID, kHost);
1577 LEAVE_PARAMS_MUTEX_STATIC
1578 return noErr;
1579}
1580
1581static inline OSStatus RenderCallback(AURenderCallbackStruct* pCB, AudioUnitRenderActionFlags* pFlags, const AudioTimeStamp* pTimestamp, UInt32 inputBusIdx, UInt32 nFrames, AudioBufferList* pOutBufList)
1582{
1583 TRACE
1584 return pCB->inputProc(pCB->inputProcRefCon, pFlags, pTimestamp, inputBusIdx, nFrames, pOutBufList);
1585}
1586
1587// static
1588OSStatus IPlugAU::RenderProc(void* pPlug, AudioUnitRenderActionFlags* pFlags, const AudioTimeStamp* pTimestamp,
1589 UInt32 outputBusIdx, UInt32 nFrames, AudioBufferList* pOutBufList)
1590{
1591 Trace(TRACELOC, "%d:%d:%d", outputBusIdx, pOutBufList->mNumberBuffers, nFrames);
1592
1593 IPlugAU* _this = (IPlugAU*) pPlug;
1594
1595 _this->mLastRenderTimeStamp = *pTimestamp;
1596
1597 if (!(pTimestamp->mFlags & kAudioTimeStampSampleTimeValid) /*|| outputBusIdx >= _this->mOutBuses.GetSize()*/ || nFrames > _this->GetBlockSize())
1598 {
1599 return kAudioUnitErr_InvalidPropertyValue;
1600 }
1601
1602 int nRenderNotify = _this->mRenderNotify.GetSize();
1603
1604 if (nRenderNotify)
1605 {
1606 for (int i = 0; i < nRenderNotify; ++i)
1607 {
1608 AURenderCallbackStruct* pRN = _this->mRenderNotify.Get(i);
1609 AudioUnitRenderActionFlags flags = kAudioUnitRenderAction_PreRender;
1610 RenderCallback(pRN, &flags, pTimestamp, outputBusIdx, nFrames, pOutBufList);
1611 }
1612 }
1613
1614 int lastConnectedOutputBus = -1;
1615
1616 if(!_this->IsMidiEffect())
1617 {
1618 double renderSampleTime = pTimestamp->mSampleTime;
1619
1620 // Pull input buffers.
1621 if (renderSampleTime != _this->mLastRenderSampleTime)
1622 {
1623 BufferList bufList;
1624 AudioBufferList* pInBufList = (AudioBufferList*) &bufList;
1625
1626 int nIn = _this->mInBuses.GetSize();
1627
1628 for (int i = 0; i < nIn; ++i)
1629 {
1630 BusChannels* pInBus = _this->mInBuses.Get(i);
1631 InputBusConnection* pInBusConn = _this->mInBusConnections.Get(i);
1632
1633 if (pInBus->mConnected)
1634 {
1635 pInBufList->mNumberBuffers = pInBus->mNHostChannels;
1636
1637 for (int b = 0; b < pInBufList->mNumberBuffers; ++b)
1638 {
1639 AudioBuffer* pBuffer = &(pInBufList->mBuffers[b]);
1640 pBuffer->mNumberChannels = 1;
1641 pBuffer->mDataByteSize = nFrames * sizeof(AudioSampleType);
1642 pBuffer->mData = 0;
1643 }
1644
1645 AudioUnitRenderActionFlags flags = 0;
1646 OSStatus r = noErr;
1647
1648 switch (pInBusConn->mInputType)
1649 {
1650 case eDirectFastProc:
1651 {
1652 r = pInBusConn->mUpstreamRenderProc(pInBusConn->mUpstreamObj, &flags, pTimestamp, pInBusConn->mUpstreamBusIdx, nFrames, pInBufList);
1653 break;
1654 }
1655 case eDirectNoFastProc:
1656 {
1657 r = AudioUnitRender(pInBusConn->mUpstreamUnit, &flags, pTimestamp, pInBusConn->mUpstreamBusIdx, nFrames, pInBufList);
1658 break;
1659 }
1660 case eRenderCallback:
1661 {
1662 AudioSampleType* pScratchInput = _this->mInScratchBuf.Get() + pInBus->mPlugChannelStartIdx * nFrames;
1663
1664 for (int b = 0; b < pInBufList->mNumberBuffers; ++b, pScratchInput += nFrames)
1665 {
1666 pInBufList->mBuffers[b].mData = pScratchInput;
1667 }
1668
1669 r = RenderCallback(&(pInBusConn->mUpstreamRenderCallback), &flags, pTimestamp, i, nFrames, pInBufList);
1670 break;
1671 }
1672 default:
1673 break;
1674 }
1675
1676 if (r != noErr)
1677 {
1678 return r; // Something went wrong upstream.
1679 }
1680
1681 for (int c = 0, chIdx = pInBus->mPlugChannelStartIdx; c < pInBus->mNHostChannels; ++c, ++chIdx)
1682 {
1683 _this->AttachBuffers(ERoute::kInput, chIdx, 1, (AudioSampleType**) &(pInBufList->mBuffers[c].mData), nFrames);
1684 }
1685 }
1686 }
1687 _this->mLastRenderSampleTime = renderSampleTime;
1688 }
1689
1690 BusChannels* pOutBus = _this->mOutBuses.Get(outputBusIdx);
1691
1692 // 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
1693 if (!(pOutBus->mConnected) || pOutBus->mNHostChannels != pOutBufList->mNumberBuffers)
1694 {
1695 const int startChannelIdx = pOutBus->mPlugChannelStartIdx;
1696 const int nConnected = std::max(pOutBus->mNHostChannels, static_cast<int>(pOutBufList->mNumberBuffers));
1697 const int nUnconnected = std::max(pOutBus->mNPlugChannels - nConnected, 0);
1698
1699 assert(nConnected > -1);
1700 _this->SetChannelConnections(ERoute::kOutput, startChannelIdx, nConnected, true);
1701 _this->SetChannelConnections(ERoute::kOutput, startChannelIdx + nConnected, nUnconnected, false); // This will disconnect the right hand channel on a single stereo bus
1702 pOutBus->mConnected = true;
1703 }
1704
1705 for (int c = 0, chIdx = pOutBus->mPlugChannelStartIdx; c < pOutBufList->mNumberBuffers; ++c, ++chIdx)
1706 {
1707 if (!(pOutBufList->mBuffers[c].mData)) // Downstream unit didn't give us buffers.
1708 pOutBufList->mBuffers[c].mData = _this->mOutScratchBuf.Get() + chIdx * nFrames;
1709
1710 _this->AttachBuffers(ERoute::kOutput, chIdx, 1, (AudioSampleType**) &(pOutBufList->mBuffers[c].mData), nFrames);
1711 }
1712
1713 for(int i = 0; i < _this->mOutBuses.GetSize(); i++)
1714 {
1715 if(!_this->mOutBuses.Get(i)->mConnected)
1716 {
1717 break;
1718 }
1719 else
1720 {
1721 lastConnectedOutputBus++;
1722 }
1723 }
1724 }
1725
1726 if (_this->IsMidiEffect() || outputBusIdx == lastConnectedOutputBus)
1727 {
1728 int busIdx1based = outputBusIdx+1;
1729
1730 if (busIdx1based < _this->mOutBuses.GetSize() /*&& (_this->GetHost() != kHostAbletonLive)*/)
1731 {
1732 int totalNumChans = _this->mOutBuses.GetSize() * 2; // stereo only for the time being
1733 int nConnected = busIdx1based * 2;
1734 _this->SetChannelConnections(ERoute::kOutput, nConnected, totalNumChans - nConnected, false); // this will disconnect the channels that are on the unconnected buses
1735 }
1736
1737 if (_this->GetBypassed())
1738 {
1739 _this->PassThroughBuffers((AudioSampleType) 0, nFrames);
1740 }
1741 else
1742 {
1743 if(_this->mMidiMsgsFromEditor.ElementsAvailable())
1744 {
1745 IMidiMsg msg;
1746
1747 while (_this->mMidiMsgsFromEditor.Pop(msg))
1748 {
1749 _this->ProcessMidiMsg(msg);
1750 }
1751 }
1752
1753 _this->PreProcess();
1754 ENTER_PARAMS_MUTEX_STATIC
1755 _this->ProcessBuffers((AudioSampleType) 0, nFrames);
1756 LEAVE_PARAMS_MUTEX_STATIC
1757 }
1758 }
1759
1760 if (nRenderNotify)
1761 {
1762 for (int i = 0; i < nRenderNotify; ++i)
1763 {
1764 AURenderCallbackStruct* pRN = _this->mRenderNotify.Get(i);
1765 AudioUnitRenderActionFlags flags = kAudioUnitRenderAction_PostRender;
1766 RenderCallback(pRN, &flags, pTimestamp, outputBusIdx, nFrames, pOutBufList);
1767 }
1768 }
1769
1770 _this->OutputSysexFromEditor();
1771
1772 return noErr;
1773}
1774
1775IPlugAU::BusChannels* IPlugAU::GetBus(AudioUnitScope scope, AudioUnitElement busIdx)
1776{
1777 if (scope == kAudioUnitScope_Input && busIdx < mInBuses.GetSize())
1778 {
1779 return mInBuses.Get(busIdx);
1780 }
1781 if (scope == kAudioUnitScope_Output && busIdx < mOutBuses.GetSize())
1782 {
1783 return mOutBuses.Get(busIdx);
1784 }
1785 // Global bus is an alias for output bus zero.
1786 if (scope == kAudioUnitScope_Global && mOutBuses.GetSize())
1787 {
1788 return mOutBuses.Get(busIdx);
1789 }
1790 return 0;
1791}
1792
1793void IPlugAU::ClearConnections()
1794{
1795 int nInBuses = mInBuses.GetSize();
1796 for (int i = 0; i < nInBuses; ++i)
1797 {
1798 BusChannels* pInBus = mInBuses.Get(i);
1799 pInBus->mConnected = false;
1800 pInBus->mNHostChannels = -1;
1801 InputBusConnection* pInBusConn = mInBusConnections.Get(i);
1802 memset(pInBusConn, 0, sizeof(InputBusConnection));
1803 }
1804 int nOutBuses = mOutBuses.GetSize();
1805 for (int i = 0; i < nOutBuses; ++i)
1806 {
1807 BusChannels* pOutBus = mOutBuses.Get(i);
1808 pOutBus->mConnected = false;
1809 pOutBus->mNHostChannels = -1;
1810 }
1811}
1812
1813#pragma mark - IPlugAU Constructor
1814
1815IPlugAU::IPlugAU(const InstanceInfo& info, const Config& config)
1816: IPlugAPIBase(config, kAPIAU)
1817, IPlugProcessor(config, kAPIAU)
1818{
1819 Trace(TRACELOC, "%s", config.pluginName);
1820
1821 memset(&mHostCallbacks, 0, sizeof(HostCallbackInfo));
1822 memset(&mMidiCallback, 0, sizeof(AUMIDIOutputCallbackStruct));
1823
1824 mCocoaViewFactoryClassName.Set(info.mCocoaViewFactoryClassName.Get());
1825
1826 int maxNIBuses = MaxNBuses(ERoute::kInput);
1827
1828 // 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
1829 if(maxNIBuses == 1 && MaxNChannelsForBus(kInput, 0) == 0)
1830 {
1831 maxNIBuses = 0;
1832 }
1833
1834 const int maxNOBuses = MaxNBuses(ERoute::kOutput);
1835
1836 if(maxNIBuses)
1837 {
1838 PtrListInitialize(&mInBusConnections, maxNIBuses);
1839 PtrListInitialize(&mInBuses, maxNIBuses);
1840 }
1841
1842 int chansSoFar = 0;
1843
1844 for (auto bus = 0; bus < maxNIBuses; bus++)
1845 {
1846 BusChannels* pInBus = mInBuses.Get(bus);
1847 pInBus->mNHostChannels = -1;
1848 pInBus->mPlugChannelStartIdx = chansSoFar;
1849 pInBus->mNPlugChannels = std::abs(MaxNChannelsForBus(ERoute::kInput, bus));
1850
1851 chansSoFar += pInBus->mNPlugChannels;
1852 }
1853
1854 PtrListInitialize(&mOutBuses, maxNOBuses);
1855
1856 chansSoFar = 0;
1857
1858 for (auto bus = 0; bus < maxNOBuses; bus++)
1859 {
1860 BusChannels* pOutBus = mOutBuses.Get(bus);
1861 pOutBus->mNHostChannels = -1;
1862 pOutBus->mPlugChannelStartIdx = chansSoFar;
1863 pOutBus->mNPlugChannels = std::abs(MaxNChannelsForBus(ERoute::kOutput, bus));
1864
1865 chansSoFar += pOutBus->mNPlugChannels;
1866 }
1867
1868 AssessInputConnections();
1869
1870 SetBlockSize(DEFAULT_BLOCK_SIZE);
1871 ResizeScratchBuffers();
1872 InitLatencyDelay();
1873
1874 CreateTimer();
1875}
1876
1877IPlugAU::~IPlugAU()
1878{
1879 mRenderNotify.Empty(true);
1880 mInBuses.Empty(true);
1881 mOutBuses.Empty(true);
1882 mInBusConnections.Empty(true);
1883 mPropertyListeners.Empty(true);
1884}
1885
1886void IPlugAU::SendAUEvent(AudioUnitEventType type, AudioComponentInstance ci, int idx)
1887{
1888 AudioUnitEvent auEvent;
1889 memset(&auEvent, 0, sizeof(AudioUnitEvent));
1890 auEvent.mEventType = type;
1891 auEvent.mArgument.mParameter.mAudioUnit = ci;
1892 auEvent.mArgument.mParameter.mParameterID = idx;
1893 auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
1894 auEvent.mArgument.mParameter.mElement = 0;
1895 AUEventListenerNotify(0, 0, &auEvent);
1896}
1897
1899{
1900 Trace(TRACELOC, "%d", idx);
1901 SendAUEvent(kAudioUnitEvent_BeginParameterChangeGesture, mCI, idx);
1902}
1903
1904void IPlugAU::InformHostOfParamChange(int idx, double normalizedValue)
1905{
1906 Trace(TRACELOC, "%d:%f", idx, normalizedValue);
1907 SendAUEvent(kAudioUnitEvent_ParameterValueChange, mCI, idx);
1908}
1909
1911{
1912 Trace(TRACELOC, "%d", idx);
1913 SendAUEvent(kAudioUnitEvent_EndParameterChangeGesture, mCI, idx);
1914}
1915
1917{
1918 //InformListeners(kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global);
1919 InformListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global);
1920}
1921
1923{
1924 InformListeners(kAudioUnitProperty_ParameterList, kAudioUnitScope_Global);
1925 InformListeners(kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global);
1926}
1927
1928void IPlugAU::PreProcess()
1929{
1930 ITimeInfo timeInfo;
1931
1932 if (mHostCallbacks.beatAndTempoProc)
1933 {
1934 double currentBeat = 0.0, tempo = 0.0;
1935 mHostCallbacks.beatAndTempoProc(mHostCallbacks.hostUserData, &currentBeat, &tempo);
1936
1937 if (tempo > 0.0)
1938 timeInfo.mTempo = tempo;
1939
1940 if (currentBeat >= 0.0)
1941 timeInfo.mPPQPos = currentBeat;
1942 }
1943
1944 if (mHostCallbacks.transportStateProc)
1945 {
1946 double samplePos = 0.0, loopStartBeat=0.0, loopEndBeat=0.0;
1947 Boolean playing, changed, looping;
1948 mHostCallbacks.transportStateProc(mHostCallbacks.hostUserData, &playing, &changed, &samplePos, &looping, &loopStartBeat, &loopEndBeat);
1949
1950 if (samplePos > 0.0)
1951 timeInfo.mSamplePos = samplePos;
1952
1953 if (loopStartBeat > 0.0)
1954 timeInfo.mCycleStart = loopStartBeat;
1955
1956 if (loopEndBeat > 0.0)
1957 timeInfo.mCycleEnd = loopEndBeat;
1958
1959 timeInfo.mTransportIsRunning = playing;
1960 timeInfo.mTransportLoopEnabled = looping;
1961 }
1962
1963 UInt32 sampleOffsetToNextBeat = 0, tsDenom = 0;
1964 float tsNum = 0.0f;
1965 double currentMeasureDownBeat = 0.0;
1966
1967 if (mHostCallbacks.musicalTimeLocationProc)
1968 {
1969 mHostCallbacks.musicalTimeLocationProc(mHostCallbacks.hostUserData, &sampleOffsetToNextBeat, &tsNum, &tsDenom, &currentMeasureDownBeat);
1970
1971 timeInfo.mNumerator = (int) tsNum;
1972 timeInfo.mDenominator = (int) tsDenom;
1973 if (currentMeasureDownBeat>0.0)
1974 timeInfo.mLastBar=currentMeasureDownBeat;
1975 }
1976
1977 SetTimeInfo(timeInfo);
1978}
1979
1980void IPlugAU::ResizeScratchBuffers()
1981{
1982 TRACE
1983 int NInputs = MaxNChannels(ERoute::kInput) * GetBlockSize();
1984 int NOutputs = MaxNChannels(ERoute::kOutput) * GetBlockSize();
1985 mInScratchBuf.Resize(NInputs);
1986 mOutScratchBuf.Resize(NOutputs);
1987 memset(mInScratchBuf.Get(), 0, NInputs * sizeof(AudioSampleType));
1988 memset(mOutScratchBuf.Get(), 0, NOutputs * sizeof(AudioSampleType));
1989}
1990
1991void IPlugAU::InformListeners(AudioUnitPropertyID propID, AudioUnitScope scope)
1992{
1993 TRACE
1994 int i, n = mPropertyListeners.GetSize();
1995
1996 for (i = 0; i < n; ++i)
1997 {
1998 PropertyListener* pListener = mPropertyListeners.Get(i);
1999
2000 if (pListener->mPropID == propID)
2001 {
2002 pListener->mListenerProc(pListener->mProcArgs, mCI, propID, scope, 0);
2003 }
2004 }
2005}
2006
2007void IPlugAU::SetLatency(int samples)
2008{
2009 TRACE
2010 InformListeners(kAudioUnitProperty_Latency, kAudioUnitScope_Global);
2012}
2013
2015{
2016 if(mMidiCallback.midiOutputCallback == nullptr)
2017 return false;
2018
2019 MIDIPacketList packetList;
2020
2021 packetList.packet[0].data[0] = msg.mStatus;
2022 packetList.packet[0].data[1] = msg.mData1;
2023 packetList.packet[0].data[2] = msg.mData2;
2024 packetList.packet[0].length = 3;
2025 packetList.packet[0].timeStamp = msg.mOffset;
2026 packetList.numPackets = 1;
2027
2028 if(mMidiCallback.midiOutputCallback)
2029 {
2030 OSStatus status = mMidiCallback.midiOutputCallback(mMidiCallback.userData, &mLastRenderTimeStamp, 0, &packetList);
2031
2032 if (status == noErr)
2033 return true;
2034 }
2035
2036 return false;
2037}
2038
2039bool IPlugAU::SendMidiMsgs(WDL_TypedBuf<IMidiMsg>& msgs)
2040{
2041 bool result = false;
2042
2043 if(mMidiCallback.midiOutputCallback == nullptr)
2044 return false;
2045
2046 WDL_HeapBuf heapBuf;
2047 MIDIPacketList* pPktlist = (MIDIPacketList*) heapBuf.ResizeOK(msgs.GetSize() * 3);
2048 MIDIPacket* pPkt = MIDIPacketListInit(pPktlist);
2049 ByteCount listSize = heapBuf.GetSize();
2050
2051 IMidiMsg* pMsg = msgs.Get();
2052 for (int i = 0; i < msgs.GetSize(); ++i, ++pMsg)
2053 {
2054 pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, pMsg->mOffset /* TODO: is this correct? */, 1, &pMsg->mStatus);
2055 pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, pMsg->mOffset /* TODO: is this correct? */, 1, &pMsg->mData1);
2056 pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, pMsg->mOffset /* TODO: is this correct? */, 1, &pMsg->mData2);
2057 }
2058
2059 if(mMidiCallback.midiOutputCallback)
2060 {
2061 OSStatus status = mMidiCallback.midiOutputCallback(mMidiCallback.userData, &mLastRenderTimeStamp, 0, pPktlist);
2062
2063 if (status == noErr)
2064 result = true;
2065 }
2066
2067 return result;
2068}
2069
2070bool IPlugAU::SendSysEx(const ISysEx& sysEx)
2071{
2072 bool result = false;
2073
2074 if(mMidiCallback.midiOutputCallback == nullptr)
2075 return false;
2076
2077 assert(sysEx.mSize <= 65536); // maximum packet list size
2078
2079 WDL_HeapBuf heapBuf;
2080 MIDIPacketList* pPktlist = (MIDIPacketList*) heapBuf.ResizeOK(sysEx.mSize);
2081 MIDIPacket* pPkt = MIDIPacketListInit(pPktlist);
2082
2083 ByteCount listSize = heapBuf.GetSize();
2084 ByteCount bytesLeft = listSize;
2085
2086 while (bytesLeft) {
2087 ByteCount packetSize = listSize < 256 ? listSize : 256;
2088 pPkt = MIDIPacketListAdd(pPktlist, listSize, pPkt, sysEx.mOffset /* TODO: is this correct? */, packetSize, sysEx.mData);
2089 bytesLeft -= packetSize;
2090 }
2091
2092 assert(pPkt != nullptr);
2093
2094 if(mMidiCallback.midiOutputCallback)
2095 {
2096 OSStatus status = mMidiCallback.midiOutputCallback(mMidiCallback.userData, &mLastRenderTimeStamp, 0, pPktlist);
2097
2098 if (status == noErr)
2099 result = true;
2100 }
2101
2102 return result;
2103}
2104
2105void IPlugAU::OutputSysexFromEditor()
2106{
2107 //Output SYSEX from the editor, which has bypassed ProcessSysEx()
2108 if(mSysExDataFromEditor.ElementsAvailable())
2109 {
2110 while (mSysExDataFromEditor.Pop(mSysexBuf))
2111 {
2112 ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
2113 SendSysEx(smsg);
2114 }
2115 }
2116}
2117
2118#pragma mark - IPlugAU Dispatch
2119
2120#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
2121
2122static IPlugAU* GetPlug(void *x)
2123{
2124 return (IPlugAU*) &((AudioComponentPlugInInstance*) x)->mInstanceStorage;
2125}
2126
2127//static
2128OSStatus IPlugAU::AUMethodInitialize(void* pSelf)
2129{
2130 return DoInitialize(GetPlug(pSelf));
2131}
2132
2133//static
2134OSStatus IPlugAU::AUMethodUninitialize(void* pSelf)
2135{
2136 return DoUninitialize(GetPlug(pSelf));
2137}
2138
2139//static
2140OSStatus IPlugAU::AUMethodGetPropertyInfo(void* pSelf, AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, UInt32* outDataSize, Boolean* outWritable)
2141{
2142 return DoGetPropertyInfo(GetPlug(pSelf), prop, scope, elem, outDataSize, outWritable);
2143}
2144
2145//static
2146OSStatus IPlugAU::AUMethodGetProperty(void* pSelf, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize)
2147{
2148 return DoGetProperty(GetPlug(pSelf), inID, inScope, inElement, outData, ioDataSize);
2149}
2150
2151//static
2152OSStatus IPlugAU::AUMethodSetProperty(void* pSelf, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32* inDataSize)
2153{
2154 return DoSetProperty(GetPlug(pSelf), inID, inScope, inElement, inData, inDataSize);
2155}
2156
2157//static
2158OSStatus IPlugAU::AUMethodAddPropertyListener(void* pSelf, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2159{
2160 return DoAddPropertyListener(GetPlug(pSelf), prop, proc, userData);
2161}
2162
2163//static
2164OSStatus IPlugAU::AUMethodRemovePropertyListener(void* pSelf, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc)
2165{
2166 return DoRemovePropertyListener(GetPlug(pSelf), prop, proc);
2167}
2168
2169//static
2170OSStatus IPlugAU::AUMethodRemovePropertyListenerWithUserData(void* pSelf, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2171{
2172 return DoRemovePropertyListenerWithUserData(GetPlug(pSelf), prop, proc, userData);
2173}
2174
2175//static
2176OSStatus IPlugAU::AUMethodAddRenderNotify(void* pSelf, AURenderCallback proc, void* userData)
2177{
2178 return DoAddRenderNotify(GetPlug(pSelf), proc, userData);
2179}
2180
2181//static
2182OSStatus IPlugAU::AUMethodRemoveRenderNotify(void* pSelf, AURenderCallback proc, void* userData)
2183{
2184 return DoRemoveRenderNotify(GetPlug(pSelf), proc, userData);
2185}
2186
2187//static
2188OSStatus IPlugAU::AUMethodGetParameter(void* pSelf, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue* value)
2189{
2190 return DoGetParameter(GetPlug(pSelf), param, scope, elem, value);
2191}
2192
2193//static
2194OSStatus IPlugAU::AUMethodSetParameter(void* pSelf, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue value, UInt32 bufferOffset)
2195{
2196 return DoSetParameter(GetPlug(pSelf), param, scope, elem, value, bufferOffset);
2197}
2198
2199//static
2200OSStatus IPlugAU::AUMethodScheduleParameters(void* pSelf, const AudioUnitParameterEvent *pEvent, UInt32 nEvents)
2201{
2202 return DoScheduleParameters(GetPlug(pSelf), pEvent, nEvents);
2203}
2204
2205//static
2206OSStatus IPlugAU::AUMethodRender(void* pSelf, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
2207{
2208 return DoRender(GetPlug(pSelf), ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
2209}
2210
2211//static
2212OSStatus IPlugAU::AUMethodReset(void* pSelf, AudioUnitScope scope, AudioUnitElement elem)
2213{
2214 return DoReset(GetPlug(pSelf));
2215}
2216
2217//static
2218OSStatus IPlugAU::AUMethodMIDIEvent(void* pSelf, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
2219{
2220 return DoMIDIEvent(GetPlug(pSelf), inStatus, inData1, inData2, inOffsetSampleFrame);
2221}
2222
2223//static
2224OSStatus IPlugAU::AUMethodSysEx(void* pSelf, const UInt8* inData, UInt32 inLength)
2225{
2226 return DoSysEx(GetPlug(pSelf), inData, inLength);
2227}
2228#endif
2229
2230//static
2231OSStatus IPlugAU::DoInitialize(IPlugAU* _this)
2232{
2233 if (_this->GetHost() == kHostUninit)
2234 {
2235 CFBundleRef mainBundle = CFBundleGetMainBundle();
2236 CFStringRef id = nullptr;
2237 int version = 0;
2238
2239 if (mainBundle)
2240 {
2241 id = CFBundleGetIdentifier(mainBundle);
2242 CStrLocal versionStr((CFStringRef) CFBundleGetValueForInfoDictionaryKey(mainBundle, kCFBundleVersionKey));
2243
2244 char *pStr;
2245 long ver = versionStr.Get() ? strtol(versionStr.Get(), &pStr, 10) : 0;
2246 long verRevMaj = versionStr.Get() && *pStr ? strtol(pStr + 1, &pStr, 10) : 0;
2247 long verRevMin = versionStr.Get() && *pStr ? strtol(pStr + 1, &pStr, 10) : 0;
2248
2249 version = (int) (((ver & 0xFFFF) << 16) | ((verRevMaj & 0xFF) << 8) | (verRevMin & 0xFF));
2250 }
2251
2252 _this->SetHost(id ? CStrLocal(id).Get() : "", version);
2253 }
2254
2255 if (!(_this->CheckLegalIO()))
2256 {
2257 return badComponentSelector;
2258 }
2259
2260 _this->mActive = true;
2261 _this->OnParamReset(kReset);
2262 _this->OnActivate(true);
2263
2264 return noErr;
2265}
2266
2267//static
2268OSStatus IPlugAU::DoUninitialize(IPlugAU* _this)
2269{
2270 _this->mActive = false;
2271 _this->OnActivate(false);
2272 return noErr;
2273}
2274
2275//static
2276OSStatus IPlugAU::DoGetPropertyInfo(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, UInt32* outDataSize, Boolean* outWritable)
2277{
2278 UInt32 dataSize = 0;
2279
2280 if (!outDataSize)
2281 outDataSize = &dataSize;
2282
2283 Boolean writeable;
2284
2285 if (!outWritable)
2286 outWritable = &writeable;
2287
2288 *outWritable = false;
2289
2290 return _this->GetProperty(prop, scope, elem, outDataSize, outWritable, 0 /* indicates info */);
2291}
2292
2293//static
2294OSStatus IPlugAU::DoGetProperty(IPlugAU* _this, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32 *ioDataSize)
2295{
2296 UInt32 dataSize = 0;
2297
2298 if (!ioDataSize)
2299 ioDataSize = &dataSize;
2300
2301 Boolean writeable = false;
2302
2303 return _this->GetProperty(inID, inScope, inElement, ioDataSize, &writeable, outData);
2304}
2305
2306//static
2307OSStatus IPlugAU::DoSetProperty(IPlugAU* _this, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32* inDataSize)
2308{
2309 return _this->SetProperty(inID, inScope, inElement, inDataSize, inData);
2310}
2311
2312//static
2313OSStatus IPlugAU::DoAddPropertyListener(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2314{
2315 PropertyListener listener;
2316 listener.mPropID = prop;
2317 listener.mListenerProc = proc;
2318 listener.mProcArgs = userData;
2319 int i, n = _this->mPropertyListeners.GetSize();
2320 for (i = 0; i < n; ++i)
2321 {
2322 PropertyListener* pListener = _this->mPropertyListeners.Get(i);
2323 if (listener.mPropID == pListener->mPropID && listener.mListenerProc == pListener->mListenerProc)
2324 {
2325 return noErr;
2326 }
2327 }
2328 PtrListAddFromStack(&(_this->mPropertyListeners), &listener);
2329 return noErr;
2330}
2331
2332//static
2333OSStatus IPlugAU::DoRemovePropertyListener(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc)
2334{
2335 PropertyListener listener;
2336 listener.mPropID = prop;
2337 listener.mListenerProc = proc;
2338 int i, n = _this->mPropertyListeners.GetSize();
2339 for (i = 0; i < n; ++i)
2340 {
2341 PropertyListener* pListener = _this->mPropertyListeners.Get(i);
2342 if (listener.mPropID == pListener->mPropID && listener.mListenerProc == pListener->mListenerProc)
2343 {
2344 _this->mPropertyListeners.Delete(i, true);
2345 break;
2346 }
2347 }
2348 return noErr;
2349}
2350
2351//static
2352OSStatus IPlugAU::DoRemovePropertyListenerWithUserData(IPlugAU* _this, AudioUnitPropertyID prop, AudioUnitPropertyListenerProc proc, void* userData)
2353{
2354 PropertyListener listener;
2355 listener.mPropID = prop;
2356 listener.mListenerProc = proc;
2357 listener.mProcArgs = userData;
2358 int i, n = _this->mPropertyListeners.GetSize();
2359 for (i = 0; i < n; ++i)
2360 {
2361 PropertyListener* pListener = _this->mPropertyListeners.Get(i);
2362 if (listener.mPropID == pListener->mPropID &&
2363 listener.mListenerProc == pListener->mListenerProc && listener.mProcArgs == pListener->mProcArgs)
2364 {
2365 _this->mPropertyListeners.Delete(i, true);
2366 break;
2367 }
2368 }
2369 return noErr;
2370}
2371
2372//static
2373OSStatus IPlugAU::DoAddRenderNotify(IPlugAU* _this, AURenderCallback proc, void* userData)
2374{
2375 AURenderCallbackStruct acs;
2376 acs.inputProc = proc;
2377 acs.inputProcRefCon = userData;
2378
2379 PtrListAddFromStack(&(_this->mRenderNotify), &acs);
2380 return noErr;
2381}
2382
2383//static
2384OSStatus IPlugAU::DoRemoveRenderNotify(IPlugAU* _this, AURenderCallback proc, void* userData)
2385{
2386
2387 AURenderCallbackStruct acs;
2388 acs.inputProc = proc;
2389 acs.inputProcRefCon = userData;
2390
2391 int i, n = _this->mRenderNotify.GetSize();
2392 for (i = 0; i < n; ++i)
2393 {
2394 AURenderCallbackStruct* pACS = _this->mRenderNotify.Get(i);
2395 if (acs.inputProc == pACS->inputProc)
2396 {
2397 _this->mRenderNotify.Delete(i, true);
2398 break;
2399 }
2400 }
2401 return noErr;
2402}
2403
2404//static
2405OSStatus IPlugAU::DoGetParameter(IPlugAU* _this, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue *value)
2406{
2407 //mutex locked below
2408 return _this->GetParamProc(_this, param, scope, elem, value);
2409}
2410
2411//static
2412OSStatus IPlugAU::DoSetParameter(IPlugAU* _this, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement elem, AudioUnitParameterValue value, UInt32 bufferOffset)
2413{
2414 //mutex locked below
2415 return _this->SetParamProc(_this, param, scope, elem, value, bufferOffset);
2416}
2417
2418//static
2419OSStatus IPlugAU::DoScheduleParameters(IPlugAU* _this, const AudioUnitParameterEvent *pEvent, UInt32 nEvents)
2420{
2421 //mutex locked below
2422 for (int i = 0; i < nEvents; ++i, ++pEvent)
2423 {
2424 if (pEvent->eventType == kParameterEvent_Immediate)
2425 {
2426 OSStatus r = SetParamProc(_this, pEvent->parameter, pEvent->scope, pEvent->element,
2427 pEvent->eventValues.immediate.value, pEvent->eventValues.immediate.bufferOffset);
2428 if (r != noErr)
2429 {
2430 return r;
2431 }
2432 }
2433 }
2434 return noErr;
2435}
2436
2437//static
2438OSStatus IPlugAU::DoRender(IPlugAU* _this, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
2439{
2440 return RenderProc(_this, ioActionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData);
2441}
2442
2443//static
2444OSStatus IPlugAU::DoReset(IPlugAU* _this)
2445{
2446 _this->OnReset();
2447 return noErr;
2448}
2449
2450//static
2451OSStatus IPlugAU::DoMIDIEvent(IPlugAU* _this, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
2452{
2453 if(_this->DoesMIDIIn())
2454 {
2455 IMidiMsg msg;
2456 msg.mStatus = inStatus;
2457 msg.mData1 = inData1;
2458 msg.mData2 = inData2;
2459 msg.mOffset = inOffsetSampleFrame;
2460 _this->ProcessMidiMsg(msg);
2461 _this->mMidiMsgsFromProcessor.Push(msg);
2462 return noErr;
2463 }
2464 else
2465 return badComponentSelector;
2466}
2467
2468//static
2469OSStatus IPlugAU::DoSysEx(IPlugAU* _this, const UInt8* inData, UInt32 inLength)
2470{
2471 if(_this->DoesMIDIIn())
2472 {
2473 ISysEx sysex;
2474 sysex.mData = inData;
2475 sysex.mSize = inLength;
2476 sysex.mOffset = 0;
2477 _this->ProcessSysEx(sysex);
2478 return noErr;
2479 }
2480 else
2481 return badComponentSelector;
2482}
2483
Manages a block of memory, for plug-in settings store/recall.
Definition: IPlugStructs.h:112
uint8_t * GetData()
Gets a ptr to the chunk data.
Definition: IPlugStructs.h:242
int Size() const
Returns the current size of the chunk.
Definition: IPlugStructs.h:221
int Resize(int newSize)
Resizes the chunk.
Definition: IPlugStructs.h:229
IPlug's parameter class.
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:43
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...
void SetHost(const char *host, int version)
Called to set the name of the current host, if known (calls on to HostSpecificInit() and OnHostIdenti...
AudioUnit v2 API base class for an IPlug plug-in.
Definition: IPlugAU.h:56
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:1898
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:1916
void InformHostOfParameterDetailsChange() override
Implemented by the API class, call this if you update parameter labels and hopefully the host should ...
Definition: IPlugAU.cpp:1922
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:1904
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:2014
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:1910
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:2039
void SetLatency(int samples) override
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
Definition: IPlugAU.cpp:2007
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:2070
The base class for IPlug Audio Processing.
const IOConfig * GetIOConfig(int idx) const
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
bool LegalIO(int NInputChans, int NOutputChans) const
Check if a certain configuration of input channels and output channels is allowed based on the channe...
virtual void GetBusName(ERoute direction, int busIdx, int nBuses, WDL_String &str) const
Get the name for a particular bus.
virtual void SetLatency(int latency)
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
bool IsMidiEffect() const
bool IsInstrument() const
int MaxNBuses(ERoute direction, int *pConfigIdxWithTheMostBuses=nullptr) const
Used to determine the maximum number of input or output buses based on what was specified in the chan...
bool DoesMPE() const
int GetAUPluginType() const
bool DoesMIDIIn() const
double GetSampleRate() const
int GetBlockSize() const
bool GetBypassed() const
int NIOConfigs() const
int MaxNChannels(ERoute direction) const
virtual void ProcessSysEx(ISysEx &msg)
Override this method to handle incoming MIDI System Exclusive (SysEx) messages.
virtual void OnActivate(bool active)
Override OnActivate() which should be called by the API class when a plug-in is "switched on" by the ...
int GetLatency() const
virtual void OnReset()
Override this method in your plug-in class to do something prior to playback etc.
virtual int UnserializeState(const IByteChunk &chunk, int startPos)
Override this method to unserialize custom state data, if your plugin does state chunks.
int GetMfrID() const
int GetPluginVersion(bool decimal) const
Get the plug-in version number.
bool HasUI() const
int GetUniqueID() const
virtual bool SerializeState(IByteChunk &chunk) const
Override this method to serialize custom state data, if your plugin does state chunks.
int AddParamGroup(const char *name)
Called to add a parameter group name, when a unique group name is discovered.
int GetCurrentPresetIdx() const
Get the index of the current, active preset.
bool RestorePreset(int idx)
Restore a preset by index.
const char * GetParamGroupName(int idx) const
Get the parameter group name as a particular index.
const char * GetPresetName(int idx) const
Get the name a preset.
EHost GetHost() const
void PruneUninitializedPresets()
[AUV2 only] Removes any presets that weren't initialized
int NPresets() const
Gets the number of factory presets.
int NParamGroups() const
ERoute
Used to identify whether a bus/channel connection is an input or an output.
Encapsulates information about the host transport state.
Definition: IPlugStructs.h:585
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
void MakeNoteOnMsg(int noteNumber, int velocity, int offset, int channel=0)
Make a Note On message.
Definition: IPlugMidi.h:145
void MakeNoteOffMsg(int noteNumber, int offset, int channel=0)
Make a Note Off message.
Definition: IPlugMidi.h:158
An IOConfig is used to store bus info for each input/output configuration defined in the channel io s...
Definition: IPlugStructs.h:504
int NBuses(ERoute direction) const
Definition: IPlugStructs.h:549
int GetTotalNChannels(ERoute direction) const
Get the total number of channels across all direction buses for this IOConfig.
Definition: IPlugStructs.h:557
bool ContainsWildcard(ERoute direction) const
Definition: IPlugStructs.h:571
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:539