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"
8 # include "CarlaBackendUtils.hpp"
9 # include "CarlaPluginUI.hpp"
10 # include "CarlaMacUtils.hpp"
11 # include <AudioToolbox/AudioUnit.h>
12 # include <Foundation/Foundation.h>
15 CARLA_BACKEND_START_NAMESPACE
17 // --------------------------------------------------------------------------------------------------------------------
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
42 CarlaPluginAU(CarlaEngine
* const engine
, const uint id
)
43 : CarlaPlugin(engine
, id
),
45 fAudioBufferData(nullptr)
47 carla_stdout("CarlaPluginAU::CarlaPluginAU(%p, %i)", engine
, id
);
50 ~CarlaPluginAU() override
52 carla_stdout("CarlaPluginAU::~CarlaPluginAU()");
57 pData
->singleMutex
.lock();
58 pData
->masterMutex
.lock();
60 if (pData
->client
!= nullptr && pData
->client
->isActive())
61 pData
->client
->deactivate(true);
66 pData
->active
= false;
69 if (fInterface
!= nullptr)
71 fInterface
->Close(fInterface
);
75 if (fAudioBufferData
!= nullptr)
77 std::free(fAudioBufferData
);
78 fAudioBufferData
= nullptr;
81 // if (fLastChunk != nullptr)
83 // std::free(fLastChunk);
84 // fLastChunk = nullptr;
90 // -------------------------------------------------------------------
93 PluginType
getType() const noexcept override
98 PluginCategory
getCategory() const noexcept override
101 return PLUGIN_CATEGORY_NONE
;
104 uint32_t getLatencyInFrames() const noexcept override
110 // -------------------------------------------------------------------
111 // Information (count)
113 // -------------------------------------------------------------------
114 // Information (current data)
116 // -------------------------------------------------------------------
117 // Information (per-plugin data)
119 uint
getOptionsAvailable() const noexcept override
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
)
138 bool getLabel(char* const strBuf
) const noexcept override
140 std::strncpy(strBuf
, fLabel
.buffer(), STR_MAX
);
144 bool getMaker(char* const strBuf
) const noexcept override
146 std::strncpy(strBuf
, fMaker
.buffer(), STR_MAX
);
150 bool getRealName(char* const strBuf
) const noexcept override
152 std::strncpy(strBuf
, fName
.buffer(), STR_MAX
);
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
)
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
);
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
);
187 carla_stdout("CFStringGetCString not used '%s'", info
.name
);
188 std::strncpy(strBuf
, info
.name
, STR_MAX
);
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 // ----------------------------------------------------------------------------------------------------------------
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);
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();
255 Boolean outWritable
= false;
259 if (fFunctions
.getPropertyInfo(fInterface
,
260 kAudioUnitProperty_SupportedNumChannels
,
261 kAudioUnitScope_Global
,
262 0, &outDataSize
, &outWritable
) == noErr
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
);
302 carla_stdout("kAudioUnitProperty_SupportedNumChannels failed");
305 delete[] channelInfo
;
310 if (fFunctions
.getPropertyInfo(fInterface
,
311 kAudioUnitProperty_ElementCount
,
312 kAudioUnitScope_Input
,
313 0, &outDataSize
, &outWritable
) == noErr
314 && outDataSize
== sizeof(UInt32
))
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
);
336 if (fFunctions
.getPropertyInfo(fInterface
,
337 kAudioUnitProperty_ElementCount
,
338 kAudioUnitScope_Output
,
339 0, &outDataSize
, &outWritable
) == noErr
340 && outDataSize
== sizeof(UInt32
))
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
);
364 pData
->audioIn
.createNew(audioIns
);
369 pData
->audioOut
.createNew(audioOuts
);
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;
385 fAudioBufferData
= static_cast<AudioBufferList
*>(std::malloc(sizeof(uint32_t)));
386 fAudioBufferData
->mNumberBuffers
= 0;
390 for (uint32_t i
=0; i
< audioIns
; ++i
)
394 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
396 portName
= pData
->name
;
402 portName
+= "input_";
403 portName
+= CarlaString(i
+ 1);
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
;
415 for (uint32_t i
=0; i
< audioOuts
; ++i
)
419 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
421 portName
= pData
->name
;
427 portName
+= "output_";
428 portName
+= CarlaString(i
+ 1);
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
;
441 if (fFunctions
.getPropertyInfo(fInterface
,
442 kAudioUnitProperty_ParameterList
,
443 kAudioUnitScope_Global
,
444 0, &outDataSize
, &outWritable
) == noErr
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
);
463 if (fFunctions
.getPropertyInfo(fInterface
, kAudioUnitProperty_ParameterInfo
, kAudioUnitScope_Global
, paramIds
[i
], &outDataSize
, &outWritable
) != noErr
)
465 if (outDataSize
!= sizeof(AudioUnitParameterInfo
))
467 if (fFunctions
.getProperty(fInterface
, kAudioUnitProperty_ParameterInfo
, kAudioUnitScope_Global
, paramIds
[i
], &info
, &outDataSize
) != noErr
)
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
;
481 else if (info
.flags
& (kAudioUnitParameterFlag_IsReadable
|kAudioUnitParameterFlag_MeterReadOnly
))
483 pData
->param
.data
[i
].type
= PARAMETER_OUTPUT
;
488 pData
->param
.data
[i
].type
= PARAMETER_UNKNOWN
;
494 def
= info
.defaultValue
;
499 if (carla_isEqual(min
, max
))
501 carla_stderr2("WARNING - Broken plugin parameter '%s': max == min", info
.name
);
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
)
523 pData
->param
.data
[i
].hints
|= PARAMETER_IS_BOOLEAN
;
525 else if (info
.unit
== kAudioUnitParameterUnit_Indexed
)
530 pData
->param
.data
[i
].hints
|= PARAMETER_IS_INTEGER
;
534 float range
= max
- min
;
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
;
552 if (needsCtrlIn
|| hasMidiIn
)
556 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
558 portName
= pData
->name
;
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();
571 if (needsCtrlOut
|| hasMidiOut
)
575 if (processMode
== ENGINE_PROCESS_MODE_SINGLE_CLIENT
)
577 portName
= pData
->name
;
581 portName
+= "events-out";
582 portName
.truncate(portNameSize
);
584 pData
->event
.portOut
= (CarlaEngineEventPort
*)pData
->client
->addPort(kEnginePortTypeEvent
, portName
, false, 0);
590 if (audioOuts
> 0 && (audioIns
== audioOuts
|| audioIns
== 1))
591 pData
->hints
|= PLUGIN_CAN_DRYWET
;
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);
608 carla_debug("CarlaPluginAU::reload() - end");
611 // -------------------------------------------------------------------
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,
661 const uint32_t frames
) override
663 // ------------------------------------------------------------------------------------------------------------
668 // disable any output sound
669 for (uint32_t i
=0; i
< pData
->audioOut
.count
; ++i
)
670 carla_zeroFloats(audioOut
[i
], frames
);
674 // ------------------------------------------------------------------------------------------------------------
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
);
702 // ------------------------------------------------------------------------------------------------------------
703 // Check if needs reset
705 if (pData
->needsReset
)
710 // ------------------------------------------------------------------------------------------------------------
711 // Event Input (main port)
713 if (pData
->event
.portIn
!= nullptr)
718 // ------------------------------------------------------------------------------------------------------------
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;
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 // ------------------------------------------------------------------------------------------------------------
760 // ------------------------------------------------------------------------------------------------------------
761 // Events/MIDI Output
766 // ----------------------------------------------------------------------------------------------------------------
769 void handlePluginUIClosed() override
771 carla_stdout("CarlaPluginAU::handlePluginUIClosed()");
776 void handlePluginUIResized(const uint width
, const uint height
) override
781 // -------------------------------------------------------------------
784 bool init(const CarlaPluginPtr plugin
,
785 const char* const filename
,
786 const char* const label
,
787 const char* const name
,
790 CARLA_SAFE_ASSERT_RETURN(pData
->engine
!= nullptr, false);
792 // ---------------------------------------------------------------
795 if (pData
->client
!= nullptr)
797 pData
->engine
->setLastError("Plugin client is already registered");
801 if (filename
== nullptr || filename
[0] == '\0')
803 pData
->engine
->setLastError("null filename");
807 // ---------------------------------------------------------------
808 // load bundle information
810 if (! fBundleLoader
.load(filename
))
812 pData
->engine
->setLastError("Failed to load AU bundle executable");
816 const CFTypeRef componentsRef
= CFBundleGetValueForInfoDictionaryKey(fBundleLoader
.getRef(), CFSTR("AudioComponents"));
818 if (componentsRef
== nullptr || CFGetTypeID(componentsRef
) != CFArrayGetTypeID())
820 pData
->engine
->setLastError("Not an AU component");
824 // ---------------------------------------------------------------
825 // find binary matching requested label
827 CFStringRef componentName
;
828 AudioComponentDescription desc
= {};
831 const CFArrayRef components
= static_cast<CFArrayRef
>(componentsRef
);
833 for (uint32_t c
= 0, count
= CFArrayGetCount(components
); c
< count
; ++c
)
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)
884 if (desc
.componentType
== 0)
886 pData
->engine
->setLastError("Failed to find request plugin in Component bundle");
890 // ---------------------------------------------------------------
893 fInterface
= factoryFn(&desc
);
895 if (fInterface
== nullptr)
897 pData
->engine
->setLastError("Component failed to create new interface");
901 if (! fFunctions
.init(fInterface
))
903 pData
->engine
->setLastError("Component does not provide all necessary functions");
904 fInterface
= nullptr;
908 if (fInterface
->Open(fInterface
, (AudioUnit
)(void*)0x1) != noErr
)
910 pData
->engine
->setLastError("Component failed to open");
911 fInterface
->Close(fInterface
);
912 fInterface
= nullptr;
916 // ---------------------------------------------------------------
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';
933 fMaker
= nameBuffer
+ componentNameLen
;
938 pData
->name
= pData
->engine
->getUniquePluginName(name
!= nullptr && name
[0] != '\0' ? name
: fName
.buffer());
939 pData
->filename
= carla_strdup(filename
);
943 // ---------------------------------------------------------------
946 pData
->client
= pData
->engine
->addClient(plugin
);
948 if (pData
->client
== nullptr || ! pData
->client
->isOk())
950 pData
->engine
->setLastError("Failed to register plugin client");
954 // ------------------------------------------------------------------------------------------------------------
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");
971 const Float64 sampleRate
= pData
->engine
->getSampleRate();
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
))
983 if (fFunctions
.getProperty(fInterface
,
984 kAudioUnitProperty_ElementCount
,
985 kAudioUnitScope_Input
,
986 0, &outData
, &outDataSize
) == noErr
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");
1002 outWritable
= false;
1003 if (fFunctions
.getPropertyInfo(fInterface
,
1004 kAudioUnitProperty_ElementCount
,
1005 kAudioUnitScope_Output
,
1006 0, &outDataSize
, &outWritable
) == noErr
1007 && outDataSize
== sizeof(UInt32
))
1010 if (fFunctions
.getProperty(fInterface
,
1011 kAudioUnitProperty_ElementCount
,
1012 kAudioUnitScope_Output
,
1013 0, &outData
, &outDataSize
) == noErr
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");
1028 // ------------------------------------------------------------------------------------------------------------
1029 // set default options
1031 pData
->options
= PLUGIN_OPTION_FIXED_BUFFERS
;
1037 BundleLoader fBundleLoader
;
1038 AudioComponentPlugInInterface
* fInterface
;
1039 AudioBufferList
* fAudioBufferData
;
1045 InitializeFn initialize
;
1046 UninitializeFn uninitialize
;
1047 GetPropertyInfoFn getPropertyInfo
;
1048 GetPropertyFn getProperty
;
1049 SetPropertyFn setProperty
;
1050 GetParameterFn getParameter
;
1051 SetParameterFn setParameter
;
1052 ScheduleParametersFn scheduleParameters
;
1055 MIDIEventFn midiEvent
;
1058 : initialize(nullptr),
1059 uninitialize(nullptr),
1060 getPropertyInfo(nullptr),
1061 getProperty(nullptr),
1062 setProperty(nullptr),
1063 getParameter(nullptr),
1064 setParameter(nullptr),
1065 scheduleParameters(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;
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
);
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
))
1113 init
.engine
->setLastError("AU support not available");
1118 // --------------------------------------------------------------------------------------------------------------------
1120 CARLA_BACKEND_END_NAMESPACE