DistrhoPluginAU.cpp (129594B)
1 /* 2 * DISTRHO Plugin Framework (DPF) 3 * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any purpose with 6 * or without fee is hereby granted, provided that the above copyright notice and this 7 * permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN 11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 // TODO 18 // - g_nextBundlePath vs d_nextBundlePath cleanup 19 // - scale points to kAudioUnitParameterFlag_ValuesHaveStrings 20 // - report latency changes 21 22 #include "DistrhoPluginInternal.hpp" 23 #include "../DistrhoPluginUtils.hpp" 24 25 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 26 # include "../extra/RingBuffer.hpp" 27 #endif 28 29 #include <AudioUnit/AudioUnit.h> 30 #include <AudioToolbox/AudioUnitUtilities.h> 31 32 #include <map> 33 #include <vector> 34 35 #ifndef DISTRHO_PLUGIN_BRAND_ID 36 # error DISTRHO_PLUGIN_BRAND_ID undefined! 37 #endif 38 39 #ifndef DISTRHO_PLUGIN_UNIQUE_ID 40 # error DISTRHO_PLUGIN_UNIQUE_ID undefined! 41 #endif 42 43 START_NAMESPACE_DISTRHO 44 45 // -------------------------------------------------------------------------------------------------------------------- 46 47 #ifndef __MAC_12_3 48 enum { 49 kAudioUnitProperty_MIDIOutputBufferSizeHint = 66, 50 }; 51 #endif 52 53 // -------------------------------------------------------------------------------------------------------------------- 54 55 static const char* AudioUnitPropertyID2Str(const AudioUnitPropertyID prop) noexcept 56 { 57 switch (prop) 58 { 59 #define PROP(s) case s: return #s; 60 PROP(kAudioUnitProperty_ClassInfo) 61 PROP(kAudioUnitProperty_MakeConnection) 62 PROP(kAudioUnitProperty_SampleRate) 63 PROP(kAudioUnitProperty_ParameterList) 64 PROP(kAudioUnitProperty_ParameterInfo) 65 #if !TARGET_OS_IPHONE 66 PROP(kAudioUnitProperty_FastDispatch) 67 #endif 68 PROP(kAudioUnitProperty_CPULoad) 69 PROP(kAudioUnitProperty_StreamFormat) 70 PROP(kAudioUnitProperty_ElementCount) 71 PROP(kAudioUnitProperty_Latency) 72 PROP(kAudioUnitProperty_SupportedNumChannels) 73 PROP(kAudioUnitProperty_MaximumFramesPerSlice) 74 PROP(kAudioUnitProperty_ParameterValueStrings) 75 PROP(kAudioUnitProperty_AudioChannelLayout) 76 PROP(kAudioUnitProperty_TailTime) 77 PROP(kAudioUnitProperty_BypassEffect) 78 PROP(kAudioUnitProperty_LastRenderError) 79 PROP(kAudioUnitProperty_SetRenderCallback) 80 PROP(kAudioUnitProperty_FactoryPresets) 81 PROP(kAudioUnitProperty_RenderQuality) 82 PROP(kAudioUnitProperty_HostCallbacks) 83 PROP(kAudioUnitProperty_InPlaceProcessing) 84 PROP(kAudioUnitProperty_ElementName) 85 PROP(kAudioUnitProperty_SupportedChannelLayoutTags) 86 PROP(kAudioUnitProperty_PresentPreset) 87 PROP(kAudioUnitProperty_DependentParameters) 88 PROP(kAudioUnitProperty_InputSamplesInOutput) 89 PROP(kAudioUnitProperty_ShouldAllocateBuffer) 90 PROP(kAudioUnitProperty_FrequencyResponse) 91 PROP(kAudioUnitProperty_ParameterHistoryInfo) 92 PROP(kAudioUnitProperty_NickName) 93 PROP(kAudioUnitProperty_OfflineRender) 94 PROP(kAudioUnitProperty_ParameterIDName) 95 PROP(kAudioUnitProperty_ParameterStringFromValue) 96 PROP(kAudioUnitProperty_ParameterClumpName) 97 PROP(kAudioUnitProperty_ParameterValueFromString) 98 PROP(kAudioUnitProperty_PresentationLatency) 99 PROP(kAudioUnitProperty_ClassInfoFromDocument) 100 PROP(kAudioUnitProperty_RequestViewController) 101 PROP(kAudioUnitProperty_ParametersForOverview) 102 PROP(kAudioUnitProperty_SupportsMPE) 103 PROP(kAudioUnitProperty_RenderContextObserver) 104 PROP(kAudioUnitProperty_LastRenderSampleTime) 105 PROP(kAudioUnitProperty_LoadedOutOfProcess) 106 #if !TARGET_OS_IPHONE 107 PROP(kAudioUnitProperty_SetExternalBuffer) 108 PROP(kAudioUnitProperty_GetUIComponentList) 109 PROP(kAudioUnitProperty_CocoaUI) 110 PROP(kAudioUnitProperty_IconLocation) 111 PROP(kAudioUnitProperty_AUHostIdentifier) 112 #endif 113 PROP(kAudioUnitProperty_MIDIOutputCallbackInfo) 114 PROP(kAudioUnitProperty_MIDIOutputCallback) 115 PROP(kAudioUnitProperty_MIDIOutputEventListCallback) 116 PROP(kAudioUnitProperty_AudioUnitMIDIProtocol) 117 PROP(kAudioUnitProperty_HostMIDIProtocol) 118 PROP(kAudioUnitProperty_MIDIOutputBufferSizeHint) 119 PROP(kMusicDeviceProperty_DualSchedulingMode) 120 #undef PROP 121 // DPF specific properties 122 #define PROP(s) case d_cconst(#s): return #s; 123 PROP(DPFi) 124 PROP(DPFe) 125 PROP(DPFp) 126 PROP(DPFn) 127 PROP(DPFo) 128 PROP(DPFl) 129 PROP(DPFs) 130 PROP(DPFa) 131 #undef PROP 132 } 133 return "[unknown]"; 134 } 135 136 static const char* AudioUnitScope2Str(const AudioUnitScope scope) noexcept 137 { 138 switch (scope) 139 { 140 #define SCOPE(s) case s: return #s; 141 SCOPE(kAudioUnitScope_Global) 142 SCOPE(kAudioUnitScope_Input) 143 SCOPE(kAudioUnitScope_Output) 144 SCOPE(kAudioUnitScope_Group) 145 SCOPE(kAudioUnitScope_Part) 146 SCOPE(kAudioUnitScope_Note) 147 SCOPE(kAudioUnitScope_Layer) 148 SCOPE(kAudioUnitScope_LayerItem) 149 #undef SCOPE 150 } 151 return "[unknown]"; 152 } 153 154 static const char* AudioUnitSelector2Str(const SInt16 selector) noexcept 155 { 156 switch (selector) 157 { 158 #define SEL(s) case s: return #s; 159 SEL(kAudioUnitInitializeSelect) 160 SEL(kAudioUnitUninitializeSelect) 161 SEL(kAudioUnitGetPropertyInfoSelect) 162 SEL(kAudioUnitGetPropertySelect) 163 SEL(kAudioUnitSetPropertySelect) 164 SEL(kAudioUnitAddPropertyListenerSelect) 165 SEL(kAudioUnitRemovePropertyListenerSelect) 166 SEL(kAudioUnitRemovePropertyListenerWithUserDataSelect) 167 SEL(kAudioUnitAddRenderNotifySelect) 168 SEL(kAudioUnitRemoveRenderNotifySelect) 169 SEL(kAudioUnitGetParameterSelect) 170 SEL(kAudioUnitSetParameterSelect) 171 SEL(kAudioUnitScheduleParametersSelect) 172 SEL(kAudioUnitRenderSelect) 173 SEL(kAudioUnitResetSelect) 174 SEL(kAudioUnitComplexRenderSelect) 175 SEL(kAudioUnitProcessSelect) 176 SEL(kAudioUnitProcessMultipleSelect) 177 SEL(kMusicDeviceMIDIEventSelect) 178 SEL(kMusicDeviceSysExSelect) 179 SEL(kMusicDevicePrepareInstrumentSelect) 180 SEL(kMusicDeviceReleaseInstrumentSelect) 181 SEL(kMusicDeviceStartNoteSelect) 182 SEL(kMusicDeviceStopNoteSelect) 183 SEL(kMusicDeviceMIDIEventListSelect) 184 SEL(kAudioOutputUnitStartSelect) 185 SEL(kAudioOutputUnitStopSelect) 186 #undef SEL 187 } 188 return "[unknown]"; 189 } 190 191 #if 0 192 static OSStatus FastDispatchGetParameter(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, Float32*); 193 static OSStatus FastDispatchSetParameter(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, Float32, UInt32); 194 static OSStatus FastDispatchRender(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*); 195 #endif 196 197 // -------------------------------------------------------------------------------------------------------------------- 198 199 static constexpr const uint32_t kType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_AU_TYPE)); 200 static constexpr const uint32_t kSubType = d_cconst(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID)); 201 static constexpr const uint32_t kManufacturer = d_cconst(STRINGIFY(DISTRHO_PLUGIN_BRAND_ID)); 202 203 static constexpr const uint32_t kWantedAudioFormat = kAudioFormatFlagsNativeFloatPacked 204 | kAudioFormatFlagIsNonInterleaved; 205 206 207 // -------------------------------------------------------------------------------------------------------------------- 208 // clang `std::max` is not constexpr compatible, we need to define our own 209 210 template<typename T> 211 static inline constexpr T d_max(const T a, const T b) { return a > b ? a : b; } 212 213 // -------------------------------------------------------------------------------------------------------------------- 214 215 static constexpr const AUChannelInfo kChannelInfo[] = { 216 { DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS }, 217 #ifdef DISTRHO_PLUGIN_EXTRA_IO 218 DISTRHO_PLUGIN_EXTRA_IO 219 #endif 220 }; 221 222 #ifdef DISTRHO_PLUGIN_EXTRA_IO 223 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 224 #error DISTRHO_PLUGIN_EXTRA_IO defined but no IO available 225 #endif 226 227 static inline 228 bool isInputNumChannelsValid(const uint16_t numChannels) 229 { 230 for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i) 231 { 232 if (kChannelInfo[i].inChannels == numChannels) 233 return true; 234 } 235 return false; 236 } 237 238 static inline 239 bool isOutputNumChannelsValid(const uint16_t numChannels) 240 { 241 for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i) 242 { 243 if (kChannelInfo[i].outChannels == numChannels) 244 return true; 245 } 246 return false; 247 } 248 249 static inline 250 bool isNumChannelsComboValid(const uint16_t numInputs, const uint16_t numOutputs) 251 { 252 for (uint16_t i = 0; i < ARRAY_SIZE(kChannelInfo); ++i) 253 { 254 if (kChannelInfo[i].inChannels == numInputs && kChannelInfo[i].outChannels == numOutputs) 255 return true; 256 } 257 return false; 258 } 259 #endif 260 261 // -------------------------------------------------------------------------------------------------------------------- 262 263 struct PropertyListener { 264 AudioUnitPropertyID prop; 265 AudioUnitPropertyListenerProc proc; 266 void* userData; 267 }; 268 269 struct RenderListener { 270 AURenderCallback proc; 271 void* userData; 272 }; 273 274 typedef std::vector<PropertyListener> PropertyListeners; 275 typedef std::vector<RenderListener> RenderListeners; 276 277 // -------------------------------------------------------------------------------------------------------------------- 278 279 typedef struct { 280 UInt32 numPackets; 281 MIDIPacket packets[kMaxMidiEvents]; 282 } d_MIDIPacketList; 283 284 // -------------------------------------------------------------------------------------------------------------------- 285 286 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 287 static constexpr const writeMidiFunc writeMidiCallback = nullptr; 288 #endif 289 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 290 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; 291 #endif 292 #if ! DISTRHO_PLUGIN_WANT_STATE 293 static constexpr const updateStateValueFunc updateStateValueCallback = nullptr; 294 #endif 295 #if DISTRHO_PLUGIN_WANT_TIMEPOS 296 static constexpr const double kDefaultTicksPerBeat = 1920.0; 297 #endif 298 299 typedef std::map<const String, String> StringMap; 300 301 // -------------------------------------------------------------------------------------------------------------------- 302 303 class PluginAU 304 { 305 public: 306 PluginAU(const AudioUnit component) 307 : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, updateStateValueCallback), 308 fComponent(component), 309 fLastRenderError(noErr), 310 fPropertyListeners(), 311 fRenderListeners(), 312 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 313 fInputConnectionBus(0), 314 fInputConnectionUnit(nullptr), 315 fSampleRateForInput(d_nextSampleRate), 316 #ifdef DISTRHO_PLUGIN_EXTRA_IO 317 fNumInputs(DISTRHO_PLUGIN_NUM_INPUTS), 318 #endif 319 #endif 320 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 321 fSampleRateForOutput(d_nextSampleRate), 322 #ifdef DISTRHO_PLUGIN_EXTRA_IO 323 fNumOutputs(DISTRHO_PLUGIN_NUM_OUTPUTS), 324 #endif 325 #endif 326 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 327 fAudioBufferList(nullptr), 328 #endif 329 fUsingRenderListeners(false), 330 fParameterCount(fPlugin.getParameterCount()), 331 fLastParameterValues(nullptr), 332 fBypassParameterIndex(UINT32_MAX) 333 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 334 , fMidiEventCount(0) 335 #endif 336 #if DISTRHO_PLUGIN_WANT_PROGRAMS 337 , fCurrentProgram(-1) 338 , fLastFactoryProgram(0) 339 , fProgramCount(fPlugin.getProgramCount()) 340 , fFactoryPresetsData(nullptr) 341 #endif 342 #if DISTRHO_PLUGIN_WANT_STATE 343 , fStateCount(fPlugin.getStateCount()) 344 #endif 345 { 346 if (fParameterCount != 0) 347 { 348 fLastParameterValues = new float[fParameterCount]; 349 std::memset(fLastParameterValues, 0, sizeof(float) * fParameterCount); 350 351 for (uint32_t i=0; i<fParameterCount; ++i) 352 { 353 fLastParameterValues[i] = fPlugin.getParameterValue(i); 354 355 if (fPlugin.getParameterDesignation(i) == kParameterDesignationBypass) 356 fBypassParameterIndex = i; 357 } 358 } 359 360 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 361 std::memset(&fInputRenderCallback, 0, sizeof(fInputRenderCallback)); 362 fInputRenderCallback.inputProc = nullptr; 363 fInputRenderCallback.inputProcRefCon = nullptr; 364 #endif 365 366 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 367 std::memset(&fMidiEvents, 0, sizeof(fMidiEvents)); 368 #endif 369 370 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 371 std::memset(&fMidiOutput, 0, sizeof(fMidiOutput)); 372 std::memset(&fMidiOutputPackets, 0, sizeof(fMidiOutputPackets)); 373 #endif 374 375 #if DISTRHO_PLUGIN_WANT_PROGRAMS 376 if (fProgramCount != 0) 377 { 378 fFactoryPresetsData = new AUPreset[fProgramCount]; 379 std::memset(fFactoryPresetsData, 0, sizeof(AUPreset) * fProgramCount); 380 381 for (uint32_t i=0; i<fProgramCount; ++i) 382 { 383 fFactoryPresetsData[i].presetNumber = i; 384 385 if (const CFStringRef nameRef = CFStringCreateWithCString(nullptr, 386 fPlugin.getProgramName(i), 387 kCFStringEncodingUTF8)) 388 fFactoryPresetsData[i].presetName = nameRef; 389 else 390 fFactoryPresetsData[i].presetName = CFSTR(""); 391 } 392 } 393 else 394 { 395 fFactoryPresetsData = new AUPreset; 396 std::memset(fFactoryPresetsData, 0, sizeof(AUPreset)); 397 398 fFactoryPresetsData->presetNumber = 0; 399 fFactoryPresetsData->presetName = CFSTR("Default"); 400 } 401 #endif 402 403 fUserPresetData.presetNumber = -1; 404 fUserPresetData.presetName = CFSTR(""); 405 406 #if DISTRHO_PLUGIN_WANT_STATE 407 for (uint32_t i=0; i<fStateCount; ++i) 408 { 409 const String& dkey(fPlugin.getStateKey(i)); 410 fStateMap[dkey] = fPlugin.getStateDefaultValue(i); 411 } 412 #endif 413 414 #if DISTRHO_PLUGIN_WANT_TIMEPOS 415 std::memset(&fHostCallbackInfo, 0, sizeof(fHostCallbackInfo)); 416 fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat; 417 #endif 418 } 419 420 ~PluginAU() 421 { 422 delete[] fLastParameterValues; 423 CFRelease(fUserPresetData.presetName); 424 425 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 426 reallocAudioBufferList(false); 427 #endif 428 429 #if DISTRHO_PLUGIN_WANT_PROGRAMS 430 for (uint32_t i=0; i<fProgramCount; ++i) 431 CFRelease(fFactoryPresetsData[i].presetName); 432 delete[] fFactoryPresetsData; 433 #endif 434 } 435 436 OSStatus auInitialize() 437 { 438 #if defined(DISTRHO_PLUGIN_EXTRA_IO) && DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 439 if (! isNumChannelsComboValid(fNumInputs, fNumOutputs)) 440 return kAudioUnitErr_FormatNotSupported; 441 #endif 442 443 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 444 if (! reallocAudioBufferList(true)) 445 return kAudio_ParamError; 446 #endif 447 448 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 449 fMidiEventCount = 0; 450 #endif 451 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 452 fMidiOutputPackets.numPackets = 0; 453 #endif 454 #if DISTRHO_PLUGIN_WANT_TIMEPOS 455 fTimePosition.clear(); 456 fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat; 457 #endif 458 459 fPlugin.activate(); 460 return noErr; 461 } 462 463 OSStatus auUninitialize() 464 { 465 fPlugin.deactivateIfNeeded(); 466 467 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 468 reallocAudioBufferList(false); 469 #endif 470 return noErr; 471 } 472 473 OSStatus auGetPropertyInfo(const AudioUnitPropertyID inProp, 474 const AudioUnitScope inScope, 475 const AudioUnitElement inElement, 476 UInt32& outDataSize, 477 Boolean& outWritable) 478 { 479 switch (inProp) 480 { 481 case kAudioUnitProperty_ClassInfo: 482 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 483 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 484 outDataSize = sizeof(CFPropertyListRef); 485 outWritable = true; 486 return noErr; 487 488 case kAudioUnitProperty_MakeConnection: 489 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); 490 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 491 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 492 outDataSize = sizeof(AudioUnitConnection); 493 outWritable = true; 494 return noErr; 495 #else 496 return kAudioUnitErr_InvalidProperty; 497 #endif 498 499 case kAudioUnitProperty_SampleRate: 500 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 501 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 502 if (inScope == kAudioUnitScope_Input) 503 { 504 outDataSize = sizeof(Float64); 505 outWritable = true; 506 return noErr; 507 } 508 #endif 509 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 510 if (inScope == kAudioUnitScope_Output) 511 { 512 outDataSize = sizeof(Float64); 513 outWritable = true; 514 return noErr; 515 } 516 #endif 517 return kAudioUnitErr_InvalidScope; 518 519 case kAudioUnitProperty_ParameterList: 520 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 521 outDataSize = inScope == kAudioUnitScope_Global ? sizeof(AudioUnitParameterID) * fParameterCount : 0; 522 outWritable = false; 523 return noErr; 524 525 case kAudioUnitProperty_ParameterInfo: 526 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 527 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); 528 outDataSize = sizeof(AudioUnitParameterInfo); 529 outWritable = false; 530 return noErr; 531 532 #if 0 533 case kAudioUnitProperty_FastDispatch: 534 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 535 outDataSize = sizeof(void*); 536 outWritable = false; 537 return noErr; 538 #endif 539 540 case kAudioUnitProperty_StreamFormat: 541 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 542 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 543 if (inScope == kAudioUnitScope_Input) 544 { 545 outDataSize = sizeof(AudioStreamBasicDescription); 546 outWritable = true; 547 return noErr; 548 } 549 #endif 550 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 551 if (inScope == kAudioUnitScope_Output) 552 { 553 outDataSize = sizeof(AudioStreamBasicDescription); 554 outWritable = true; 555 return noErr; 556 } 557 #endif 558 return kAudioUnitErr_InvalidScope; 559 560 case kAudioUnitProperty_ElementCount: 561 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 562 outDataSize = sizeof(UInt32); 563 outWritable = false; 564 return noErr; 565 566 case kAudioUnitProperty_Latency: 567 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 568 #if DISTRHO_PLUGIN_WANT_LATENCY 569 if (inScope == kAudioUnitScope_Global) 570 { 571 outDataSize = sizeof(Float64); 572 outWritable = false; 573 return noErr; 574 } 575 #endif 576 return kAudioUnitErr_InvalidProperty; 577 578 case kAudioUnitProperty_SupportedNumChannels: 579 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 580 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 581 outDataSize = sizeof(kChannelInfo); 582 outWritable = false; 583 return noErr; 584 585 case kAudioUnitProperty_MaximumFramesPerSlice: 586 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 587 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 588 outDataSize = sizeof(UInt32); 589 outWritable = true; 590 return noErr; 591 592 case kAudioUnitProperty_BypassEffect: 593 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 594 if (inScope == kAudioUnitScope_Global && fBypassParameterIndex != UINT32_MAX) 595 { 596 outDataSize = sizeof(UInt32); 597 outWritable = true; 598 return noErr; 599 } 600 return kAudioUnitErr_InvalidProperty; 601 602 case kAudioUnitProperty_LastRenderError: 603 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 604 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 605 outDataSize = sizeof(OSStatus); 606 outWritable = false; 607 return noErr; 608 609 case kAudioUnitProperty_SetRenderCallback: 610 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); 611 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 612 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 613 outDataSize = sizeof(AURenderCallbackStruct); 614 outWritable = true; 615 return noErr; 616 #else 617 return kAudioUnitErr_InvalidProperty; 618 #endif 619 620 case kAudioUnitProperty_FactoryPresets: 621 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 622 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 623 #if DISTRHO_PLUGIN_WANT_PROGRAMS 624 outDataSize = sizeof(CFArrayRef); 625 outWritable = false; 626 return noErr; 627 #else 628 return kAudioUnitErr_InvalidProperty; 629 #endif 630 631 case kAudioUnitProperty_HostCallbacks: 632 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 633 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 634 #if DISTRHO_PLUGIN_WANT_TIMEPOS 635 outDataSize = sizeof(HostCallbackInfo); 636 outWritable = false; 637 return noErr; 638 #else 639 return kAudioUnitErr_InvalidProperty; 640 #endif 641 642 case kAudioUnitProperty_InPlaceProcessing: 643 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 644 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 645 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 646 outDataSize = sizeof(UInt32); 647 outWritable = false; 648 return noErr; 649 #else 650 return kAudioUnitErr_InvalidProperty; 651 #endif 652 653 case kAudioUnitProperty_PresentPreset: 654 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 655 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 656 outDataSize = sizeof(AUPreset); 657 outWritable = true; 658 return noErr; 659 660 case kAudioUnitProperty_CocoaUI: 661 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 662 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 663 #if DISTRHO_PLUGIN_HAS_UI 664 outDataSize = sizeof(AudioUnitCocoaViewInfo); 665 outWritable = false; 666 return noErr; 667 #else 668 return kAudioUnitErr_InvalidProperty; 669 #endif 670 671 case kAudioUnitProperty_MIDIOutputCallbackInfo: 672 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 673 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 674 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 675 outDataSize = sizeof(CFArrayRef); 676 outWritable = false; 677 return noErr; 678 #else 679 return kAudioUnitErr_InvalidProperty; 680 #endif 681 682 case kAudioUnitProperty_MIDIOutputCallback: 683 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 684 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 685 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 686 outDataSize = sizeof(AUMIDIOutputCallbackStruct); 687 outWritable = true; 688 return noErr; 689 #else 690 return kAudioUnitErr_InvalidProperty; 691 #endif 692 693 case kAudioUnitProperty_AudioUnitMIDIProtocol: 694 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 695 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 696 // FIXME implement the event list stuff 697 #if 0 && (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT) 698 outDataSize = sizeof(SInt32); 699 outWritable = false; 700 return noErr; 701 #else 702 return kAudioUnitErr_InvalidProperty; 703 #endif 704 705 case 'DPFi': 706 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 707 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 708 outDataSize = sizeof(uint16_t); 709 outWritable = false; 710 return noErr; 711 712 case 'DPFe': 713 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 714 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); 715 outDataSize = sizeof(uint8_t); 716 outWritable = true; 717 return noErr; 718 719 case 'DPFp': 720 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 721 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); 722 outDataSize = sizeof(float); 723 outWritable = true; 724 return noErr; 725 726 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 727 case 'DPFn': 728 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 729 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 730 outDataSize = sizeof(uint8_t) * 3; 731 outWritable = true; 732 return noErr; 733 #endif 734 735 #if DISTRHO_PLUGIN_WANT_PROGRAMS 736 case 'DPFo': 737 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 738 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 739 outDataSize = sizeof(uint32_t); 740 outWritable = false; 741 return noErr; 742 #endif 743 744 #if DISTRHO_PLUGIN_WANT_STATE 745 case 'DPFl': 746 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 747 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 748 outDataSize = sizeof(CFArrayRef); 749 outWritable = false; 750 return noErr; 751 752 case 'DPFs': 753 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 754 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement); 755 outDataSize = sizeof(CFStringRef); 756 outWritable = true; 757 return noErr; 758 #endif 759 760 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 761 case 'DPFa': 762 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 763 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 764 outDataSize = sizeof(void*); 765 outWritable = false; 766 return noErr; 767 #endif 768 769 // unwanted properties 770 case kAudioUnitProperty_CPULoad: 771 case kAudioUnitProperty_RenderContextObserver: 772 case kAudioUnitProperty_AudioChannelLayout: 773 case kAudioUnitProperty_TailTime: 774 case kAudioUnitProperty_SupportedChannelLayoutTags: 775 case kMusicDeviceProperty_DualSchedulingMode: 776 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 777 return kAudioUnitErr_InvalidProperty; 778 } 779 780 d_stdout("TODO GetPropertyInfo(%d:%x:%s, %d:%s, %d, ...)", 781 inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); 782 return kAudioUnitErr_InvalidProperty; 783 } 784 785 OSStatus auGetProperty(const AudioUnitPropertyID inProp, 786 const AudioUnitScope inScope, 787 const AudioUnitElement inElement, 788 void* const outData) 789 { 790 switch (inProp) 791 { 792 case kAudioUnitProperty_ClassInfo: 793 *static_cast<CFPropertyListRef*>(outData) = retrieveClassInfo(); 794 return noErr; 795 796 case kAudioUnitProperty_SampleRate: 797 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 798 if (inScope == kAudioUnitScope_Input) 799 { 800 *static_cast<Float64*>(outData) = fSampleRateForInput; 801 } 802 else 803 #endif 804 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 805 if (inScope == kAudioUnitScope_Output) 806 { 807 *static_cast<Float64*>(outData) = fSampleRateForOutput; 808 } 809 else 810 #endif 811 { 812 return kAudioUnitErr_InvalidScope; 813 } 814 return noErr; 815 816 case kAudioUnitProperty_ParameterList: 817 { 818 AudioUnitParameterID* const paramList = static_cast<AudioUnitParameterID*>(outData); 819 820 for (uint32_t i=0; i<fParameterCount; ++i) 821 paramList[i] = i; 822 } 823 return noErr; 824 825 case kAudioUnitProperty_ParameterInfo: 826 { 827 AudioUnitParameterInfo* const info = static_cast<AudioUnitParameterInfo*>(outData); 828 std::memset(info, 0, sizeof(*info)); 829 830 const ParameterRanges& ranges(fPlugin.getParameterRanges(inElement)); 831 832 info->flags = kAudioUnitParameterFlag_IsHighResolution 833 | kAudioUnitParameterFlag_IsReadable 834 | kAudioUnitParameterFlag_HasCFNameString; 835 836 if (fPlugin.getParameterDesignation(inElement) == kParameterDesignationBypass) 837 { 838 info->flags |= kAudioUnitParameterFlag_IsWritable; 839 info->unit = kAudioUnitParameterUnit_Boolean; 840 841 d_strncpy(info->name, "Bypass", sizeof(info->name)); 842 info->cfNameString = CFSTR("Bypass"); 843 } 844 else 845 { 846 const uint32_t hints = fPlugin.getParameterHints(inElement); 847 848 info->flags |= kAudioUnitParameterFlag_CFNameRelease; 849 850 if (hints & kParameterIsOutput) 851 { 852 info->flags |= kAudioUnitParameterFlag_MeterReadOnly; 853 } 854 else 855 { 856 info->flags |= kAudioUnitParameterFlag_IsWritable; 857 858 if ((hints & kParameterIsAutomatable) == 0x0) 859 info->flags |= kAudioUnitParameterFlag_NonRealTime; 860 } 861 862 if (hints & kParameterIsBoolean) 863 info->unit = kAudioUnitParameterUnit_Boolean; 864 else if (hints & kParameterIsInteger) 865 info->unit = kAudioUnitParameterUnit_Indexed; 866 else 867 info->unit = kAudioUnitParameterUnit_Generic; 868 869 // | kAudioUnitParameterFlag_ValuesHaveStrings; 870 871 const String& name(fPlugin.getParameterName(inElement)); 872 d_strncpy(info->name, name, sizeof(info->name)); 873 info->cfNameString = CFStringCreateWithCString(nullptr, name, kCFStringEncodingUTF8); 874 } 875 876 info->minValue = ranges.min; 877 info->maxValue = ranges.max; 878 info->defaultValue = ranges.def; 879 } 880 return noErr; 881 882 #if 0 883 case kAudioUnitProperty_FastDispatch: 884 switch (inElement) 885 { 886 case kAudioUnitGetParameterSelect: 887 *static_cast<AudioUnitGetParameterProc*>(outData) = FastDispatchGetParameter; 888 return noErr; 889 case kAudioUnitSetParameterSelect: 890 *static_cast<AudioUnitSetParameterProc*>(outData) = FastDispatchSetParameter; 891 return noErr; 892 case kAudioUnitRenderSelect: 893 *static_cast<AudioUnitRenderProc*>(outData) = FastDispatchRender; 894 return noErr; 895 } 896 d_stdout("WIP FastDispatch(%d:%x:%s)", inElement, inElement, AudioUnitPropertyID2Str(inElement)); 897 return kAudioUnitErr_InvalidElement; 898 #endif 899 900 case kAudioUnitProperty_StreamFormat: 901 { 902 AudioStreamBasicDescription* const desc = static_cast<AudioStreamBasicDescription*>(outData); 903 std::memset(desc, 0, sizeof(*desc)); 904 905 if (inElement != 0) 906 return kAudioUnitErr_InvalidElement; 907 908 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 909 if (inScope == kAudioUnitScope_Input) 910 { 911 #ifdef DISTRHO_PLUGIN_EXTRA_IO 912 desc->mChannelsPerFrame = fNumInputs; 913 #else 914 desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_INPUTS; 915 #endif 916 } 917 else 918 #endif 919 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 920 if (inScope == kAudioUnitScope_Output) 921 { 922 #ifdef DISTRHO_PLUGIN_EXTRA_IO 923 desc->mChannelsPerFrame = fNumOutputs; 924 #else 925 desc->mChannelsPerFrame = DISTRHO_PLUGIN_NUM_OUTPUTS; 926 #endif 927 } 928 else 929 #endif 930 { 931 return kAudioUnitErr_InvalidScope; 932 } 933 934 desc->mFormatID = kAudioFormatLinearPCM; 935 desc->mFormatFlags = kWantedAudioFormat; 936 desc->mSampleRate = fPlugin.getSampleRate(); 937 desc->mBitsPerChannel = 32; 938 desc->mBytesPerFrame = sizeof(float); 939 desc->mBytesPerPacket = sizeof(float); 940 desc->mFramesPerPacket = 1; 941 } 942 return noErr; 943 944 case kAudioUnitProperty_ElementCount: 945 switch (inScope) 946 { 947 case kAudioUnitScope_Global: 948 *static_cast<UInt32*>(outData) = 1; 949 break; 950 case kAudioUnitScope_Input: 951 *static_cast<UInt32*>(outData) = DISTRHO_PLUGIN_NUM_INPUTS != 0 ? 1 : 0; 952 break; 953 case kAudioUnitScope_Output: 954 *static_cast<UInt32*>(outData) = DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? 1 : 0; 955 break; 956 default: 957 *static_cast<UInt32*>(outData) = 0; 958 break; 959 } 960 return noErr; 961 962 #if DISTRHO_PLUGIN_WANT_LATENCY 963 case kAudioUnitProperty_Latency: 964 *static_cast<Float64*>(outData) = static_cast<double>(fPlugin.getLatency()) / fPlugin.getSampleRate(); 965 return noErr; 966 #endif 967 968 case kAudioUnitProperty_SupportedNumChannels: 969 std::memcpy(outData, kChannelInfo, sizeof(kChannelInfo)); 970 return noErr; 971 972 case kAudioUnitProperty_MaximumFramesPerSlice: 973 *static_cast<UInt32*>(outData) = fPlugin.getBufferSize(); 974 return noErr; 975 976 case kAudioUnitProperty_LastRenderError: 977 *static_cast<OSStatus*>(outData) = fLastRenderError; 978 fLastRenderError = noErr; 979 return noErr; 980 981 case kAudioUnitProperty_BypassEffect: 982 *static_cast<OSStatus*>(outData) = fPlugin.getParameterValue(fBypassParameterIndex) > 0.5f ? 1 : 0; 983 return noErr; 984 985 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 986 case kAudioUnitProperty_SetRenderCallback: 987 std::memcpy(outData, &fInputRenderCallback, sizeof(AURenderCallbackStruct)); 988 return noErr; 989 #endif 990 991 #if DISTRHO_PLUGIN_WANT_PROGRAMS 992 case kAudioUnitProperty_FactoryPresets: 993 if (const CFMutableArrayRef presetsRef = CFArrayCreateMutable(nullptr, fProgramCount, nullptr)) 994 { 995 for (uint32_t i=0; i<fProgramCount; ++i) 996 CFArrayAppendValue(presetsRef, &fFactoryPresetsData[i]); 997 998 *static_cast<CFArrayRef*>(outData) = presetsRef; 999 return noErr; 1000 } 1001 return kAudio_ParamError; 1002 #endif 1003 1004 #if DISTRHO_PLUGIN_WANT_TIMEPOS 1005 case kAudioUnitProperty_HostCallbacks: 1006 std::memcpy(outData, &fHostCallbackInfo, sizeof(HostCallbackInfo)); 1007 return noErr; 1008 #endif 1009 1010 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1011 case kAudioUnitProperty_InPlaceProcessing: 1012 *static_cast<UInt32*>(outData) = 1; 1013 return noErr; 1014 #endif 1015 1016 case kAudioUnitProperty_PresentPreset: 1017 #if DISTRHO_PLUGIN_WANT_PROGRAMS 1018 if (fCurrentProgram >= 0) 1019 { 1020 std::memcpy(outData, &fFactoryPresetsData[fCurrentProgram], sizeof(AUPreset)); 1021 } 1022 else 1023 #endif 1024 { 1025 std::memcpy(outData, &fUserPresetData, sizeof(AUPreset)); 1026 } 1027 return noErr; 1028 1029 #if DISTRHO_PLUGIN_HAS_UI 1030 case kAudioUnitProperty_CocoaUI: 1031 { 1032 AudioUnitCocoaViewInfo* const info = static_cast<AudioUnitCocoaViewInfo*>(outData); 1033 std::memset(info, 0, sizeof(*info)); 1034 1035 NSString* const bundlePathString = [[NSString alloc] 1036 initWithBytes:d_nextBundlePath 1037 length:strlen(d_nextBundlePath) 1038 encoding:NSUTF8StringEncoding]; 1039 1040 info->mCocoaAUViewBundleLocation = static_cast<CFURLRef>([[NSURL fileURLWithPath: bundlePathString] retain]); 1041 1042 #define MACRO_STR3(a, b, c) a "_" b "_" c 1043 #define MACRO_STR2(a, b, c) MACRO_STR3(#a, #b, #c) 1044 #define MACRO_STR(a, b, c) MACRO_STR2(a, b, c) 1045 1046 info->mCocoaAUViewClass[0] = CFSTR("CocoaAUView_" MACRO_STR(DISTRHO_PLUGIN_AU_TYPE, 1047 DISTRHO_PLUGIN_UNIQUE_ID, 1048 DISTRHO_PLUGIN_BRAND_ID)); 1049 1050 #undef MACRO_STR 1051 #undef MACRO_STR2 1052 #undef MACRO_STR3 1053 1054 [bundlePathString release]; 1055 } 1056 return noErr; 1057 #endif 1058 1059 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1060 case kAudioUnitProperty_MIDIOutputCallbackInfo: 1061 { 1062 CFStringRef refs[1] = { CFSTR("MIDI Output") }; 1063 *static_cast<CFArrayRef*>(outData) = CFArrayCreate(nullptr, 1064 reinterpret_cast<const void**>(refs), 1065 1, 1066 &kCFTypeArrayCallBacks); 1067 } 1068 return noErr; 1069 #endif 1070 1071 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1072 /* FIXME implement the event list stuff 1073 case kAudioUnitProperty_AudioUnitMIDIProtocol: 1074 *static_cast<SInt32*>(outData) = kMIDIProtocol_1_0; 1075 return noErr; 1076 */ 1077 #endif 1078 1079 case 'DPFp': 1080 *static_cast<float*>(outData) = fPlugin.getParameterValue(inElement); 1081 return noErr; 1082 1083 #if DISTRHO_PLUGIN_WANT_PROGRAMS 1084 case 'DPFo': 1085 *static_cast<uint32_t*>(outData) = fLastFactoryProgram; 1086 return noErr; 1087 #endif 1088 1089 #if DISTRHO_PLUGIN_WANT_STATE 1090 case 'DPFl': 1091 if (const CFMutableArrayRef keysRef = CFArrayCreateMutable(nullptr, 1092 fStateCount, 1093 &kCFTypeArrayCallBacks)) 1094 { 1095 for (uint32_t i=0; i<fStateCount; ++i) 1096 { 1097 const CFStringRef keyRef = CFStringCreateWithCString(nullptr, 1098 fPlugin.getStateKey(i), 1099 kCFStringEncodingASCII); 1100 CFArrayAppendValue(keysRef, keyRef); 1101 CFRelease(keyRef); 1102 } 1103 1104 *static_cast<CFArrayRef*>(outData) = keysRef; 1105 return noErr; 1106 } 1107 return kAudio_ParamError; 1108 1109 case 'DPFs': 1110 { 1111 const String& key(fPlugin.getStateKey(inElement)); 1112 #if DISTRHO_PLUGIN_WANT_FULL_STATE 1113 fStateMap[key] = fPlugin.getStateValue(key); 1114 #endif 1115 1116 *static_cast<CFStringRef*>(outData) = CFStringCreateWithCString(nullptr, 1117 fStateMap[key], 1118 kCFStringEncodingUTF8); 1119 } 1120 return noErr; 1121 #endif 1122 1123 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1124 case 'DPFa': 1125 *static_cast<void**>(outData) = fPlugin.getInstancePointer(); 1126 return noErr; 1127 #endif 1128 } 1129 1130 d_stdout("TODO GetProperty(%d:%x:%s, %d:%s, %d, ...)", 1131 inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); 1132 return kAudioUnitErr_InvalidProperty; 1133 } 1134 1135 OSStatus auSetProperty(const AudioUnitPropertyID inProp, 1136 const AudioUnitScope inScope, 1137 const AudioUnitElement inElement, 1138 const void* const inData, 1139 const UInt32 inDataSize) 1140 { 1141 switch (inProp) 1142 { 1143 case kAudioUnitProperty_ClassInfo: 1144 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1145 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1146 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFPropertyListRef), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1147 { 1148 const CFPropertyListRef propList = *static_cast<const CFPropertyListRef*>(inData); 1149 DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(propList) == CFDictionaryGetTypeID(), kAudioUnitErr_InvalidPropertyValue); 1150 1151 restoreClassInfo(static_cast<CFDictionaryRef>(propList)); 1152 } 1153 return noErr; 1154 1155 case kAudioUnitProperty_MakeConnection: 1156 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); 1157 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1158 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioUnitConnection), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1159 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1160 { 1161 const AudioUnitConnection conn = *static_cast<const AudioUnitConnection*>(inData); 1162 1163 if (conn.sourceAudioUnit == nullptr) 1164 { 1165 fInputConnectionBus = 0; 1166 fInputConnectionUnit = nullptr; 1167 return noErr; 1168 } 1169 1170 AudioStreamBasicDescription desc; 1171 std::memset(&desc, 0, sizeof(desc)); 1172 UInt32 dataSize = sizeof(AudioStreamBasicDescription); 1173 if (AudioUnitGetProperty(conn.sourceAudioUnit, 1174 kAudioUnitProperty_StreamFormat, 1175 kAudioUnitScope_Output, 1176 conn.sourceOutputNumber, &desc, &dataSize) != noErr) 1177 return kAudioUnitErr_InvalidPropertyValue; 1178 1179 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFormatID == kAudioFormatLinearPCM, 1180 desc.mFormatID, kAudioUnitErr_FormatNotSupported); 1181 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBitsPerChannel == 32, 1182 desc.mBitsPerChannel, kAudioUnitErr_FormatNotSupported); 1183 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBytesPerFrame == sizeof(float), 1184 desc.mBytesPerFrame, kAudioUnitErr_FormatNotSupported); 1185 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mBytesPerPacket == sizeof(float), 1186 desc.mBytesPerPacket, kAudioUnitErr_FormatNotSupported); 1187 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFramesPerPacket == 1, 1188 desc.mFramesPerPacket, kAudioUnitErr_FormatNotSupported); 1189 DISTRHO_SAFE_ASSERT_INT_RETURN(desc.mFormatFlags == kWantedAudioFormat, 1190 desc.mFormatFlags, kAudioUnitErr_FormatNotSupported); 1191 #ifdef DISTRHO_PLUGIN_EXTRA_IO 1192 DISTRHO_SAFE_ASSERT_UINT_RETURN(desc.mChannelsPerFrame == fNumInputs, 1193 desc.mChannelsPerFrame, kAudioUnitErr_FormatNotSupported); 1194 #else 1195 DISTRHO_SAFE_ASSERT_UINT_RETURN(desc.mChannelsPerFrame == DISTRHO_PLUGIN_NUM_INPUTS, 1196 desc.mChannelsPerFrame, kAudioUnitErr_FormatNotSupported); 1197 #endif 1198 1199 fInputConnectionBus = conn.sourceOutputNumber; 1200 fInputConnectionUnit = conn.sourceAudioUnit; 1201 } 1202 return noErr; 1203 #else 1204 return kAudioUnitErr_PropertyNotInUse; 1205 #endif 1206 1207 case kAudioUnitProperty_SampleRate: 1208 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1209 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); 1210 #elif DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1211 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); 1212 #else 1213 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); 1214 #endif 1215 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1216 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(Float64), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1217 { 1218 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 || DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1219 const Float64 sampleRate = *static_cast<const Float64*>(inData); 1220 #endif 1221 1222 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1223 if (inScope == kAudioUnitScope_Input) 1224 { 1225 if (d_isNotEqual(fSampleRateForInput, sampleRate)) 1226 { 1227 fSampleRateForInput = sampleRate; 1228 d_nextSampleRate = sampleRate; 1229 1230 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1231 if (d_isEqual(fSampleRateForOutput, sampleRate)) 1232 #endif 1233 { 1234 fPlugin.setSampleRate(sampleRate, true); 1235 } 1236 1237 notifyPropertyListeners(inProp, inScope, inElement); 1238 } 1239 return noErr; 1240 } 1241 #endif 1242 1243 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1244 if (inScope == kAudioUnitScope_Output) 1245 { 1246 if (d_isNotEqual(fSampleRateForOutput, sampleRate)) 1247 { 1248 fSampleRateForOutput = sampleRate; 1249 d_nextSampleRate = sampleRate; 1250 1251 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1252 if (d_isEqual(fSampleRateForInput, sampleRate)) 1253 #endif 1254 { 1255 fPlugin.setSampleRate(sampleRate, true); 1256 } 1257 1258 notifyPropertyListeners(inProp, inScope, inElement); 1259 } 1260 return noErr; 1261 } 1262 #endif 1263 } 1264 return kAudioUnitErr_PropertyNotInUse; 1265 1266 case kAudioUnitProperty_StreamFormat: 1267 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1268 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input || inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); 1269 #elif DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1270 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Output, inScope, kAudioUnitErr_InvalidScope); 1271 #else 1272 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); 1273 #endif 1274 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1275 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AudioStreamBasicDescription), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1276 { 1277 const AudioStreamBasicDescription* const desc = static_cast<const AudioStreamBasicDescription*>(inData); 1278 1279 if (desc->mFormatID != kAudioFormatLinearPCM) 1280 return kAudioUnitErr_FormatNotSupported; 1281 if (desc->mBitsPerChannel != 32) 1282 return kAudioUnitErr_FormatNotSupported; 1283 if (desc->mBytesPerFrame != sizeof(float)) 1284 return kAudioUnitErr_FormatNotSupported; 1285 if (desc->mBytesPerPacket != sizeof(float)) 1286 return kAudioUnitErr_FormatNotSupported; 1287 if (desc->mFramesPerPacket != 1) 1288 return kAudioUnitErr_FormatNotSupported; 1289 if (desc->mFormatFlags != kWantedAudioFormat) 1290 return kAudioUnitErr_FormatNotSupported; 1291 1292 #ifndef DISTRHO_PLUGIN_EXTRA_IO 1293 if (desc->mChannelsPerFrame != (inScope == kAudioUnitScope_Input ? DISTRHO_PLUGIN_NUM_INPUTS 1294 : DISTRHO_PLUGIN_NUM_OUTPUTS)) 1295 return kAudioUnitErr_FormatNotSupported; 1296 #endif 1297 1298 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1299 if (inScope == kAudioUnitScope_Input) 1300 { 1301 bool changed = false; 1302 1303 #ifdef DISTRHO_PLUGIN_EXTRA_IO 1304 if (! isInputNumChannelsValid(desc->mChannelsPerFrame)) 1305 return kAudioUnitErr_FormatNotSupported; 1306 1307 if (fNumInputs != desc->mChannelsPerFrame) 1308 { 1309 changed = true; 1310 fNumInputs = desc->mChannelsPerFrame; 1311 1312 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1313 if (isNumChannelsComboValid(fNumInputs, fNumOutputs)) 1314 #endif 1315 { 1316 fPlugin.setAudioPortIO(fNumInputs, fNumOutputs); 1317 } 1318 } 1319 #endif 1320 1321 if (d_isNotEqual(fSampleRateForInput, desc->mSampleRate)) 1322 { 1323 changed = true; 1324 fSampleRateForInput = desc->mSampleRate; 1325 1326 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1327 if (d_isEqual(fSampleRateForOutput, desc->mSampleRate)) 1328 #endif 1329 { 1330 fPlugin.setSampleRate(desc->mSampleRate, true); 1331 } 1332 1333 notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); 1334 } 1335 1336 if (changed) 1337 notifyPropertyListeners(inProp, inScope, inElement); 1338 1339 return noErr; 1340 } 1341 #endif 1342 1343 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1344 if (inScope == kAudioUnitScope_Output) 1345 { 1346 bool changed = false; 1347 1348 #ifdef DISTRHO_PLUGIN_EXTRA_IO 1349 if (! isOutputNumChannelsValid(desc->mChannelsPerFrame)) 1350 return kAudioUnitErr_FormatNotSupported; 1351 1352 if (fNumOutputs != desc->mChannelsPerFrame) 1353 { 1354 changed = true; 1355 fNumOutputs = desc->mChannelsPerFrame; 1356 1357 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1358 if (isNumChannelsComboValid(fNumInputs, fNumOutputs)) 1359 #endif 1360 { 1361 fPlugin.setAudioPortIO(fNumInputs, fNumOutputs); 1362 } 1363 } 1364 #endif 1365 1366 if (d_isNotEqual(fSampleRateForOutput, desc->mSampleRate)) 1367 { 1368 changed = true; 1369 fSampleRateForOutput = desc->mSampleRate; 1370 1371 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1372 if (d_isEqual(fSampleRateForInput, desc->mSampleRate)) 1373 #endif 1374 { 1375 fPlugin.setSampleRate(desc->mSampleRate, true); 1376 } 1377 1378 notifyPropertyListeners(kAudioUnitProperty_SampleRate, inScope, inElement); 1379 } 1380 1381 if (changed) 1382 notifyPropertyListeners(inProp, inScope, inElement); 1383 1384 return noErr; 1385 } 1386 #endif 1387 } 1388 return kAudioUnitErr_PropertyNotInUse; 1389 1390 case kAudioUnitProperty_MaximumFramesPerSlice: 1391 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1392 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1393 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1394 { 1395 const UInt32 bufferSize = *static_cast<const UInt32*>(inData); 1396 1397 if (fPlugin.setBufferSize(bufferSize, true)) 1398 notifyPropertyListeners(inProp, inScope, inElement); 1399 } 1400 return noErr; 1401 1402 case kAudioUnitProperty_BypassEffect: 1403 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1404 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1405 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(UInt32), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1406 DISTRHO_SAFE_ASSERT_RETURN(fBypassParameterIndex != UINT32_MAX, kAudioUnitErr_PropertyNotInUse); 1407 { 1408 const bool bypass = *static_cast<const UInt32*>(inData) != 0; 1409 1410 if ((fLastParameterValues[fBypassParameterIndex] > 0.5f) != bypass) 1411 { 1412 const float value = bypass ? 1.f : 0.f; 1413 fLastParameterValues[fBypassParameterIndex] = value; 1414 fPlugin.setParameterValue(fBypassParameterIndex, value); 1415 notifyPropertyListeners(inProp, inScope, inElement); 1416 } 1417 } 1418 return noErr; 1419 1420 case kAudioUnitProperty_SetRenderCallback: 1421 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Input, inScope, kAudioUnitErr_InvalidScope); 1422 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1423 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AURenderCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1424 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1425 std::memcpy(&fInputRenderCallback, inData, sizeof(AURenderCallbackStruct)); 1426 return noErr; 1427 #else 1428 return kAudioUnitErr_PropertyNotInUse; 1429 #endif 1430 1431 case kAudioUnitProperty_HostCallbacks: 1432 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1433 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1434 #if DISTRHO_PLUGIN_WANT_TIMEPOS 1435 { 1436 const UInt32 usableDataSize = std::min(inDataSize, static_cast<UInt32>(sizeof(HostCallbackInfo))); 1437 const bool changed = std::memcmp(&fHostCallbackInfo, inData, usableDataSize) != 0; 1438 1439 std::memcpy(&fHostCallbackInfo, inData, usableDataSize); 1440 1441 if (sizeof(HostCallbackInfo) > usableDataSize) 1442 std::memset(&fHostCallbackInfo + usableDataSize, 0, sizeof(HostCallbackInfo) - usableDataSize); 1443 1444 if (changed) 1445 notifyPropertyListeners(inProp, inScope, inElement); 1446 } 1447 return noErr; 1448 #else 1449 return kAudioUnitErr_PropertyNotInUse; 1450 #endif 1451 1452 case kAudioUnitProperty_PresentPreset: 1453 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1454 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1455 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUPreset), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1456 { 1457 const int32_t presetNumber = static_cast<const AUPreset*>(inData)->presetNumber; 1458 1459 #if DISTRHO_PLUGIN_WANT_PROGRAMS 1460 if (presetNumber >= 0) 1461 { 1462 if (fCurrentProgram != presetNumber) 1463 { 1464 fCurrentProgram = presetNumber; 1465 fLastFactoryProgram = presetNumber; 1466 fPlugin.loadProgram(fLastFactoryProgram); 1467 notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); 1468 } 1469 } 1470 else 1471 { 1472 fCurrentProgram = presetNumber; 1473 CFRelease(fUserPresetData.presetName); 1474 std::memcpy(&fUserPresetData, inData, sizeof(AUPreset)); 1475 } 1476 #else 1477 DISTRHO_SAFE_ASSERT_INT_RETURN(presetNumber < 0, presetNumber, kAudioUnitErr_InvalidPropertyValue); 1478 CFRelease(fUserPresetData.presetName); 1479 std::memcpy(&fUserPresetData, inData, sizeof(AUPreset)); 1480 #endif 1481 } 1482 return noErr; 1483 1484 case kAudioUnitProperty_MIDIOutputCallback: 1485 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1486 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1487 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(AUMIDIOutputCallbackStruct), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1488 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1489 std::memcpy(&fMidiOutput, inData, sizeof(AUMIDIOutputCallbackStruct)); 1490 return noErr; 1491 #else 1492 return kAudioUnitErr_PropertyNotInUse; 1493 #endif 1494 1495 case 'DPFi': 1496 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1497 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1498 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint16_t), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1499 { 1500 const uint16_t magic = *static_cast<const uint16_t*>(inData); 1501 1502 if (magic != 1337) 1503 return noErr; 1504 1505 #if DISTRHO_PLUGIN_WANT_PROGRAMS 1506 notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); 1507 #endif 1508 1509 #if DISTRHO_PLUGIN_WANT_STATE 1510 for (uint32_t i=0; i<fStateCount; ++i) 1511 notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i); 1512 #endif 1513 1514 for (uint32_t i=0; i<fParameterCount; ++i) 1515 notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i); 1516 } 1517 return noErr; 1518 1519 case 'DPFe': 1520 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1521 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); 1522 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1523 { 1524 const uint8_t flag = *static_cast<const uint8_t*>(inData); 1525 1526 if (flag != 1 && flag != 2) 1527 return noErr; 1528 1529 AudioUnitEvent event; 1530 std::memset(&event, 0, sizeof(event)); 1531 1532 event.mEventType = flag == 1 ? kAudioUnitEvent_BeginParameterChangeGesture 1533 : kAudioUnitEvent_EndParameterChangeGesture; 1534 event.mArgument.mParameter.mAudioUnit = fComponent; 1535 event.mArgument.mParameter.mParameterID = inElement; 1536 event.mArgument.mParameter.mScope = kAudioUnitScope_Global; 1537 AUEventListenerNotify(NULL, NULL, &event); 1538 } 1539 return noErr; 1540 1541 case 'DPFp': 1542 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1543 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fParameterCount, inElement, kAudioUnitErr_InvalidElement); 1544 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(float), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1545 { 1546 const float value = *static_cast<const float*>(inData); 1547 DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); 1548 1549 if (d_isEqual(fLastParameterValues[inElement], value)) 1550 return noErr; 1551 1552 fLastParameterValues[inElement] = value; 1553 fPlugin.setParameterValue(inElement, value); 1554 1555 AudioUnitEvent event; 1556 std::memset(&event, 0, sizeof(event)); 1557 1558 event.mEventType = kAudioUnitEvent_ParameterValueChange; 1559 event.mArgument.mParameter.mAudioUnit = fComponent; 1560 event.mArgument.mParameter.mParameterID = inElement; 1561 event.mArgument.mParameter.mScope = kAudioUnitScope_Global; 1562 AUEventListenerNotify(NULL, NULL, &event); 1563 1564 if (fBypassParameterIndex == inElement) 1565 notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); 1566 } 1567 return noErr; 1568 1569 case 'DPFn': 1570 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1571 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement == 0, inElement, kAudioUnitErr_InvalidElement); 1572 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(uint8_t) * 3, inDataSize, kAudioUnitErr_InvalidPropertyValue); 1573 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 1574 { 1575 const uint8_t* const midiData = static_cast<const uint8_t*>(inData); 1576 1577 if (midiData[0] == 0) 1578 return noErr; 1579 1580 fNotesRingBuffer.writeCustomData(midiData, 3); 1581 fNotesRingBuffer.commitWrite(); 1582 } 1583 return noErr; 1584 #else 1585 return kAudioUnitErr_PropertyNotInUse; 1586 #endif 1587 1588 case 'DPFs': 1589 DISTRHO_SAFE_ASSERT_UINT_RETURN(inScope == kAudioUnitScope_Global, inScope, kAudioUnitErr_InvalidScope); 1590 DISTRHO_SAFE_ASSERT_UINT_RETURN(inDataSize == sizeof(CFStringRef), inDataSize, kAudioUnitErr_InvalidPropertyValue); 1591 #if DISTRHO_PLUGIN_WANT_STATE 1592 DISTRHO_SAFE_ASSERT_UINT_RETURN(inElement < fStateCount, inElement, kAudioUnitErr_InvalidElement); 1593 { 1594 const CFStringRef valueRef = *static_cast<const CFStringRef*>(inData); 1595 DISTRHO_SAFE_ASSERT_RETURN(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID(), 1596 kAudioUnitErr_InvalidPropertyValue); 1597 1598 const CFIndex valueLen = CFStringGetLength(valueRef); 1599 char* const value = static_cast<char*>(std::malloc(valueLen + 1)); 1600 DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError); 1601 DISTRHO_SAFE_ASSERT_RETURN(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8), 1602 kAudioUnitErr_InvalidPropertyValue); 1603 1604 const String& key(fPlugin.getStateKey(inElement)); 1605 1606 // save this key as needed 1607 if (fPlugin.wantStateKey(key)) 1608 fStateMap[key] = value; 1609 1610 fPlugin.setState(key, value); 1611 1612 std::free(value); 1613 } 1614 return noErr; 1615 #else 1616 return kAudioUnitErr_PropertyNotInUse; 1617 #endif 1618 1619 // unwanted properties 1620 case kMusicDeviceProperty_DualSchedulingMode: 1621 return kAudioUnitErr_PropertyNotInUse; 1622 } 1623 1624 d_stdout("TODO SetProperty(%d:%x:%s, %d:%s, %d, %p, %u)", 1625 inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); 1626 return kAudioUnitErr_InvalidProperty; 1627 } 1628 1629 OSStatus auAddPropertyListener(const AudioUnitPropertyID prop, 1630 const AudioUnitPropertyListenerProc proc, 1631 void* const userData) 1632 { 1633 const PropertyListener pl = { 1634 prop, proc, userData 1635 }; 1636 1637 fPropertyListeners.push_back(pl); 1638 return noErr; 1639 } 1640 1641 OSStatus auRemovePropertyListener(const AudioUnitPropertyID prop, const AudioUnitPropertyListenerProc proc) 1642 { 1643 for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) 1644 { 1645 const PropertyListener& pl(*it); 1646 1647 if (pl.prop == prop && pl.proc == proc) 1648 { 1649 fPropertyListeners.erase(it); 1650 return auRemovePropertyListener(prop, proc); 1651 } 1652 } 1653 1654 return noErr; 1655 } 1656 1657 OSStatus auRemovePropertyListenerWithUserData(const AudioUnitPropertyID prop, 1658 const AudioUnitPropertyListenerProc proc, 1659 void* const userData) 1660 { 1661 for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) 1662 { 1663 const PropertyListener& pl(*it); 1664 1665 if (pl.prop == prop && pl.proc == proc && pl.userData == userData) 1666 { 1667 fPropertyListeners.erase(it); 1668 return auRemovePropertyListenerWithUserData(prop, proc, userData); 1669 } 1670 } 1671 1672 return noErr; 1673 } 1674 1675 OSStatus auAddRenderNotify(const AURenderCallback proc, void* const userData) 1676 { 1677 fUsingRenderListeners = true; 1678 1679 const RenderListener rl = { 1680 proc, userData 1681 }; 1682 1683 fRenderListeners.push_back(rl); 1684 return noErr; 1685 } 1686 1687 OSStatus auRemoveRenderNotify(const AURenderCallback proc, void* const userData) 1688 { 1689 for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it) 1690 { 1691 const RenderListener& rl(*it); 1692 1693 if (rl.proc == proc && rl.userData == userData) 1694 { 1695 fRenderListeners.erase(it); 1696 return auRemoveRenderNotify(proc, userData); 1697 } 1698 } 1699 1700 if (fRenderListeners.empty()) 1701 fUsingRenderListeners = false; 1702 1703 return noErr; 1704 } 1705 1706 OSStatus auGetParameter(const AudioUnitParameterID param, 1707 const AudioUnitScope scope, 1708 const AudioUnitElement elem, 1709 AudioUnitParameterValue* const value) 1710 { 1711 DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope); 1712 DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement); 1713 DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); 1714 DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, kAudio_ParamError); 1715 1716 *value = fPlugin.getParameterValue(param); 1717 return noErr; 1718 } 1719 1720 OSStatus auSetParameter(const AudioUnitParameterID param, 1721 const AudioUnitScope scope, 1722 const AudioUnitElement elem, 1723 const AudioUnitParameterValue value, 1724 UInt32 /* bufferOffset */) 1725 { 1726 DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global, scope, kAudioUnitErr_InvalidScope); 1727 DISTRHO_SAFE_ASSERT_UINT_RETURN(param < fParameterCount, param, kAudioUnitErr_InvalidElement); 1728 DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); 1729 DISTRHO_SAFE_ASSERT_RETURN(std::isfinite(value), kAudioUnitErr_InvalidParameterValue); 1730 1731 if (d_isNotEqual(fLastParameterValues[param], value)) 1732 { 1733 fLastParameterValues[param] = value; 1734 fPlugin.setParameterValue(param, value); 1735 1736 // TODO flag param only, notify listeners later on bg thread (sem_post etc) 1737 notifyPropertyListeners('DPFp', kAudioUnitScope_Global, param); 1738 1739 if (fBypassParameterIndex == elem) 1740 notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); 1741 } 1742 1743 return noErr; 1744 } 1745 1746 OSStatus auScheduleParameters(const AudioUnitParameterEvent* const events, const UInt32 numEvents) 1747 { 1748 for (UInt32 i=0; i<numEvents; ++i) 1749 { 1750 const AudioUnitParameterEvent& event(events[i]); 1751 1752 if (event.eventType == kParameterEvent_Immediate) 1753 { 1754 auSetParameter(event.parameter, 1755 event.scope, 1756 event.element, 1757 event.eventValues.immediate.value, 1758 event.eventValues.immediate.bufferOffset); 1759 } 1760 else 1761 { 1762 // TODO? 1763 d_stdout("WIP ScheduleParameters(%p, %u)", events, numEvents); 1764 } 1765 } 1766 return noErr; 1767 } 1768 1769 OSStatus auReset(const AudioUnitScope scope, const AudioUnitElement elem) 1770 { 1771 DISTRHO_SAFE_ASSERT_UINT_RETURN(scope == kAudioUnitScope_Global || scope == kAudioUnitScope_Input || scope == kAudioUnitScope_Output, scope, kAudioUnitErr_InvalidScope); 1772 DISTRHO_SAFE_ASSERT_UINT_RETURN(elem == 0, elem, kAudioUnitErr_InvalidElement); 1773 1774 if (fPlugin.isActive()) 1775 { 1776 fPlugin.deactivate(); 1777 fPlugin.activate(); 1778 } 1779 1780 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 1781 fMidiEventCount = 0; 1782 #endif 1783 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1784 fMidiOutputPackets.numPackets = 0; 1785 #endif 1786 #if DISTRHO_PLUGIN_WANT_TIMEPOS 1787 fTimePosition.clear(); 1788 fTimePosition.bbt.ticksPerBeat = kDefaultTicksPerBeat; 1789 #endif 1790 return noErr; 1791 } 1792 1793 OSStatus auRender(const AudioUnitRenderActionFlags actionFlags, 1794 const AudioTimeStamp* const inTimeStamp, 1795 const UInt32 inBusNumber, 1796 const UInt32 inFramesToProcess, 1797 AudioBufferList* const ioData) 1798 { 1799 if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) 1800 { 1801 DISTRHO_SAFE_ASSERT_RETURN(fPlugin.isActive(), kAudio_ParamError); 1802 DISTRHO_SAFE_ASSERT_UINT_RETURN(inBusNumber == 0, inBusNumber, kAudioUnitErr_InvalidElement); 1803 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1804 DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == fAudioBufferList->mNumberBuffers, 1805 ioData->mNumberBuffers, kAudio_ParamError); 1806 #else 1807 DISTRHO_SAFE_ASSERT_UINT_RETURN(ioData->mNumberBuffers == 0, ioData->mNumberBuffers, kAudio_ParamError); 1808 #endif 1809 1810 if (inFramesToProcess > fPlugin.getBufferSize()) 1811 { 1812 setLastRenderError(kAudioUnitErr_TooManyFramesToProcess); 1813 return kAudioUnitErr_TooManyFramesToProcess; 1814 } 1815 1816 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1817 for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) 1818 { 1819 if (ioData->mBuffers[i].mDataByteSize != sizeof(float) * inFramesToProcess) 1820 { 1821 setLastRenderError(kAudio_ParamError); 1822 return kAudio_ParamError; 1823 } 1824 } 1825 #endif 1826 } 1827 1828 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1829 #ifdef DISTRHO_PLUGIN_EXTRA_IO 1830 const uint32_t numInputs = fNumInputs; 1831 #else 1832 constexpr const uint32_t numInputs = DISTRHO_PLUGIN_NUM_INPUTS; 1833 #endif 1834 const float* inputs[numInputs]; 1835 #else 1836 constexpr const float** inputs = nullptr; 1837 #endif 1838 1839 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1840 #ifdef DISTRHO_PLUGIN_EXTRA_IO 1841 const uint32_t numOutputs = fNumOutputs; 1842 #else 1843 constexpr const uint32_t numOutputs = DISTRHO_PLUGIN_NUM_OUTPUTS; 1844 #endif 1845 float* outputs[numOutputs]; 1846 #else 1847 constexpr float** outputs = nullptr; 1848 #endif 1849 1850 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1851 if (fInputConnectionUnit != nullptr) 1852 { 1853 AudioUnitRenderActionFlags ioActionFlags = 0; 1854 const OSStatus err = AudioUnitRender(fInputConnectionUnit, 1855 &ioActionFlags, 1856 inTimeStamp, 1857 fInputConnectionBus, 1858 inFramesToProcess, 1859 fAudioBufferList); 1860 1861 if (err != noErr) 1862 { 1863 setLastRenderError(err); 1864 return err; 1865 } 1866 1867 for (uint16_t i = 0; i < numInputs; ++i) 1868 inputs[i] = static_cast<const float*>(fAudioBufferList->mBuffers[i].mData); 1869 1870 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1871 for (uint16_t i = 0; i < numOutputs; ++i) 1872 { 1873 if (ioData->mBuffers[i].mData == nullptr) 1874 ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; 1875 1876 outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData); 1877 } 1878 #endif 1879 } 1880 else if (fInputRenderCallback.inputProc != nullptr) 1881 { 1882 bool adjustDataByteSize, usingHostBuffer = true; 1883 UInt32 prevDataByteSize; 1884 1885 for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) 1886 { 1887 if (ioData->mBuffers[i].mData == nullptr) 1888 { 1889 usingHostBuffer = false; 1890 ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; 1891 } 1892 } 1893 1894 if (! usingHostBuffer) 1895 { 1896 prevDataByteSize = fAudioBufferList->mBuffers[0].mDataByteSize; 1897 adjustDataByteSize = prevDataByteSize != sizeof(float) * inFramesToProcess; 1898 1899 if (adjustDataByteSize) 1900 { 1901 for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) 1902 fAudioBufferList->mBuffers[i].mDataByteSize = sizeof(float) * inFramesToProcess; 1903 } 1904 } 1905 else 1906 { 1907 adjustDataByteSize = false; 1908 } 1909 1910 AudioUnitRenderActionFlags rActionFlags = 0; 1911 AudioBufferList* const rData = usingHostBuffer ? ioData : fAudioBufferList; 1912 const OSStatus err = fInputRenderCallback.inputProc(fInputRenderCallback.inputProcRefCon, 1913 &rActionFlags, 1914 inTimeStamp, 1915 inBusNumber, 1916 inFramesToProcess, 1917 rData); 1918 1919 if (err != noErr) 1920 { 1921 if (adjustDataByteSize) 1922 { 1923 for (uint16_t i = 0; i < ioData->mNumberBuffers; ++i) 1924 fAudioBufferList->mBuffers[i].mDataByteSize = prevDataByteSize; 1925 } 1926 1927 setLastRenderError(err); 1928 return err; 1929 } 1930 1931 if (usingHostBuffer) 1932 { 1933 for (uint16_t i = 0; i < numInputs; ++i) 1934 inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData); 1935 1936 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1937 for (uint16_t i = 0; i < numOutputs; ++i) 1938 outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData); 1939 #endif 1940 1941 } 1942 else 1943 { 1944 for (uint16_t i = 0; i < numInputs; ++i) 1945 inputs[i] = static_cast<const float*>(fAudioBufferList->mBuffers[i].mData); 1946 1947 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1948 for (uint16_t i = 0; i < numOutputs; ++i) 1949 outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData); 1950 #endif 1951 } 1952 } 1953 else 1954 #endif // DISTRHO_PLUGIN_NUM_INPUTS != 0 1955 { 1956 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 1957 for (uint16_t i = 0; i < numInputs; ++i) 1958 { 1959 if (ioData->mBuffers[i].mData == nullptr) 1960 { 1961 ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; 1962 std::memset(ioData->mBuffers[i].mData, 0, sizeof(float) * inFramesToProcess); 1963 } 1964 1965 inputs[i] = static_cast<const float*>(ioData->mBuffers[i].mData); 1966 } 1967 #endif 1968 1969 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 1970 for (uint16_t i = 0; i < numOutputs; ++i) 1971 { 1972 if (ioData->mBuffers[i].mData == nullptr) 1973 ioData->mBuffers[i].mData = fAudioBufferList->mBuffers[i].mData; 1974 1975 outputs[i] = static_cast<float*>(ioData->mBuffers[i].mData); 1976 } 1977 #endif 1978 } 1979 1980 if (fUsingRenderListeners) 1981 { 1982 AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PreRender; 1983 notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData); 1984 } 1985 1986 run(inputs, outputs, inFramesToProcess, inTimeStamp); 1987 1988 if (fUsingRenderListeners) 1989 { 1990 AudioUnitRenderActionFlags ioActionFlags = actionFlags | kAudioUnitRenderAction_PostRender; 1991 notifyRenderListeners(&ioActionFlags, inTimeStamp, inBusNumber, inFramesToProcess, ioData); 1992 } 1993 1994 return noErr; 1995 } 1996 1997 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 1998 OSStatus auMIDIEvent(const UInt32 inStatus, 1999 const UInt32 inData1, 2000 const UInt32 inData2, 2001 const UInt32 inOffsetSampleFrame) 2002 { 2003 if (fMidiEventCount >= kMaxMidiEvents) 2004 return noErr; 2005 2006 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); 2007 midiEvent.frame = inOffsetSampleFrame; 2008 midiEvent.data[0] = inStatus; 2009 midiEvent.data[1] = inData1; 2010 midiEvent.data[2] = inData2; 2011 2012 // TODO 2013 switch (inStatus) 2014 { 2015 case 0x80: 2016 case 0x90: 2017 case 0xA0: 2018 case 0xB0: 2019 case 0xD0: 2020 case 0xE0: 2021 midiEvent.size = 3; 2022 break; 2023 case 0xC0: 2024 midiEvent.size = 2; 2025 break; 2026 default: 2027 midiEvent.size = 1; 2028 break; 2029 } 2030 2031 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 2032 // handle case of plugin having no working audio, simulate audio-side processing 2033 run(nullptr, nullptr, std::max(1u, inOffsetSampleFrame), nullptr); 2034 #endif 2035 2036 return noErr; 2037 } 2038 2039 OSStatus auSysEx(const UInt8* const inData, const UInt32 inLength) 2040 { 2041 if (fMidiEventCount >= kMaxMidiEvents) 2042 return noErr; 2043 2044 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); 2045 midiEvent.frame = fMidiEventCount != 1 ? fMidiEvents[fMidiEventCount - 1].frame : 0; 2046 midiEvent.size = inLength; 2047 2048 // FIXME who owns inData ?? 2049 if (inLength > MidiEvent::kDataSize) 2050 { 2051 std::memset(midiEvent.data, 0, MidiEvent::kDataSize); 2052 midiEvent.dataExt = inData; 2053 } 2054 else 2055 { 2056 std::memcpy(midiEvent.data, inData, inLength); 2057 } 2058 2059 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS == 0 2060 // handle case of plugin having no working audio, simulate audio-side processing 2061 run(nullptr, nullptr, 1, nullptr); 2062 #endif 2063 2064 return noErr; 2065 } 2066 #endif 2067 2068 // ---------------------------------------------------------------------------------------------------------------- 2069 2070 private: 2071 PluginExporter fPlugin; 2072 2073 // AU component 2074 const AudioUnit fComponent; 2075 2076 // AUv2 related fields 2077 OSStatus fLastRenderError; 2078 PropertyListeners fPropertyListeners; 2079 RenderListeners fRenderListeners; 2080 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 2081 UInt32 fInputConnectionBus; 2082 AudioUnit fInputConnectionUnit; 2083 AURenderCallbackStruct fInputRenderCallback; 2084 Float64 fSampleRateForInput; 2085 #ifdef DISTRHO_PLUGIN_EXTRA_IO 2086 uint32_t fNumInputs; 2087 #endif 2088 #endif 2089 #if DISTRHO_PLUGIN_NUM_OUTPUTS != 0 2090 Float64 fSampleRateForOutput; 2091 #ifdef DISTRHO_PLUGIN_EXTRA_IO 2092 uint32_t fNumOutputs; 2093 #endif 2094 #endif 2095 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 2096 AudioBufferList* fAudioBufferList; 2097 #endif 2098 bool fUsingRenderListeners; 2099 2100 // Caching 2101 const uint32_t fParameterCount; 2102 float* fLastParameterValues; 2103 uint32_t fBypassParameterIndex; 2104 2105 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 2106 uint32_t fMidiEventCount; 2107 MidiEvent fMidiEvents[kMaxMidiEvents]; 2108 SmallStackRingBuffer fNotesRingBuffer; 2109 #endif 2110 2111 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 2112 AUMIDIOutputCallbackStruct fMidiOutput; 2113 d_MIDIPacketList fMidiOutputPackets; 2114 #endif 2115 2116 #if DISTRHO_PLUGIN_WANT_PROGRAMS 2117 int32_t fCurrentProgram; 2118 uint32_t fLastFactoryProgram; 2119 uint32_t fProgramCount; 2120 AUPreset* fFactoryPresetsData; 2121 #endif 2122 AUPreset fUserPresetData; 2123 2124 #if DISTRHO_PLUGIN_WANT_STATE 2125 const uint32_t fStateCount; 2126 StringMap fStateMap; 2127 #endif 2128 2129 #if DISTRHO_PLUGIN_WANT_TIMEPOS 2130 HostCallbackInfo fHostCallbackInfo; 2131 TimePosition fTimePosition; 2132 #endif 2133 2134 // ---------------------------------------------------------------------------------------------------------------- 2135 2136 void notifyPropertyListeners(const AudioUnitPropertyID prop, const AudioUnitScope scope, const AudioUnitElement elem) 2137 { 2138 for (PropertyListeners::iterator it = fPropertyListeners.begin(); it != fPropertyListeners.end(); ++it) 2139 { 2140 const PropertyListener& pl(*it); 2141 2142 if (pl.prop == prop) 2143 pl.proc(pl.userData, fComponent, prop, scope, elem); 2144 } 2145 } 2146 2147 void notifyRenderListeners(AudioUnitRenderActionFlags* const ioActionFlags, 2148 const AudioTimeStamp* const inTimeStamp, 2149 const UInt32 inBusNumber, 2150 const UInt32 inNumberFrames, 2151 AudioBufferList* const ioData) 2152 { 2153 for (RenderListeners::iterator it = fRenderListeners.begin(); it != fRenderListeners.end(); ++it) 2154 { 2155 const RenderListener& rl(*it); 2156 2157 rl.proc(rl.userData, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); 2158 } 2159 } 2160 2161 // ---------------------------------------------------------------------------------------------------------------- 2162 2163 void run(const float** inputs, float** outputs, const uint32_t frames, const AudioTimeStamp* const inTimeStamp) 2164 { 2165 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 2166 if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) 2167 { 2168 uint8_t midiData[3]; 2169 const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount - 1].frame : 0; 2170 2171 while (fNotesRingBuffer.isDataAvailableForReading()) 2172 { 2173 if (! fNotesRingBuffer.readCustomData(midiData, 3)) 2174 break; 2175 2176 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); 2177 midiEvent.frame = frame; 2178 midiEvent.size = 3; 2179 std::memcpy(midiEvent.data, midiData, 3); 2180 2181 if (fMidiEventCount == kMaxMidiEvents) 2182 break; 2183 } 2184 } 2185 #endif 2186 2187 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 2188 fMidiOutputPackets.numPackets = 0; 2189 #endif 2190 2191 #if DISTRHO_PLUGIN_WANT_TIMEPOS 2192 if (fHostCallbackInfo.beatAndTempoProc != nullptr || 2193 fHostCallbackInfo.musicalTimeLocationProc != nullptr || 2194 fHostCallbackInfo.transportStateProc != nullptr) 2195 { 2196 // reused values so we can check null and return value together 2197 Boolean b1 = false; 2198 Float32 f1 = 4.f; // initial value for beats per bar 2199 Float64 g1 = 0.0; 2200 Float64 g2 = 0.0; 2201 UInt32 u1 = 0; 2202 2203 if (fHostCallbackInfo.musicalTimeLocationProc != nullptr 2204 && fHostCallbackInfo.musicalTimeLocationProc(fHostCallbackInfo.hostUserData, 2205 nullptr, &f1, &u1, nullptr) == noErr) 2206 { 2207 fTimePosition.bbt.beatsPerBar = f1; 2208 fTimePosition.bbt.beatType = u1; 2209 } 2210 else 2211 { 2212 fTimePosition.bbt.beatsPerBar = 4.f; 2213 fTimePosition.bbt.beatType = 4.f; 2214 } 2215 2216 if (fHostCallbackInfo.beatAndTempoProc != nullptr 2217 && fHostCallbackInfo.beatAndTempoProc(fHostCallbackInfo.hostUserData, &g1, &g2) == noErr) 2218 { 2219 const double beat = static_cast<int32_t>(g1); 2220 2221 fTimePosition.bbt.valid = true; 2222 fTimePosition.bbt.bar = static_cast<int32_t>(beat / f1) + 1; 2223 fTimePosition.bbt.beat = static_cast<int32_t>(std::fmod(beat, f1)) + 1; 2224 fTimePosition.bbt.tick = std::fmod(g1, 1.0) * 1920.0; 2225 fTimePosition.bbt.beatsPerMinute = g2; 2226 } 2227 else 2228 { 2229 fTimePosition.bbt.valid = false; 2230 fTimePosition.bbt.bar = 1; 2231 fTimePosition.bbt.beat = 1; 2232 fTimePosition.bbt.tick = 0.0; 2233 fTimePosition.bbt.beatsPerMinute = 120.0; 2234 } 2235 2236 if (fHostCallbackInfo.transportStateProc != nullptr 2237 && fHostCallbackInfo.transportStateProc(fHostCallbackInfo.hostUserData, 2238 &b1, nullptr, &g1, nullptr, nullptr, nullptr) == noErr) 2239 { 2240 fTimePosition.playing = b1; 2241 fTimePosition.frame = static_cast<int64_t>(g1); 2242 } 2243 else 2244 { 2245 fTimePosition.playing = false; 2246 fTimePosition.frame = 0; 2247 } 2248 2249 fTimePosition.bbt.barStartTick = kDefaultTicksPerBeat * 2250 fTimePosition.bbt.beatsPerBar * 2251 (fTimePosition.bbt.bar - 1); 2252 2253 fPlugin.setTimePosition(fTimePosition); 2254 } 2255 #endif 2256 2257 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 2258 fPlugin.run(inputs, outputs, frames, fMidiEvents, fMidiEventCount); 2259 fMidiEventCount = 0; 2260 #else 2261 fPlugin.run(inputs, outputs, frames); 2262 #endif 2263 2264 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 2265 if (fMidiOutputPackets.numPackets != 0 && fMidiOutput.midiOutputCallback != nullptr) 2266 { 2267 fMidiOutput.midiOutputCallback(fMidiOutput.userData, 2268 inTimeStamp, 2269 0, 2270 reinterpret_cast<const ::MIDIPacketList*>(&fMidiOutputPackets)); 2271 } 2272 #else 2273 // unused 2274 (void)inTimeStamp; 2275 #endif 2276 2277 float value; 2278 AudioUnitEvent event; 2279 std::memset(&event, 0, sizeof(event)); 2280 event.mEventType = kAudioUnitEvent_ParameterValueChange; 2281 event.mArgument.mParameter.mAudioUnit = fComponent; 2282 event.mArgument.mParameter.mScope = kAudioUnitScope_Global; 2283 2284 for (uint32_t i=0; i<fParameterCount; ++i) 2285 { 2286 if (fPlugin.isParameterOutputOrTrigger(i)) 2287 { 2288 value = fPlugin.getParameterValue(i); 2289 2290 if (d_isEqual(fLastParameterValues[i], value)) 2291 continue; 2292 2293 fLastParameterValues[i] = value; 2294 2295 // TODO flag param only, notify listeners later on bg thread (sem_post etc) 2296 event.mArgument.mParameter.mParameterID = i; 2297 AUEventListenerNotify(NULL, NULL, &event); 2298 notifyPropertyListeners('DPFp', kAudioUnitScope_Global, i); 2299 } 2300 } 2301 } 2302 2303 void setLastRenderError(const OSStatus err) 2304 { 2305 if (fLastRenderError != noErr) 2306 return; 2307 2308 fLastRenderError = err; 2309 notifyPropertyListeners(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0); 2310 } 2311 2312 // ---------------------------------------------------------------------------------------------------------------- 2313 2314 #if DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS != 0 2315 bool reallocAudioBufferList(const bool alloc) 2316 { 2317 if (fAudioBufferList != nullptr) 2318 { 2319 for (uint16_t i = 0; i < fAudioBufferList->mNumberBuffers; ++i) 2320 delete[] static_cast<float*>(fAudioBufferList->mBuffers[i].mData); 2321 } 2322 2323 #ifdef DISTRHO_PLUGIN_EXTRA_IO 2324 #if DISTRHO_PLUGIN_NUM_INPUTS != 0 && DISTRHO_PLUGIN_NUM_OUTPUTS != 0 2325 const uint16_t numBuffers = std::max(fNumInputs, fNumOutputs); 2326 #elif DISTRHO_PLUGIN_NUM_INPUTS != 0 2327 const uint16_t numBuffers = fNumInputs; 2328 #else 2329 const uint16_t numBuffers = fNumOutputs; 2330 #endif 2331 #else 2332 constexpr const uint16_t numBuffers = d_max(DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS); 2333 #endif 2334 const uint32_t bufferSize = fPlugin.getBufferSize(); 2335 2336 if (! alloc) 2337 { 2338 std::free(fAudioBufferList); 2339 fAudioBufferList = nullptr; 2340 return true; 2341 } 2342 2343 if (AudioBufferList* const abl = static_cast<AudioBufferList*>( 2344 std::realloc(fAudioBufferList, sizeof(uint32_t) + sizeof(AudioBuffer) * numBuffers))) 2345 { 2346 abl->mNumberBuffers = numBuffers; 2347 2348 for (uint16_t i = 0; i < numBuffers; ++i) 2349 { 2350 abl->mBuffers[i].mNumberChannels = 1; 2351 abl->mBuffers[i].mData = new float[bufferSize]; 2352 abl->mBuffers[i].mDataByteSize = sizeof(float) * bufferSize; 2353 } 2354 2355 fAudioBufferList = abl; 2356 return true; 2357 } 2358 2359 std::free(fAudioBufferList); 2360 fAudioBufferList = nullptr; 2361 return false; 2362 } 2363 #endif 2364 2365 // ---------------------------------------------------------------------------------------------------------------- 2366 2367 CFMutableDictionaryRef retrieveClassInfo() 2368 { 2369 CFMutableDictionaryRef clsInfo = CFDictionaryCreateMutable(nullptr, 2370 0, 2371 &kCFTypeDictionaryKeyCallBacks, 2372 &kCFTypeDictionaryValueCallBacks); 2373 SInt32 value; 2374 2375 value = 0; 2376 if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) 2377 { 2378 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetVersionKey), num); 2379 CFRelease(num); 2380 } 2381 2382 value = kType; 2383 if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) 2384 { 2385 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetTypeKey), num); 2386 CFRelease(num); 2387 } 2388 2389 value = kSubType; 2390 if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) 2391 { 2392 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetSubtypeKey), num); 2393 CFRelease(num); 2394 } 2395 2396 value = kManufacturer; 2397 if (const CFNumberRef num = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value)) 2398 { 2399 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetManufacturerKey), num); 2400 CFRelease(num); 2401 } 2402 2403 #if DISTRHO_PLUGIN_WANT_PROGRAMS 2404 if (fCurrentProgram >= 0) 2405 { 2406 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fFactoryPresetsData[fCurrentProgram].presetName); 2407 } 2408 else 2409 #endif 2410 { 2411 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetNameKey), fUserPresetData.presetName); 2412 } 2413 2414 if (const CFMutableDictionaryRef data = CFDictionaryCreateMutable(nullptr, 2415 0, 2416 &kCFTypeDictionaryKeyCallBacks, 2417 &kCFTypeDictionaryValueCallBacks)) 2418 { 2419 #if DISTRHO_PLUGIN_WANT_PROGRAMS 2420 const SInt32 program = fCurrentProgram; 2421 if (const CFNumberRef programRef = CFNumberCreate(nullptr, kCFNumberSInt32Type, &program)) 2422 { 2423 CFDictionarySetValue(data, CFSTR("program"), programRef); 2424 CFRelease(programRef); 2425 } 2426 #endif 2427 2428 #if DISTRHO_PLUGIN_WANT_FULL_STATE 2429 // Update current state 2430 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) 2431 { 2432 const String& key(cit->first); 2433 fStateMap[key] = fPlugin.getStateValue(key); 2434 } 2435 #endif 2436 2437 #if DISTRHO_PLUGIN_WANT_STATE 2438 if (const CFMutableArrayRef statesRef = CFArrayCreateMutable(nullptr, 2439 fStateCount, 2440 &kCFTypeArrayCallBacks)) 2441 { 2442 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) 2443 { 2444 const String& key(cit->first); 2445 const String& value(cit->second); 2446 2447 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS && ! DISTRHO_PLUGIN_HAS_UI 2448 bool wantStateKey = true; 2449 2450 for (uint32_t i=0; i<fStateCount; ++i) 2451 { 2452 if (fPlugin.getStateKey(i) == key) 2453 { 2454 if (fPlugin.getStateHints(i) & kStateIsOnlyForUI) 2455 wantStateKey = false; 2456 2457 break; 2458 } 2459 } 2460 2461 if (! wantStateKey) 2462 continue; 2463 #endif 2464 2465 CFStringRef keyRef = CFStringCreateWithCString(nullptr, key, kCFStringEncodingASCII); 2466 CFStringRef valueRef = CFStringCreateWithCString(nullptr, value, kCFStringEncodingUTF8); 2467 2468 if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr, 2469 reinterpret_cast<const void**>(&keyRef), 2470 reinterpret_cast<const void**>(&valueRef), 2471 1, 2472 &kCFTypeDictionaryKeyCallBacks, 2473 &kCFTypeDictionaryValueCallBacks)) 2474 { 2475 CFArrayAppendValue(statesRef, dictRef); 2476 CFRelease(dictRef); 2477 } 2478 2479 CFRelease(keyRef); 2480 CFRelease(valueRef); 2481 } 2482 2483 CFDictionarySetValue(data, CFSTR("states"), statesRef); 2484 CFRelease(statesRef); 2485 } 2486 #endif 2487 2488 if (const CFMutableArrayRef paramsRef = CFArrayCreateMutable(nullptr, 2489 fParameterCount, 2490 &kCFTypeArrayCallBacks)) 2491 { 2492 for (uint32_t i=0; i<fParameterCount; ++i) 2493 { 2494 if (fPlugin.isParameterOutputOrTrigger(i)) 2495 continue; 2496 2497 const float value = fPlugin.getParameterValue(i); 2498 2499 CFStringRef keyRef = CFStringCreateWithCString(nullptr, 2500 fPlugin.getParameterSymbol(i), 2501 kCFStringEncodingASCII); 2502 CFNumberRef valueRef = CFNumberCreate(nullptr, kCFNumberFloat32Type, &value); 2503 2504 if (CFDictionaryRef dictRef = CFDictionaryCreate(nullptr, 2505 reinterpret_cast<const void**>(&keyRef), 2506 reinterpret_cast<const void**>(&valueRef), 2507 1, 2508 &kCFTypeDictionaryKeyCallBacks, 2509 &kCFTypeDictionaryValueCallBacks)) 2510 { 2511 CFArrayAppendValue(paramsRef, dictRef); 2512 CFRelease(dictRef); 2513 } 2514 2515 CFRelease(keyRef); 2516 CFRelease(valueRef); 2517 } 2518 2519 CFDictionarySetValue(data, CFSTR("params"), paramsRef); 2520 CFRelease(paramsRef); 2521 } 2522 2523 CFDictionarySetValue(clsInfo, CFSTR(kAUPresetDataKey), data); 2524 CFRelease(data); 2525 } 2526 2527 return clsInfo; 2528 } 2529 2530 void restoreClassInfo(const CFDictionaryRef clsInfo) 2531 { 2532 CFDictionaryRef data = nullptr; 2533 DISTRHO_SAFE_ASSERT_RETURN(CFDictionaryGetValueIfPresent(clsInfo, 2534 CFSTR(kAUPresetDataKey), 2535 reinterpret_cast<const void**>(&data)),); 2536 DISTRHO_SAFE_ASSERT_RETURN(CFGetTypeID(data) == CFDictionaryGetTypeID(),); 2537 2538 #if DISTRHO_PLUGIN_WANT_PROGRAMS 2539 CFNumberRef programRef = nullptr; 2540 if (CFDictionaryGetValueIfPresent(data, CFSTR("program"), reinterpret_cast<const void**>(&programRef)) 2541 && CFGetTypeID(programRef) == CFNumberGetTypeID()) 2542 { 2543 SInt32 program = -1; 2544 if (CFNumberGetValue(programRef, kCFNumberSInt32Type, &program)) 2545 { 2546 fCurrentProgram = program; 2547 2548 if (program >= 0) 2549 { 2550 fLastFactoryProgram = program; 2551 fPlugin.loadProgram(fLastFactoryProgram); 2552 notifyPropertyListeners('DPFo', kAudioUnitScope_Global, 0); 2553 } 2554 2555 notifyPropertyListeners(kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0); 2556 } 2557 } 2558 #endif 2559 2560 #if DISTRHO_PLUGIN_WANT_STATE 2561 CFArrayRef statesRef = nullptr; 2562 if (CFDictionaryGetValueIfPresent(data, CFSTR("states"), reinterpret_cast<const void**>(&statesRef)) 2563 && CFGetTypeID(statesRef) == CFArrayGetTypeID()) 2564 { 2565 const CFIndex numStates = CFArrayGetCount(statesRef); 2566 char* key = nullptr; 2567 char* value = nullptr; 2568 CFIndex keyLen = -1; 2569 CFIndex valueLen = -1; 2570 2571 for (CFIndex i=0; i<numStates; ++i) 2572 { 2573 const CFDictionaryRef state = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(statesRef, i)); 2574 DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(state) == CFDictionaryGetTypeID()); 2575 DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(state) == 1); 2576 2577 CFStringRef keyRef = nullptr; 2578 CFStringRef valueRef = nullptr; 2579 CFDictionaryGetKeysAndValues(state, 2580 reinterpret_cast<const void**>(&keyRef), 2581 reinterpret_cast<const void**>(&valueRef)); 2582 DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID()); 2583 DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFStringGetTypeID()); 2584 2585 const CFIndex keyRefLen = CFStringGetLength(keyRef); 2586 if (keyLen < keyRefLen) 2587 { 2588 keyLen = keyRefLen; 2589 key = static_cast<char*>(std::realloc(key, keyLen + 1)); 2590 } 2591 DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, key, keyLen + 1, kCFStringEncodingASCII)); 2592 2593 if (! fPlugin.wantStateKey(key)) 2594 continue; 2595 2596 const CFIndex valueRefLen = CFStringGetLength(valueRef); 2597 if (valueLen < valueRefLen) 2598 { 2599 valueLen = valueRefLen; 2600 value = static_cast<char*>(std::realloc(value, valueLen + 1)); 2601 } 2602 DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(valueRef, value, valueLen + 1, kCFStringEncodingUTF8)); 2603 2604 const String dkey(key); 2605 fStateMap[dkey] = value; 2606 fPlugin.setState(key, value); 2607 2608 for (uint32_t j=0; j<fStateCount; ++j) 2609 { 2610 if (fPlugin.getStateKey(j) == key) 2611 { 2612 if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) 2613 notifyPropertyListeners('DPFs', kAudioUnitScope_Global, j); 2614 2615 break; 2616 } 2617 } 2618 } 2619 2620 std::free(key); 2621 std::free(value); 2622 } 2623 #endif 2624 2625 CFArrayRef paramsRef = nullptr; 2626 if (CFDictionaryGetValueIfPresent(data, CFSTR("params"), reinterpret_cast<const void**>(¶msRef)) 2627 && CFGetTypeID(paramsRef) == CFArrayGetTypeID()) 2628 { 2629 const CFIndex numParams = CFArrayGetCount(paramsRef); 2630 char* symbol = nullptr; 2631 CFIndex symbolLen = -1; 2632 2633 for (CFIndex i=0; i<numParams; ++i) 2634 { 2635 const CFDictionaryRef param = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(paramsRef, i)); 2636 DISTRHO_SAFE_ASSERT_BREAK(CFGetTypeID(param) == CFDictionaryGetTypeID()); 2637 DISTRHO_SAFE_ASSERT_BREAK(CFDictionaryGetCount(param) == 1); 2638 2639 CFStringRef keyRef = nullptr; 2640 CFNumberRef valueRef = nullptr; 2641 CFDictionaryGetKeysAndValues(param, 2642 reinterpret_cast<const void**>(&keyRef), 2643 reinterpret_cast<const void**>(&valueRef)); 2644 DISTRHO_SAFE_ASSERT_BREAK(keyRef != nullptr && CFGetTypeID(keyRef) == CFStringGetTypeID()); 2645 DISTRHO_SAFE_ASSERT_BREAK(valueRef != nullptr && CFGetTypeID(valueRef) == CFNumberGetTypeID()); 2646 2647 float value = 0.f; 2648 DISTRHO_SAFE_ASSERT_BREAK(CFNumberGetValue(valueRef, kCFNumberFloat32Type, &value)); 2649 2650 const CFIndex keyRefLen = CFStringGetLength(keyRef); 2651 if (symbolLen < keyRefLen) 2652 { 2653 symbolLen = keyRefLen; 2654 symbol = static_cast<char*>(std::realloc(symbol, symbolLen + 1)); 2655 } 2656 DISTRHO_SAFE_ASSERT_BREAK(CFStringGetCString(keyRef, symbol, symbolLen + 1, kCFStringEncodingASCII)); 2657 2658 for (uint32_t j=0; j<fParameterCount; ++j) 2659 { 2660 if (fPlugin.isParameterOutputOrTrigger(j)) 2661 continue; 2662 if (fPlugin.getParameterSymbol(j) != symbol) 2663 continue; 2664 2665 fLastParameterValues[j] = value; 2666 fPlugin.setParameterValue(j, value); 2667 notifyPropertyListeners('DPFp', kAudioUnitScope_Global, j); 2668 2669 if (fBypassParameterIndex == j) 2670 notifyPropertyListeners(kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0); 2671 2672 break; 2673 } 2674 } 2675 2676 std::free(symbol); 2677 } 2678 } 2679 2680 // ---------------------------------------------------------------------------------------------------------------- 2681 // DPF callbacks 2682 2683 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 2684 bool writeMidi(const MidiEvent& midiEvent) 2685 { 2686 DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fMidiOutput.midiOutputCallback != nullptr, false); 2687 2688 if (midiEvent.size > sizeof(MIDIPacket::data)) 2689 return true; 2690 if (fMidiOutputPackets.numPackets == kMaxMidiEvents) 2691 return false; 2692 2693 const uint8_t* const midiData = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data; 2694 MIDIPacket& packet(fMidiOutputPackets.packets[fMidiOutputPackets.numPackets++]); 2695 packet.timeStamp = midiEvent.frame; 2696 packet.length = midiEvent.size; 2697 std::memcpy(packet.data, midiData, midiEvent.size); 2698 return true; 2699 } 2700 2701 static bool writeMidiCallback(void* const ptr, const MidiEvent& midiEvent) 2702 { 2703 return static_cast<PluginAU*>(ptr)->writeMidi(midiEvent); 2704 } 2705 #endif 2706 2707 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 2708 bool requestParameterValueChange(const uint32_t index, const float value) 2709 { 2710 AudioUnitEvent event; 2711 std::memset(&event, 0, sizeof(event)); 2712 event.mEventType = kAudioUnitEvent_ParameterValueChange; 2713 event.mArgument.mParameter.mAudioUnit = fComponent; 2714 event.mArgument.mParameter.mParameterID = index; 2715 event.mArgument.mParameter.mScope = kAudioUnitScope_Global; 2716 2717 fLastParameterValues[index] = value; 2718 AUEventListenerNotify(NULL, NULL, &event); 2719 notifyPropertyListeners('DPFp', kAudioUnitScope_Global, index); 2720 return true; 2721 } 2722 2723 static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) 2724 { 2725 return static_cast<PluginAU*>(ptr)->requestParameterValueChange(index, value); 2726 } 2727 #endif 2728 2729 #if DISTRHO_PLUGIN_WANT_STATE 2730 bool updateState(const char* const key, const char* const newValue) 2731 { 2732 fPlugin.setState(key, newValue); 2733 2734 for (uint32_t i=0; i<fStateCount; ++i) 2735 { 2736 if (fPlugin.getStateKey(i) == key) 2737 { 2738 const String dkey(key); 2739 fStateMap[dkey] = newValue; 2740 2741 if ((fPlugin.getStateHints(i) & kStateIsOnlyForDSP) == 0x0) 2742 notifyPropertyListeners('DPFs', kAudioUnitScope_Global, i); 2743 2744 return true; 2745 } 2746 } 2747 2748 d_stderr("Failed to find plugin state with key \"%s\"", key); 2749 return false; 2750 } 2751 2752 static bool updateStateValueCallback(void* const ptr, const char* const key, const char* const newValue) 2753 { 2754 return static_cast<PluginAU*>(ptr)->updateState(key, newValue); 2755 } 2756 #endif 2757 2758 DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginAU) 2759 }; 2760 2761 // -------------------------------------------------------------------------------------------------------------------- 2762 2763 struct AudioComponentPlugInInstance { 2764 AudioComponentPlugInInterface acpi; 2765 PluginAU* plugin; 2766 2767 AudioComponentPlugInInstance() noexcept 2768 : acpi(), 2769 plugin(nullptr) 2770 { 2771 std::memset(&acpi, 0, sizeof(acpi)); 2772 acpi.Open = Open; 2773 acpi.Close = Close; 2774 acpi.Lookup = Lookup; 2775 acpi.reserved = nullptr; 2776 } 2777 2778 ~AudioComponentPlugInInstance() 2779 { 2780 delete plugin; 2781 } 2782 2783 static OSStatus Open(void* const self, const AudioUnit component) 2784 { 2785 static_cast<AudioComponentPlugInInstance*>(self)->plugin = new PluginAU(component); 2786 return noErr; 2787 } 2788 2789 static OSStatus Close(void* const self) 2790 { 2791 delete static_cast<AudioComponentPlugInInstance*>(self); 2792 return noErr; 2793 } 2794 2795 static AudioComponentMethod Lookup(const SInt16 selector) 2796 { 2797 d_debug("AudioComponentPlugInInstance::Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); 2798 2799 switch (selector) 2800 { 2801 case kAudioUnitInitializeSelect: 2802 return reinterpret_cast<AudioComponentMethod>(Initialize); 2803 case kAudioUnitUninitializeSelect: 2804 return reinterpret_cast<AudioComponentMethod>(Uninitialize); 2805 case kAudioUnitGetPropertyInfoSelect: 2806 return reinterpret_cast<AudioComponentMethod>(GetPropertyInfo); 2807 case kAudioUnitGetPropertySelect: 2808 return reinterpret_cast<AudioComponentMethod>(GetProperty); 2809 case kAudioUnitSetPropertySelect: 2810 return reinterpret_cast<AudioComponentMethod>(SetProperty); 2811 case kAudioUnitAddPropertyListenerSelect: 2812 return reinterpret_cast<AudioComponentMethod>(AddPropertyListener); 2813 case kAudioUnitRemovePropertyListenerSelect: 2814 return reinterpret_cast<AudioComponentMethod>(RemovePropertyListener); 2815 case kAudioUnitRemovePropertyListenerWithUserDataSelect: 2816 return reinterpret_cast<AudioComponentMethod>(RemovePropertyListenerWithUserData); 2817 case kAudioUnitAddRenderNotifySelect: 2818 return reinterpret_cast<AudioComponentMethod>(AddRenderNotify); 2819 case kAudioUnitRemoveRenderNotifySelect: 2820 return reinterpret_cast<AudioComponentMethod>(RemoveRenderNotify); 2821 case kAudioUnitGetParameterSelect: 2822 return reinterpret_cast<AudioComponentMethod>(GetParameter); 2823 case kAudioUnitSetParameterSelect: 2824 return reinterpret_cast<AudioComponentMethod>(SetParameter); 2825 case kAudioUnitScheduleParametersSelect: 2826 return reinterpret_cast<AudioComponentMethod>(ScheduleParameters); 2827 case kAudioUnitRenderSelect: 2828 return reinterpret_cast<AudioComponentMethod>(Render); 2829 /* 2830 case kAudioUnitComplexRenderSelect: 2831 return reinterpret_cast<AudioComponentMethod>(ComplexRender); 2832 */ 2833 case kAudioUnitResetSelect: 2834 return reinterpret_cast<AudioComponentMethod>(Reset); 2835 /* 2836 case kAudioUnitProcessSelect: 2837 return reinterpret_cast<AudioComponentMethod>(Process); 2838 case kAudioUnitProcessMultipleSelect: 2839 return reinterpret_cast<AudioComponentMethod>(ProcessMultiple); 2840 */ 2841 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 2842 case kMusicDeviceMIDIEventSelect: 2843 return reinterpret_cast<AudioComponentMethod>(MIDIEvent); 2844 case kMusicDeviceSysExSelect: 2845 return reinterpret_cast<AudioComponentMethod>(SysEx); 2846 #else 2847 case kMusicDeviceMIDIEventSelect: 2848 case kMusicDeviceSysExSelect: 2849 return nullptr; 2850 #endif 2851 } 2852 2853 d_stdout("TODO Lookup(%3d:%s)", selector, AudioUnitSelector2Str(selector)); 2854 return nullptr; 2855 } 2856 2857 static OSStatus Initialize(AudioComponentPlugInInstance* const self) 2858 { 2859 d_debug("AudioComponentPlugInInstance::Initialize(%p)", self); 2860 return self->plugin->auInitialize(); 2861 } 2862 2863 static OSStatus Uninitialize(AudioComponentPlugInInstance* const self) 2864 { 2865 d_debug("AudioComponentPlugInInstance::Uninitialize(%p)", self); 2866 return self->plugin->auUninitialize(); 2867 } 2868 2869 static OSStatus GetPropertyInfo(AudioComponentPlugInInstance* const self, 2870 const AudioUnitPropertyID inProp, 2871 const AudioUnitScope inScope, 2872 const AudioUnitElement inElement, 2873 UInt32* const outDataSize, 2874 Boolean* const outWritable) 2875 { 2876 d_debug("AudioComponentPlugInInstance::GetPropertyInfo(%p, %d:%x:%s, %d:%s, %d, ...)", 2877 self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); 2878 2879 UInt32 dataSize = 0; 2880 Boolean writable = false; 2881 const OSStatus res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, dataSize, writable); 2882 2883 if (outDataSize != nullptr) 2884 *outDataSize = dataSize; 2885 2886 if (outWritable != nullptr) 2887 *outWritable = writable; 2888 2889 return res; 2890 } 2891 2892 static OSStatus GetProperty(AudioComponentPlugInInstance* const self, 2893 const AudioUnitPropertyID inProp, 2894 const AudioUnitScope inScope, 2895 const AudioUnitElement inElement, 2896 void* const outData, 2897 UInt32* const ioDataSize) 2898 { 2899 d_debug("AudioComponentPlugInInstance::GetProperty(%p, %d:%x:%s, %d:%s, %d, ...)", 2900 self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement); 2901 DISTRHO_SAFE_ASSERT_RETURN(ioDataSize != nullptr, kAudio_ParamError); 2902 2903 Boolean writable; 2904 UInt32 outDataSize = 0; 2905 OSStatus res; 2906 2907 if (outData == nullptr) 2908 { 2909 res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable); 2910 *ioDataSize = outDataSize; 2911 return res; 2912 } 2913 2914 const UInt32 inDataSize = *ioDataSize; 2915 if (inDataSize == 0) 2916 return kAudio_ParamError; 2917 2918 res = self->plugin->auGetPropertyInfo(inProp, inScope, inElement, outDataSize, writable); 2919 2920 if (res != noErr) 2921 return res; 2922 2923 void* outBuffer; 2924 uint8_t* tmpBuffer; 2925 if (inDataSize < outDataSize) 2926 { 2927 tmpBuffer = new uint8_t[outDataSize]; 2928 outBuffer = tmpBuffer; 2929 } 2930 else 2931 { 2932 tmpBuffer = nullptr; 2933 outBuffer = outData; 2934 } 2935 2936 res = self->plugin->auGetProperty(inProp, inScope, inElement, outBuffer); 2937 2938 if (res != noErr) 2939 { 2940 *ioDataSize = 0; 2941 return res; 2942 } 2943 2944 if (tmpBuffer != nullptr) 2945 { 2946 memcpy(outData, tmpBuffer, inDataSize); 2947 delete[] tmpBuffer; 2948 } 2949 else 2950 { 2951 *ioDataSize = outDataSize; 2952 } 2953 2954 return noErr; 2955 } 2956 2957 static OSStatus SetProperty(AudioComponentPlugInInstance* const self, 2958 const AudioUnitPropertyID inProp, 2959 const AudioUnitScope inScope, 2960 const AudioUnitElement inElement, 2961 const void* const inData, 2962 const UInt32 inDataSize) 2963 { 2964 d_debug("AudioComponentPlugInInstance::SetProperty(%p, %d:%x:%s, %d:%s, %d, %p, %u)", 2965 self, inProp, inProp, AudioUnitPropertyID2Str(inProp), inScope, AudioUnitScope2Str(inScope), inElement, inData, inDataSize); 2966 return self->plugin->auSetProperty(inProp, inScope, inElement, inData, inDataSize); 2967 } 2968 2969 static OSStatus AddPropertyListener(AudioComponentPlugInInstance* const self, 2970 const AudioUnitPropertyID prop, 2971 const AudioUnitPropertyListenerProc proc, 2972 void* const userData) 2973 { 2974 d_debug("AudioComponentPlugInInstance::AddPropertyListener(%p, %d:%x:%s, %p, %p)", 2975 self, prop, prop, AudioUnitPropertyID2Str(prop), proc, userData); 2976 return self->plugin->auAddPropertyListener(prop, proc, userData); 2977 } 2978 2979 static OSStatus RemovePropertyListener(AudioComponentPlugInInstance* const self, 2980 const AudioUnitPropertyID prop, 2981 const AudioUnitPropertyListenerProc proc) 2982 { 2983 d_debug("AudioComponentPlugInInstance::RemovePropertyListener(%p, %d:%x:%s, %p)", 2984 self, prop, prop, AudioUnitPropertyID2Str(prop), proc); 2985 return self->plugin->auRemovePropertyListener(prop, proc); 2986 } 2987 2988 static OSStatus RemovePropertyListenerWithUserData(AudioComponentPlugInInstance* const self, 2989 const AudioUnitPropertyID prop, 2990 const AudioUnitPropertyListenerProc proc, 2991 void* const userData) 2992 { 2993 d_debug("AudioComponentPlugInInstance::RemovePropertyListenerWithUserData(%p, %d:%x:%s, %p, %p)", 2994 self, prop, prop, AudioUnitPropertyID2Str(prop), proc, userData); 2995 return self->plugin->auRemovePropertyListenerWithUserData(prop, proc, userData); 2996 } 2997 2998 static OSStatus AddRenderNotify(AudioComponentPlugInInstance* const self, 2999 const AURenderCallback proc, 3000 void* const userData) 3001 { 3002 d_debug("AudioComponentPlugInInstance::AddRenderNotify(%p, %p, %p)", self, proc, userData); 3003 return self->plugin->auAddRenderNotify(proc, userData); 3004 } 3005 3006 static OSStatus RemoveRenderNotify(AudioComponentPlugInInstance* const self, 3007 const AURenderCallback proc, 3008 void* const userData) 3009 { 3010 d_debug("AudioComponentPlugInInstance::RemoveRenderNotify(%p, %p, %p)", self, proc, userData); 3011 return self->plugin->auRemoveRenderNotify(proc, userData); 3012 } 3013 3014 static OSStatus GetParameter(AudioComponentPlugInInstance* const self, 3015 const AudioUnitParameterID param, 3016 const AudioUnitScope scope, 3017 const AudioUnitElement elem, 3018 AudioUnitParameterValue* const value) 3019 { 3020 d_debug("AudioComponentPlugInInstance::GetParameter(%p, %d, %d:%s, %d, %p)", 3021 self, param, scope, AudioUnitScope2Str(scope), elem, value); 3022 return self->plugin->auGetParameter(param, scope, elem, value); 3023 } 3024 3025 static OSStatus SetParameter(AudioComponentPlugInInstance* const self, 3026 const AudioUnitParameterID param, 3027 const AudioUnitScope scope, 3028 const AudioUnitElement elem, 3029 const AudioUnitParameterValue value, 3030 const UInt32 bufferOffset) 3031 { 3032 d_debug("AudioComponentPlugInInstance::SetParameter(%p, %d %d:%s, %d, %f, %u)", 3033 self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset); 3034 return self->plugin->auSetParameter(param, scope, elem, value, bufferOffset); 3035 } 3036 3037 static OSStatus ScheduleParameters(AudioComponentPlugInInstance* const self, 3038 const AudioUnitParameterEvent* const events, 3039 const UInt32 numEvents) 3040 { 3041 d_debug("AudioComponentPlugInInstance::ScheduleParameters(%p, %p, %u)", self, events, numEvents); 3042 return self->plugin->auScheduleParameters(events, numEvents); 3043 } 3044 3045 static OSStatus Reset(AudioComponentPlugInInstance* const self, 3046 const AudioUnitScope scope, 3047 const AudioUnitElement elem) 3048 { 3049 d_debug("AudioComponentPlugInInstance::Reset(%p, %d:%s, %d)", self, scope, AudioUnitScope2Str(scope), elem); 3050 return self->plugin->auReset(scope, elem); 3051 } 3052 3053 static OSStatus Render(AudioComponentPlugInInstance* const self, 3054 AudioUnitRenderActionFlags* ioActionFlags, 3055 const AudioTimeStamp* const inTimeStamp, 3056 const UInt32 inOutputBusNumber, 3057 const UInt32 inNumberFrames, 3058 AudioBufferList* const ioData) 3059 { 3060 const AudioUnitRenderActionFlags actionFlags = ioActionFlags != nullptr ? *ioActionFlags : 0; 3061 3062 if ((actionFlags & kAudioUnitRenderAction_DoNotCheckRenderArgs) == 0x0) 3063 { 3064 DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); 3065 DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); 3066 } 3067 3068 return self->plugin->auRender(actionFlags, inTimeStamp, inOutputBusNumber, inNumberFrames, ioData); 3069 } 3070 3071 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 3072 static OSStatus MIDIEvent(AudioComponentPlugInInstance* const self, 3073 const UInt32 inStatus, 3074 const UInt32 inData1, 3075 const UInt32 inData2, 3076 const UInt32 inOffsetSampleFrame) 3077 { 3078 return self->plugin->auMIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame); 3079 } 3080 3081 static OSStatus SysEx(AudioComponentPlugInInstance* const self, const UInt8* const inData, const UInt32 inLength) 3082 { 3083 return self->plugin->auSysEx(inData, inLength); 3084 } 3085 #endif 3086 3087 DISTRHO_DECLARE_NON_COPYABLE(AudioComponentPlugInInstance) 3088 }; 3089 3090 #if 0 3091 static OSStatus FastDispatchGetParameter(void* const self, 3092 const AudioUnitParameterID param, 3093 const AudioUnitScope scope, 3094 const AudioUnitElement elem, 3095 Float32* const value) 3096 { 3097 d_debug("FastDispatchGetParameter(%p, %d, %d:%s, %d, %p)", 3098 self, param, scope, AudioUnitScope2Str(scope), elem, value); 3099 return static_cast<AudioComponentPlugInInstance*>(self)->plugin->auGetParameter(param, scope, elem, value); 3100 } 3101 3102 static OSStatus FastDispatchSetParameter(void* const self, 3103 const AudioUnitParameterID param, 3104 const AudioUnitScope scope, 3105 const AudioUnitElement elem, 3106 const Float32 value, 3107 const UInt32 bufferOffset) 3108 { 3109 d_debug("FastDispatchSetParameter(%p, %d %d:%s, %d, %f, %u)", 3110 self, param, scope, AudioUnitScope2Str(scope), elem, value, bufferOffset); 3111 return static_cast<AudioComponentPlugInInstance*>(self)->plugin->auSetParameter(param, scope, elem, value, bufferOffset); 3112 } 3113 3114 static OSStatus FastDispatchRender(void* const self, 3115 AudioUnitRenderActionFlags* const ioActionFlags, 3116 const AudioTimeStamp* const inTimeStamp, 3117 const UInt32 inBusNumber, 3118 const UInt32 inNumberFrames, 3119 AudioBufferList* const ioData) 3120 { 3121 DISTRHO_SAFE_ASSERT_RETURN(inTimeStamp != nullptr, kAudio_ParamError); 3122 DISTRHO_SAFE_ASSERT_RETURN(ioData != nullptr, kAudio_ParamError); 3123 3124 return static_cast<AudioComponentPlugInInstance*>(self)->plugin->auRender(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, *ioData); 3125 } 3126 #endif 3127 3128 // -------------------------------------------------------------------------------------------------------------------- 3129 3130 END_NAMESPACE_DISTRHO 3131 3132 DISTRHO_PLUGIN_EXPORT 3133 void* PluginAUFactory(const AudioComponentDescription* const desc) 3134 { 3135 USE_NAMESPACE_DISTRHO 3136 3137 DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentType == kType, desc->componentType, kType, nullptr); 3138 DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentSubType == kSubType, desc->componentSubType, kSubType, nullptr); 3139 DISTRHO_SAFE_ASSERT_UINT2_RETURN(desc->componentManufacturer == kManufacturer, desc->componentManufacturer, kManufacturer, nullptr); 3140 3141 if (d_nextBufferSize == 0) 3142 d_nextBufferSize = 1156; 3143 3144 if (d_isZero(d_nextSampleRate)) 3145 d_nextSampleRate = 44100.0; 3146 3147 if (d_nextBundlePath == nullptr) 3148 { 3149 static String bundlePath; 3150 3151 String tmpPath(getBinaryFilename()); 3152 tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); 3153 tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); 3154 3155 if (tmpPath.endsWith(DISTRHO_OS_SEP_STR "Contents")) 3156 { 3157 tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); 3158 bundlePath = tmpPath; 3159 } 3160 else 3161 { 3162 bundlePath = "error"; 3163 } 3164 3165 d_nextBundlePath = bundlePath.buffer(); 3166 } 3167 3168 d_nextCanRequestParameterValueChanges = true; 3169 3170 return new AudioComponentPlugInInstance(); 3171 } 3172 3173 // --------------------------------------------------------------------------------------------------------------------