A few more tweaks for AU hosting, WIP
[carla.git] / source / backend / plugin / CarlaPluginAU.cpp
blob8660883bc4a5a60b29ca849e27d7f0bb4a2c3583
1 // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
2 // SPDX-License-Identifier: GPL-2.0-or-later
4 #include "CarlaPluginInternal.hpp"
5 #include "CarlaEngine.hpp"
7 #ifdef CARLA_OS_MAC
8 # include "CarlaBackendUtils.hpp"
9 # include "CarlaPluginUI.hpp"
10 # include "CarlaMacUtils.hpp"
11 # include <AudioToolbox/AudioUnit.h>
12 # include <Foundation/Foundation.h>
13 #endif
15 CARLA_BACKEND_START_NAMESPACE
17 // --------------------------------------------------------------------------------------------------------------------
19 #ifdef CARLA_OS_MAC
20 typedef AudioComponentPlugInInterface* (*FactoryFn)(const AudioComponentDescription*);
21 typedef OSStatus (*InitializeFn)(void*);
22 typedef OSStatus (*UninitializeFn)(void*);
23 typedef OSStatus (*GetPropertyInfoFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, UInt32*, Boolean*);
24 typedef OSStatus (*GetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, void*, UInt32*);
25 typedef OSStatus (*SetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, const void*, UInt32);
26 typedef OSStatus (*GetParameterFn)(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, AudioUnitParameterValue*);
27 typedef OSStatus (*SetParameterFn)(void*, AudioUnitParameterID, AudioUnitScope, AudioUnitElement, AudioUnitParameterValue, UInt32);
28 typedef OSStatus (*ScheduleParametersFn)(void*, const AudioUnitParameterEvent*, UInt32);
29 typedef OSStatus (*ResetFn)(void*, AudioUnitScope, AudioUnitElement);
30 typedef OSStatus (*RenderFn)(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, UInt32, UInt32, AudioBufferList*);
31 typedef OSStatus (*MIDIEventFn)(void*, UInt32, UInt32, UInt32, UInt32);
33 static constexpr FourCharCode getFourCharCodeFromString(const char str[4])
35 return (str[0] << 24) + (str[1] << 16) + (str[2] << 8) + str[3];
38 class CarlaPluginAU : public CarlaPlugin,
39 private CarlaPluginUI::Callback
41 public:
42 CarlaPluginAU(CarlaEngine* const engine, const uint id)
43 : CarlaPlugin(engine, id),
44 fInterface(nullptr),
45 fAudioBufferData(nullptr)
47 carla_stdout("CarlaPluginAU::CarlaPluginAU(%p, %i)", engine, id);
50 ~CarlaPluginAU() override
52 carla_stdout("CarlaPluginAU::~CarlaPluginAU()");
54 // close UI
55 showCustomUI(false);
57 pData->singleMutex.lock();
58 pData->masterMutex.lock();
60 if (pData->client != nullptr && pData->client->isActive())
61 pData->client->deactivate(true);
63 if (pData->active)
65 deactivate();
66 pData->active = false;
69 if (fInterface != nullptr)
71 fInterface->Close(fInterface);
72 fInterface = nullptr;
75 if (fAudioBufferData != nullptr)
77 std::free(fAudioBufferData);
78 fAudioBufferData = nullptr;
81 // if (fLastChunk != nullptr)
82 // {
83 // std::free(fLastChunk);
84 // fLastChunk = nullptr;
85 // }
87 clearBuffers();
90 // -------------------------------------------------------------------
91 // Information (base)
93 PluginType getType() const noexcept override
95 return PLUGIN_AU;
98 PluginCategory getCategory() const noexcept override
100 // TODO
101 return PLUGIN_CATEGORY_NONE;
104 uint32_t getLatencyInFrames() const noexcept override
106 // TODO
107 return 0;
110 // -------------------------------------------------------------------
111 // Information (count)
113 // -------------------------------------------------------------------
114 // Information (current data)
116 // -------------------------------------------------------------------
117 // Information (per-plugin data)
119 uint getOptionsAvailable() const noexcept override
121 // TODO
122 return 0x0;
125 float getParameterValue(const uint32_t parameterId) const noexcept override
127 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, 0.f);
128 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, 0.f);
130 const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
131 AudioUnitParameterValue value = 0.f;
132 if (fFunctions.getParameter(fInterface, paramId, kAudioUnitScope_Global, 0, &value) == noErr)
133 return value;
135 return 0.f;
138 bool getLabel(char* const strBuf) const noexcept override
140 std::strncpy(strBuf, fLabel.buffer(), STR_MAX);
141 return true;
144 bool getMaker(char* const strBuf) const noexcept override
146 std::strncpy(strBuf, fMaker.buffer(), STR_MAX);
147 return true;
150 bool getRealName(char* const strBuf) const noexcept override
152 std::strncpy(strBuf, fName.buffer(), STR_MAX);
153 return true;
156 bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
158 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr, false);
159 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count, false);
161 const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
162 AudioUnitParameterInfo info = {};
163 UInt32 outDataSize = sizeof(AudioUnitParameterInfo);
165 if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramId, &info, &outDataSize) == noErr)
167 if (info.flags & kAudioUnitParameterFlag_HasCFNameString)
169 *strBuf = '\0';
170 if (! CFStringGetCString(info.cfNameString, strBuf, std::min<int>(STR_MAX, CFStringGetLength(info.cfNameString) + 1), kCFStringEncodingUTF8))
172 carla_stdout("CFStringGetCString fail '%s'", info.name);
173 std::strncpy(strBuf, info.name, STR_MAX);
175 else
177 carla_stdout("CFStringGetCString ok '%s' '%s'", info.name, strBuf);
178 std::strncpy(strBuf, info.name, STR_MAX);
181 if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
182 CFRelease(info.cfNameString);
184 return true;
187 carla_stdout("CFStringGetCString not used '%s'", info.name);
188 std::strncpy(strBuf, info.name, STR_MAX);
189 return true;
192 carla_safe_assert("fFunctions.getProperty(...)", __FILE__, __LINE__);
193 return CarlaPlugin::getParameterName(parameterId, strBuf);
196 // -------------------------------------------------------------------
197 // Set data (plugin-specific stuff)
199 void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
201 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
202 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
204 const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
205 const float fixedValue = pData->param.getFixedValue(parameterId, value);
207 fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, 0);
209 CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback);
212 void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override
214 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
215 CARLA_SAFE_ASSERT_RETURN(parameterId < pData->param.count,);
217 const AudioUnitParameterID paramId = pData->param.data[parameterId].rindex;
218 const float fixedValue = pData->param.getFixedValue(parameterId, value);
220 // TODO use scheduled events
221 fFunctions.setParameter(fInterface, paramId, kAudioUnitScope_Global, 0, value, frameOffset);
223 CarlaPlugin::setParameterValueRT(parameterId, fixedValue, frameOffset, sendCallbackLater);
226 // ----------------------------------------------------------------------------------------------------------------
227 // Plugin state
229 void reload() override
231 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
232 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
233 carla_debug("CarlaPluginAU::reload() - start");
235 const EngineProcessMode processMode = pData->engine->getProccessMode();
237 // Safely disable plugin for reload
238 const ScopedDisabler sd(this);
240 if (pData->active)
241 deactivate();
243 clearBuffers();
245 uint32_t audioIns, audioOuts, parametersIns, parametersOuts;
246 audioIns = audioOuts = parametersIns = parametersOuts = 0;
248 bool needsCtrlIn, needsCtrlOut, hasMidiIn, hasMidiOut;
249 needsCtrlIn = needsCtrlOut = hasMidiIn = hasMidiOut = false;
251 CarlaString portName;
252 const uint portNameSize = pData->engine->getMaxPortNameSize();
254 UInt32 outDataSize;
255 Boolean outWritable = false;
257 // audio ports
258 outDataSize = 0;
259 if (fFunctions.getPropertyInfo(fInterface,
260 kAudioUnitProperty_SupportedNumChannels,
261 kAudioUnitScope_Global,
262 0, &outDataSize, &outWritable) == noErr
263 && outDataSize != 0
264 && outDataSize % sizeof(AUChannelInfo) == 0)
266 const uint32_t numChannels = outDataSize / sizeof(AUChannelInfo);
267 AUChannelInfo* const channelInfo = new AUChannelInfo[numChannels];
269 carla_stdout("kAudioUnitProperty_SupportedNumChannels returns %u configs", numChannels);
271 if (fFunctions.getProperty(fInterface,
272 kAudioUnitProperty_SupportedNumChannels,
273 kAudioUnitScope_Global,
274 0, channelInfo, &outDataSize) == noErr
275 && outDataSize == numChannels * sizeof(AUChannelInfo))
277 AUChannelInfo* highestInfo = &channelInfo[0];
279 carla_stdout("kAudioUnitProperty_SupportedNumChannels returns {%d,%d}... config",
280 channelInfo[0].inChannels,
281 channelInfo[0].outChannels);
283 for (uint32_t i=0; i<numChannels; ++i)
285 if (channelInfo[i].inChannels < 0)
286 channelInfo[i].inChannels = 2;
287 if (channelInfo[i].outChannels < 0)
288 channelInfo[i].outChannels = 2;
290 if (channelInfo[i].inChannels > highestInfo->inChannels
291 && channelInfo[i].outChannels > highestInfo->outChannels)
293 highestInfo = &channelInfo[i];
297 audioIns = std::min<int16_t>(64, highestInfo->inChannels);
298 audioOuts = std::min<int16_t>(64, highestInfo->outChannels);
300 else
302 carla_stdout("kAudioUnitProperty_SupportedNumChannels failed");
305 delete[] channelInfo;
307 else
309 outDataSize = 0;
310 if (fFunctions.getPropertyInfo(fInterface,
311 kAudioUnitProperty_ElementCount,
312 kAudioUnitScope_Input,
313 0, &outDataSize, &outWritable) == noErr
314 && outDataSize == sizeof(UInt32))
316 UInt32 count = 0;
317 if (fFunctions.getProperty(fInterface,
318 kAudioUnitProperty_ElementCount,
319 kAudioUnitScope_Input,
320 0, &count, &outDataSize) == noErr
321 && outDataSize == sizeof(UInt32) && count != 0)
323 AudioStreamBasicDescription desc;
324 std::memset(&desc, 0, sizeof(desc));
325 outDataSize = sizeof(AudioStreamBasicDescription);
327 if (fFunctions.getProperty(fInterface,
328 kAudioUnitProperty_StreamFormat,
329 kAudioUnitScope_Input,
330 0, &desc, &outDataSize) == noErr)
331 audioIns = std::min<uint32_t>(64, desc.mChannelsPerFrame);
335 outDataSize = 0;
336 if (fFunctions.getPropertyInfo(fInterface,
337 kAudioUnitProperty_ElementCount,
338 kAudioUnitScope_Output,
339 0, &outDataSize, &outWritable) == noErr
340 && outDataSize == sizeof(UInt32))
342 UInt32 count = 0;
343 if (fFunctions.getProperty(fInterface,
344 kAudioUnitProperty_ElementCount,
345 kAudioUnitScope_Output,
346 0, &count, &outDataSize) == noErr
347 && outDataSize == sizeof(UInt32) && count != 0)
349 AudioStreamBasicDescription desc;
350 std::memset(&desc, 0, sizeof(desc));
351 outDataSize = sizeof(AudioStreamBasicDescription);
353 if (fFunctions.getProperty(fInterface,
354 kAudioUnitProperty_StreamFormat,
355 kAudioUnitScope_Output,
356 0, &desc, &outDataSize) == noErr)
357 audioOuts = std::min<uint32_t>(64, desc.mChannelsPerFrame);
362 if (audioIns > 0)
364 pData->audioIn.createNew(audioIns);
367 if (audioOuts > 0)
369 pData->audioOut.createNew(audioOuts);
370 needsCtrlIn = true;
373 std::free(fAudioBufferData);
375 if (const uint32_t numBuffers = std::max(audioIns, audioOuts))
377 fAudioBufferData = static_cast<AudioBufferList*>(std::malloc(sizeof(uint32_t) + sizeof(AudioBuffer) * numBuffers));
378 fAudioBufferData->mNumberBuffers = numBuffers;
380 for (uint32_t i = 0; i < numBuffers; ++i)
381 fAudioBufferData->mBuffers[i].mNumberChannels = 1;
383 else
385 fAudioBufferData = static_cast<AudioBufferList*>(std::malloc(sizeof(uint32_t)));
386 fAudioBufferData->mNumberBuffers = 0;
389 // Audio Ins
390 for (uint32_t i=0; i < audioIns; ++i)
392 portName.clear();
394 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
396 portName = pData->name;
397 portName += ":";
400 if (audioIns > 1)
402 portName += "input_";
403 portName += CarlaString(i + 1);
405 else
406 portName += "input";
408 portName.truncate(portNameSize);
410 pData->audioIn.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, true, i);
411 pData->audioIn.ports[i].rindex = i;
414 // Audio Outs
415 for (uint32_t i=0; i < audioOuts; ++i)
417 portName.clear();
419 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
421 portName = pData->name;
422 portName += ":";
425 if (audioOuts > 1)
427 portName += "output_";
428 portName += CarlaString(i + 1);
430 else
431 portName += "output";
433 portName.truncate(portNameSize);
435 pData->audioOut.ports[i].port = (CarlaEngineAudioPort*)pData->client->addPort(kEnginePortTypeAudio, portName, false, i);
436 pData->audioOut.ports[i].rindex = i;
439 // parameters
440 outDataSize = 0;
441 if (fFunctions.getPropertyInfo(fInterface,
442 kAudioUnitProperty_ParameterList,
443 kAudioUnitScope_Global,
444 0, &outDataSize, &outWritable) == noErr
445 && outDataSize != 0
446 && outDataSize % sizeof(AudioUnitParameterID) == 0)
448 const uint32_t numParams = outDataSize / sizeof(AudioUnitParameterID);
449 AudioUnitParameterID* const paramIds = new AudioUnitParameterID[numParams];
451 if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, paramIds, &outDataSize) == noErr && outDataSize == numParams * sizeof(AudioUnitParameterID))
453 pData->param.createNew(numParams, false);
455 AudioUnitParameterInfo info;
456 float min, max, def, step, stepSmall, stepLarge;
458 for (uint32_t i=0; i<numParams; ++i)
460 carla_zeroStruct(info);
462 outDataSize = 0;
463 if (fFunctions.getPropertyInfo(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &outDataSize, &outWritable) != noErr)
464 break;
465 if (outDataSize != sizeof(AudioUnitParameterInfo))
466 break;
467 if (fFunctions.getProperty(fInterface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &info, &outDataSize) != noErr)
468 break;
470 if (info.flags & kAudioUnitParameterFlag_CFNameRelease)
471 CFRelease(info.cfNameString);
473 pData->param.data[i].index = static_cast<int32_t>(i);
474 pData->param.data[i].rindex = static_cast<int32_t>(paramIds[i]);
476 if (info.flags & kAudioUnitParameterFlag_IsWritable)
478 pData->param.data[i].type = PARAMETER_INPUT;
479 needsCtrlIn = true;
481 else if (info.flags & (kAudioUnitParameterFlag_IsReadable|kAudioUnitParameterFlag_MeterReadOnly))
483 pData->param.data[i].type = PARAMETER_OUTPUT;
484 needsCtrlOut = true;
486 else
488 pData->param.data[i].type = PARAMETER_UNKNOWN;
489 continue;
492 min = info.minValue;
493 max = info.maxValue;
494 def = info.defaultValue;
496 if (min > max)
497 max = min;
499 if (carla_isEqual(min, max))
501 carla_stderr2("WARNING - Broken plugin parameter '%s': max == min", info.name);
502 max = min + 0.1f;
505 if (def < min)
506 def = min;
507 else if (def > max)
508 def = max;
510 pData->param.data[i].hints |= PARAMETER_IS_ENABLED;
512 if ((info.flags & kAudioUnitParameterFlag_NonRealTime) == 0)
514 pData->param.data[i].hints |= PARAMETER_IS_AUTOMATABLE;
515 pData->param.data[i].hints |= PARAMETER_CAN_BE_CV_CONTROLLED;
518 if (info.unit == kAudioUnitParameterUnit_Boolean)
520 step = max - min;
521 stepSmall = step;
522 stepLarge = step;
523 pData->param.data[i].hints |= PARAMETER_IS_BOOLEAN;
525 else if (info.unit == kAudioUnitParameterUnit_Indexed)
527 step = 1.0f;
528 stepSmall = 1.0f;
529 stepLarge = 10.0f;
530 pData->param.data[i].hints |= PARAMETER_IS_INTEGER;
532 else
534 float range = max - min;
535 step = range/100.0f;
536 stepSmall = range/1000.0f;
537 stepLarge = range/10.0f;
540 pData->param.ranges[i].min = min;
541 pData->param.ranges[i].max = max;
542 pData->param.ranges[i].def = def;
543 pData->param.ranges[i].step = step;
544 pData->param.ranges[i].stepSmall = stepSmall;
545 pData->param.ranges[i].stepLarge = stepLarge;
549 delete[] paramIds;
552 if (needsCtrlIn || hasMidiIn)
554 portName.clear();
556 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
558 portName = pData->name;
559 portName += ":";
562 portName += "events-in";
563 portName.truncate(portNameSize);
565 pData->event.portIn = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, true, 0);
566 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
567 pData->event.cvSourcePorts = pData->client->createCVSourcePorts();
568 #endif
571 if (needsCtrlOut || hasMidiOut)
573 portName.clear();
575 if (processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT)
577 portName = pData->name;
578 portName += ":";
581 portName += "events-out";
582 portName.truncate(portNameSize);
584 pData->event.portOut = (CarlaEngineEventPort*)pData->client->addPort(kEnginePortTypeEvent, portName, false, 0);
587 // plugin hints
588 pData->hints = 0x0;
590 if (audioOuts > 0 && (audioIns == audioOuts || audioIns == 1))
591 pData->hints |= PLUGIN_CAN_DRYWET;
593 if (audioOuts > 0)
594 pData->hints |= PLUGIN_CAN_VOLUME;
596 if (audioOuts >= 2 && audioOuts % 2 == 0)
597 pData->hints |= PLUGIN_CAN_BALANCE;
599 // extra plugin hints
600 pData->extraHints = 0x0;
602 bufferSizeChanged(pData->engine->getBufferSize());
603 reloadPrograms(true);
605 if (pData->active)
606 activate();
608 carla_debug("CarlaPluginAU::reload() - end");
611 // -------------------------------------------------------------------
612 // Plugin processing
614 void activate() noexcept override
616 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
618 AudioStreamBasicDescription streamFormat = {
619 .mFormatID = kAudioFormatLinearPCM,
620 .mBitsPerChannel = 32,
621 .mBytesPerFrame = sizeof(float),
622 .mBytesPerPacket = sizeof(float),
623 .mFramesPerPacket = 1,
624 .mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved,
625 .mChannelsPerFrame = 0,
626 .mSampleRate = pData->engine->getSampleRate(),
629 if (pData->audioIn.count != 0)
631 streamFormat.mChannelsPerFrame = pData->audioIn.count;
632 CARLA_SAFE_ASSERT_RETURN(fFunctions.setProperty(fInterface,
633 kAudioUnitProperty_StreamFormat,
634 kAudioUnitScope_Input,
635 0, &streamFormat, sizeof(streamFormat)) == noErr,);
638 if (pData->audioOut.count != 0)
640 streamFormat.mChannelsPerFrame = pData->audioOut.count;
641 CARLA_SAFE_ASSERT_RETURN(fFunctions.setProperty(fInterface,
642 kAudioUnitProperty_StreamFormat,
643 kAudioUnitScope_Output,
644 0, &streamFormat, sizeof(streamFormat)) == noErr,);
647 fFunctions.initialize(fInterface);
650 void deactivate() noexcept override
652 CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
654 fFunctions.uninitialize(fInterface);
657 void process(const float* const* const audioIn,
658 float** const audioOut,
659 const float* const* const,
660 float** const,
661 const uint32_t frames) override
663 // ------------------------------------------------------------------------------------------------------------
664 // Check if active
666 if (! pData->active)
668 // disable any output sound
669 for (uint32_t i=0; i < pData->audioOut.count; ++i)
670 carla_zeroFloats(audioOut[i], frames);
671 return;
674 // ------------------------------------------------------------------------------------------------------------
675 // Check buffers
677 CARLA_SAFE_ASSERT_RETURN(frames > 0,);
679 if (pData->audioIn.count > 0)
681 CARLA_SAFE_ASSERT_RETURN(audioIn != nullptr,);
683 if (pData->audioOut.count > 0)
685 CARLA_SAFE_ASSERT_RETURN(audioOut != nullptr,);
688 // ------------------------------------------------------------------------------------------------------------
689 // Try lock, silence otherwise
691 if (pData->engine->isOffline())
693 pData->singleMutex.lock();
695 else if (! pData->singleMutex.tryLock())
697 for (uint32_t i=0; i < pData->audioOut.count; ++i)
698 carla_zeroFloats(audioOut[i], frames);
699 return;
702 // ------------------------------------------------------------------------------------------------------------
703 // Check if needs reset
705 if (pData->needsReset)
707 // TODO
710 // ------------------------------------------------------------------------------------------------------------
711 // Event Input (main port)
713 if (pData->event.portIn != nullptr)
715 // TODO
718 // ------------------------------------------------------------------------------------------------------------
719 // Plugin processing
721 const EngineTimeInfo timeInfo(pData->engine->getTimeInfo());
723 AudioUnitRenderActionFlags actionFlags = kAudioUnitRenderAction_DoNotCheckRenderArgs;
724 AudioTimeStamp timeStamp = {};
725 timeStamp.mFlags = kAudioTimeStampSampleTimeValid;
726 timeStamp.mSampleTime = timeInfo.frame;
727 const UInt32 inBusNumber = 0;
730 uint32_t i = 0;
731 for (; i < pData->audioOut.count; ++i)
733 fAudioBufferData->mBuffers[i].mData = audioOut[i];
734 fAudioBufferData->mBuffers[i].mDataByteSize = sizeof(float) * frames;
736 if (audioOut[i] != audioIn[i])
737 std::memcpy(audioOut[i], audioIn[i], sizeof(float) * frames);
740 for (; i < pData->audioIn.count; ++i)
742 fAudioBufferData->mBuffers[i].mData = audioOut[i];
743 fAudioBufferData->mBuffers[i].mDataByteSize = sizeof(float) * frames;
747 fFunctions.render(fInterface, &actionFlags, &timeStamp, inBusNumber, frames, fAudioBufferData);
749 // ------------------------------------------------------------------------------------------------------------
751 pData->singleMutex.unlock();
753 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
754 // ------------------------------------------------------------------------------------------------------------
755 // Control Output
757 // TODO
758 #endif
760 // ------------------------------------------------------------------------------------------------------------
761 // Events/MIDI Output
763 // TODO
766 // ----------------------------------------------------------------------------------------------------------------
768 protected:
769 void handlePluginUIClosed() override
771 carla_stdout("CarlaPluginAU::handlePluginUIClosed()");
773 // TODO
776 void handlePluginUIResized(const uint width, const uint height) override
778 // TODO
781 // -------------------------------------------------------------------
783 public:
784 bool init(const CarlaPluginPtr plugin,
785 const char* const filename,
786 const char* const label,
787 const char* const name,
788 const uint options)
790 CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
792 // ---------------------------------------------------------------
793 // first checks
795 if (pData->client != nullptr)
797 pData->engine->setLastError("Plugin client is already registered");
798 return false;
801 if (filename == nullptr || filename[0] == '\0')
803 pData->engine->setLastError("null filename");
804 return false;
807 // ---------------------------------------------------------------
808 // load bundle information
810 if (! fBundleLoader.load(filename))
812 pData->engine->setLastError("Failed to load AU bundle executable");
813 return false;
816 const CFTypeRef componentsRef = CFBundleGetValueForInfoDictionaryKey(fBundleLoader.getRef(), CFSTR("AudioComponents"));
818 if (componentsRef == nullptr || CFGetTypeID(componentsRef) != CFArrayGetTypeID())
820 pData->engine->setLastError("Not an AU component");
821 return false;
824 // ---------------------------------------------------------------
825 // find binary matching requested label
827 CFStringRef componentName;
828 AudioComponentDescription desc = {};
829 FactoryFn factoryFn;
831 const CFArrayRef components = static_cast<CFArrayRef>(componentsRef);
833 for (uint32_t c = 0, count = CFArrayGetCount(components); c < count; ++c)
835 // reset
836 desc.componentType = 0;
838 const CFTypeRef componentRef = CFArrayGetValueAtIndex(components, c);
839 CARLA_SAFE_ASSERT_CONTINUE(componentRef != nullptr);
840 CARLA_SAFE_ASSERT_CONTINUE(CFGetTypeID(componentRef) == CFDictionaryGetTypeID());
842 const CFDictionaryRef component = static_cast<CFDictionaryRef>(componentRef);
844 componentName = nullptr;
845 CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("name"), (const void **)&componentName));
847 CFStringRef componentFactoryFunction = nullptr;
848 CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("factoryFunction"), (const void **)&componentFactoryFunction));
850 CFStringRef componentType = nullptr;
851 CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("type"), (const void **)&componentType));
852 CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentType) == 4);
854 CFStringRef componentSubType = nullptr;
855 CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("subtype"), (const void **)&componentSubType));
856 CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentSubType) == 4);
858 CFStringRef componentManufacturer = nullptr;
859 CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("manufacturer"), (const void **)&componentManufacturer));
860 CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentManufacturer) == 4);
862 factoryFn = fBundleLoader.getSymbol<FactoryFn>(componentFactoryFunction);
863 CARLA_SAFE_ASSERT_CONTINUE(factoryFn != nullptr);
865 char clabel[15] = {};
866 CFStringGetCString(componentType, clabel, 5, kCFStringEncodingASCII);
867 CFStringGetCString(componentSubType, clabel + 5, 5, kCFStringEncodingASCII);
868 CFStringGetCString(componentManufacturer, clabel + 10, 5, kCFStringEncodingASCII);
870 desc.componentType = getFourCharCodeFromString(clabel);
871 desc.componentSubType = getFourCharCodeFromString(clabel + 5);
872 desc.componentManufacturer = getFourCharCodeFromString(clabel + 10);
874 CARLA_SAFE_ASSERT_CONTINUE(desc.componentType != 0);
875 CARLA_SAFE_ASSERT_CONTINUE(desc.componentSubType != 0);
876 CARLA_SAFE_ASSERT_CONTINUE(desc.componentManufacturer != 0);
878 clabel[4] = clabel[9] = ',';
880 if (label == nullptr || label[0] == '\0' || std::strcmp(label, clabel) == 0)
881 break;
884 if (desc.componentType == 0)
886 pData->engine->setLastError("Failed to find request plugin in Component bundle");
887 return false;
890 // ---------------------------------------------------------------
891 // load binary
893 fInterface = factoryFn(&desc);
895 if (fInterface == nullptr)
897 pData->engine->setLastError("Component failed to create new interface");
898 return false;
901 if (! fFunctions.init(fInterface))
903 pData->engine->setLastError("Component does not provide all necessary functions");
904 fInterface = nullptr;
905 return false;
908 if (fInterface->Open(fInterface, (AudioUnit)(void*)0x1) != noErr)
910 pData->engine->setLastError("Component failed to open");
911 fInterface->Close(fInterface);
912 fInterface = nullptr;
913 return false;
916 // ---------------------------------------------------------------
917 // get info
919 const CFIndex componentNameLen = CFStringGetLength(componentName);
920 char* const nameBuffer = new char[componentNameLen + 1];
922 if (CFStringGetCString(componentName, nameBuffer, componentNameLen + 1, kCFStringEncodingUTF8))
924 if (char* const sep = std::strstr(nameBuffer, ": "))
926 sep[0] = sep[1] = '\0';
927 fName = sep + 2;
928 fMaker = nameBuffer;
930 else
932 fName = nameBuffer;
933 fMaker = nameBuffer + componentNameLen;
937 fLabel = label;
938 pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0' ? name : fName.buffer());
939 pData->filename = carla_strdup(filename);
941 delete[] nameBuffer;
943 // ---------------------------------------------------------------
944 // register client
946 pData->client = pData->engine->addClient(plugin);
948 if (pData->client == nullptr || ! pData->client->isOk())
950 pData->engine->setLastError("Failed to register plugin client");
951 return false;
954 // ------------------------------------------------------------------------------------------------------------
955 // init component
958 const UInt32 bufferSize = pData->engine->getBufferSize();
960 if (fFunctions.setProperty(fInterface,
961 kAudioUnitProperty_MaximumFramesPerSlice,
962 kAudioUnitScope_Global,
963 0, &bufferSize, sizeof(bufferSize)) != noErr)
965 pData->engine->setLastError("Failed to set Component maximum frames per slice");
966 return false;
971 const Float64 sampleRate = pData->engine->getSampleRate();
973 // input scope
974 UInt32 outDataSize = 0;
975 Boolean outWritable = false;
976 if (fFunctions.getPropertyInfo(fInterface,
977 kAudioUnitProperty_ElementCount,
978 kAudioUnitScope_Input,
979 0, &outDataSize, &outWritable) == noErr
980 && outDataSize == sizeof(UInt32))
982 UInt32 outData = 0;
983 if (fFunctions.getProperty(fInterface,
984 kAudioUnitProperty_ElementCount,
985 kAudioUnitScope_Input,
986 0, &outData, &outDataSize) == noErr
987 && outData != 0)
989 if (fFunctions.setProperty(fInterface,
990 kAudioUnitProperty_SampleRate,
991 kAudioUnitScope_Input,
992 0, &sampleRate, sizeof(sampleRate)) != noErr)
994 pData->engine->setLastError("Failed to set Component input sample rate");
995 return false;
1000 // output scope
1001 outDataSize = 0;
1002 outWritable = false;
1003 if (fFunctions.getPropertyInfo(fInterface,
1004 kAudioUnitProperty_ElementCount,
1005 kAudioUnitScope_Output,
1006 0, &outDataSize, &outWritable) == noErr
1007 && outDataSize == sizeof(UInt32))
1009 UInt32 outData = 0;
1010 if (fFunctions.getProperty(fInterface,
1011 kAudioUnitProperty_ElementCount,
1012 kAudioUnitScope_Output,
1013 0, &outData, &outDataSize) == noErr
1014 && outData != 0)
1016 if (fFunctions.setProperty(fInterface,
1017 kAudioUnitProperty_SampleRate,
1018 kAudioUnitScope_Output,
1019 0, &sampleRate, sizeof(sampleRate)) != noErr)
1021 pData->engine->setLastError("Failed to set Component output sample rate");
1022 return false;
1028 // ------------------------------------------------------------------------------------------------------------
1029 // set default options
1031 pData->options = PLUGIN_OPTION_FIXED_BUFFERS;
1033 return true;
1036 private:
1037 BundleLoader fBundleLoader;
1038 AudioComponentPlugInInterface* fInterface;
1039 AudioBufferList* fAudioBufferData;
1040 CarlaString fName;
1041 CarlaString fLabel;
1042 CarlaString fMaker;
1044 struct Functions {
1045 InitializeFn initialize;
1046 UninitializeFn uninitialize;
1047 GetPropertyInfoFn getPropertyInfo;
1048 GetPropertyFn getProperty;
1049 SetPropertyFn setProperty;
1050 GetParameterFn getParameter;
1051 SetParameterFn setParameter;
1052 ScheduleParametersFn scheduleParameters;
1053 ResetFn reset;
1054 RenderFn render;
1055 MIDIEventFn midiEvent;
1057 Functions()
1058 : initialize(nullptr),
1059 uninitialize(nullptr),
1060 getPropertyInfo(nullptr),
1061 getProperty(nullptr),
1062 setProperty(nullptr),
1063 getParameter(nullptr),
1064 setParameter(nullptr),
1065 scheduleParameters(nullptr),
1066 reset(nullptr),
1067 render(nullptr),
1068 midiEvent(nullptr) {}
1070 bool init(AudioComponentPlugInInterface* const interface)
1072 initialize = (InitializeFn)interface->Lookup(kAudioUnitInitializeSelect);
1073 uninitialize = (UninitializeFn)interface->Lookup(kAudioUnitUninitializeSelect);
1074 getPropertyInfo = (GetPropertyInfoFn)interface->Lookup(kAudioUnitGetPropertyInfoSelect);
1075 getProperty = (GetPropertyFn)interface->Lookup(kAudioUnitGetPropertySelect);
1076 setProperty = (SetPropertyFn)interface->Lookup(kAudioUnitSetPropertySelect);
1077 getParameter = (GetParameterFn)interface->Lookup(kAudioUnitGetParameterSelect);
1078 setParameter = (SetParameterFn)interface->Lookup(kAudioUnitSetParameterSelect);
1079 scheduleParameters = (ScheduleParametersFn)interface->Lookup(kAudioUnitScheduleParametersSelect);
1080 reset = (ResetFn)interface->Lookup(kAudioUnitResetSelect);
1081 render = (RenderFn)interface->Lookup(kAudioUnitRenderSelect);
1082 midiEvent = (MIDIEventFn)interface->Lookup(kMusicDeviceMIDIEventSelect);
1084 return initialize != nullptr
1085 && uninitialize != nullptr
1086 && getPropertyInfo != nullptr
1087 && getProperty != nullptr
1088 && setProperty != nullptr
1089 && getParameter != nullptr
1090 && setParameter != nullptr
1091 && scheduleParameters != nullptr
1092 && render != nullptr;
1094 } fFunctions;
1096 #endif
1098 // --------------------------------------------------------------------------------------------------------------------
1100 CarlaPluginPtr CarlaPlugin::newAU(const Initializer& init)
1102 carla_stdout("CarlaPlugin::newAU({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
1103 init.engine, init.filename, init.label, init.name, init.uniqueId);
1105 #ifdef CARLA_OS_MAC
1106 std::shared_ptr<CarlaPluginAU> plugin(new CarlaPluginAU(init.engine, init.id));
1108 if (! plugin->init(plugin, init.filename, init.label, init.name, init.options))
1109 return nullptr;
1111 return plugin;
1112 #else
1113 init.engine->setLastError("AU support not available");
1114 return nullptr;
1115 #endif
1118 // --------------------------------------------------------------------------------------------------------------------
1120 CARLA_BACKEND_END_NAMESPACE