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