26#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)
28JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE (
"-Wdeprecated-declarations")
31#include <AudioUnit/AUCocoaUIView.h>
32#include <CoreAudioKit/AUGenericView.h>
33#include <AudioToolbox/AudioUnitUtilities.h>
35#if JUCE_PLUGINHOST_ARA
36 #include <ARA_API/ARAAudioUnit.h>
41#include <CoreMIDI/MIDIServices.h>
43#include <CoreAudioKit/AUViewController.h>
45#include <juce_audio_basics/native/juce_CoreAudioTimeConversions_mac.h>
46#include <juce_audio_basics/native/juce_CoreAudioLayouts_mac.h>
47#include "juce_AU_Shared.h"
58 #define JUCE_AU_LOG(a) Logger::writeToLog(a);
60 #define JUCE_AU_LOG(a)
63namespace AudioUnitFormatHelpers
66 static ThreadLocalValue<int> insideCallback;
69 static String osTypeToString (OSType type)
noexcept
72 (juce_wchar) ((type >> 16) & 0xff),
74 (juce_wchar) (type & 0xff) };
78 static OSType stringToOSType (String s)
80 if (s.trim().length() >= 4)
85 return (((OSType) (
unsigned char) s[0]) << 24)
86 | (((OSType) (
unsigned char) s[1]) << 16)
87 | (((OSType) (
unsigned char) s[2]) << 8)
88 | ((OSType) (
unsigned char) s[3]);
91 static const char* auIdentifierPrefix =
"AudioUnit:";
93 static String createPluginIdentifier (
const AudioComponentDescription& desc)
95 String s (auIdentifierPrefix);
97 if (desc.componentType == kAudioUnitType_MusicDevice)
99 else if (desc.componentType == kAudioUnitType_MusicEffect
100 || desc.componentType == kAudioUnitType_Effect)
102 else if (desc.componentType == kAudioUnitType_Generator)
104 else if (desc.componentType == kAudioUnitType_Panner)
106 else if (desc.componentType == kAudioUnitType_Mixer)
108 else if (desc.componentType == kAudioUnitType_MIDIProcessor)
111 s << osTypeToString (desc.componentType) <<
","
112 << osTypeToString (desc.componentSubType) <<
","
113 << osTypeToString (desc.componentManufacturer);
118 static void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer)
120 CFObjectHolder<CFStringRef> cfName;
122 if (AudioComponentCopyName (comp, &cfName.object) == noErr)
123 name = String::fromCFString (cfName.object);
125 if (name.containsChar (
':'))
127 manufacturer = name.upToFirstOccurrenceOf (
":",
false,
false).trim();
128 name = name.fromFirstOccurrenceOf (
":",
false,
false).trim();
135 static bool getComponentDescFromIdentifier (
const String& fileOrIdentifier, AudioComponentDescription& desc,
136 String& name, String& version, String& manufacturer)
138 if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
140 String s (fileOrIdentifier.substring (
jmax (fileOrIdentifier.lastIndexOfChar (
':'),
141 fileOrIdentifier.lastIndexOfChar (
'/')) + 1));
144 tokens.addTokens (s,
",", StringRef());
145 tokens.removeEmptyStrings();
147 if (tokens.size() == 3)
150 desc.componentType = stringToOSType (tokens[0]);
151 desc.componentSubType = stringToOSType (tokens[1]);
152 desc.componentManufacturer = stringToOSType (tokens[2]);
154 if (AudioComponent comp = AudioComponentFindNext (
nullptr, &desc))
156 getNameAndManufacturer (comp, name, manufacturer);
158 if (manufacturer.isEmpty())
159 manufacturer = tokens[2];
161 if (version.isEmpty())
165 if (AudioComponentGetVersion (comp, &versionNum) == noErr)
167 version << (
int) (versionNum >> 16) <<
"."
168 << (
int) ((versionNum >> 8) & 0xff) <<
"."
169 << (
int) (versionNum & 0xff);
181 static bool getComponentDescFromFile ([[maybe_unused]]
const String& fileOrIdentifier,
182 [[maybe_unused]] AudioComponentDescription& desc,
183 [[maybe_unused]] String& name,
184 [[maybe_unused]] String& version,
185 [[maybe_unused]] String& manufacturer)
192 const File file (fileOrIdentifier);
193 if (! file.hasFileExtension (
".component") && ! file.hasFileExtension (
".appex"))
196 const char*
const utf8 = fileOrIdentifier.toUTF8();
198 if (
auto url = CFUniquePtr<CFURLRef> (CFURLCreateFromFileSystemRepresentation (
nullptr, (
const UInt8*) utf8,
199 (CFIndex) strlen (utf8), file.isDirectory())))
201 if (
auto bundleRef = CFUniquePtr<CFBundleRef> (CFBundleCreate (kCFAllocatorDefault, url.get())))
203 CFTypeRef bundleName = CFBundleGetValueForInfoDictionaryKey (bundleRef.get(), CFSTR (
"CFBundleName"));
205 if (bundleName !=
nullptr && CFGetTypeID (bundleName) == CFStringGetTypeID())
206 name = String::fromCFString ((CFStringRef) bundleName);
209 name = file.getFileNameWithoutExtension();
211 CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef.get(), CFSTR (
"CFBundleVersion"));
213 if (versionString !=
nullptr && CFGetTypeID (versionString) == CFStringGetTypeID())
214 version = String::fromCFString ((CFStringRef) versionString);
216 CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef.get(), CFSTR (
"CFBundleGetInfoString"));
218 if (manuString !=
nullptr && CFGetTypeID (manuString) == CFStringGetTypeID())
219 manufacturer = String::fromCFString ((CFStringRef) manuString);
221 class ScopedBundleResourceMap final
224 explicit ScopedBundleResourceMap (CFBundleRef refIn) :
ref (refIn),
225 resFileId (CFBundleOpenBundleResourceMap (
ref)),
226 valid (resFileId != -1)
229 UseResFile (resFileId);
232 ~ScopedBundleResourceMap()
235 CFBundleCloseBundleResourceMap (ref, resFileId);
238 bool isValid() const noexcept
244 const CFBundleRef
ref;
245 const ResFileRefNum resFileId;
249 const ScopedBundleResourceMap resourceMap { bundleRef.get() };
251 const OSType thngType = stringToOSType (
"thng");
252 auto numResources = Count1Resources (thngType);
254 if (resourceMap.isValid() && numResources > 0)
256 for (ResourceIndex i = 1; i <= numResources; ++i)
258 if (Handle h = Get1IndResource (thngType, i))
264 if (types[0] == kAudioUnitType_MusicDevice
265 || types[0] == kAudioUnitType_MusicEffect
266 || types[0] == kAudioUnitType_Effect
267 || types[0] == kAudioUnitType_Generator
268 || types[0] == kAudioUnitType_Panner
269 || types[0] == kAudioUnitType_Mixer
270 || types[0] == kAudioUnitType_MIDIProcessor)
272 desc.componentType = types[0];
273 desc.componentSubType = types[1];
274 desc.componentManufacturer = types[2];
276 if (AudioComponent comp = AudioComponentFindNext (
nullptr, &desc))
277 getNameAndManufacturer (comp, name, manufacturer);
289 NSBundle* bundle = [[NSBundle alloc] initWithPath: (NSString*) fileOrIdentifier.toCFString()];
291 NSArray* audioComponents = [bundle objectForInfoDictionaryKey:
@"AudioComponents"];
292 NSDictionary* dict = [audioComponents objectAtIndex: 0];
294 desc.componentManufacturer = stringToOSType (nsStringToJuce ((NSString*) [dict valueForKey:
@"manufacturer"]));
295 desc.componentType = stringToOSType (nsStringToJuce ((NSString*) [dict valueForKey:
@"type"]));
296 desc.componentSubType = stringToOSType (nsStringToJuce ((NSString*) [dict valueForKey:
@"subtype"]));
303 return desc.componentType != 0 && desc.componentSubType != 0;
307 static const char* getCategory (OSType type)
noexcept
311 case kAudioUnitType_Effect:
312 case kAudioUnitType_MusicEffect:
return "Effect";
313 case kAudioUnitType_MusicDevice:
return "Synth";
314 case kAudioUnitType_Generator:
return "Generator";
315 case kAudioUnitType_Panner:
return "Panner";
316 case kAudioUnitType_Mixer:
return "Mixer";
317 case kAudioUnitType_MIDIProcessor:
return "MidiEffects";
326 #if JUCE_MAC || JUCE_IOS
328 #define JUCE_IOS_MAC_VIEW UIView
329 using ViewComponentBaseClass = UIViewComponent;
331 #define JUCE_IOS_MAC_VIEW NSView
332 using ViewComponentBaseClass = NSViewComponent;
335 struct AutoResizingNSViewComponent final :
public ViewComponentBaseClass,
338 void childBoundsChanged (Component*)
override { triggerAsyncUpdate(); }
339 void handleAsyncUpdate()
override { resizeToFitView(); }
343 template <
typename Value>
344 static Optional<Value> tryGetProperty (AudioUnit inUnit,
345 AudioUnitPropertyID inID,
346 AudioUnitScope inScope,
347 AudioUnitElement inElement)
350 auto size = (UInt32)
sizeof (Value);
352 if (AudioUnitGetProperty (inUnit, inID, inScope, inElement, &data, &size) == noErr)
358 static UInt32 getElementCount (AudioUnit comp, AudioUnitScope scope)
noexcept
360 const auto count = tryGetProperty<UInt32> (comp, kAudioUnitProperty_ElementCount, scope, 0);
371 class SingleDirectionChannelMapping
374 void setUpMapping (AudioUnit comp,
bool isInput)
379 const auto scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
380 const auto n = getElementCount (comp, scope);
382 for (UInt32 busIndex = 0; busIndex < n; ++busIndex)
386 if (
const auto layout = tryGetProperty<AudioChannelLayout> (comp, kAudioUnitProperty_AudioChannelLayout, scope, busIndex))
388 const auto juceChannelOrder = CoreAudioLayouts::fromCoreAudio (*layout);
389 const auto auChannelOrder = CoreAudioLayouts::getCoreAudioLayoutChannels (*layout);
391 for (
auto juceChannelIndex = 0; juceChannelIndex < juceChannelOrder.size(); ++juceChannelIndex)
392 busMap.
push_back ((
size_t) auChannelOrder.indexOf (juceChannelOrder.getTypeOfChannel (juceChannelIndex)));
395 busOffset.push_back (busMap.
empty() ? unknownChannelCount : channels.
size());
396 channels.insert (channels.end(), busMap.
begin(), busMap.
end());
400 size_t getAuIndexForJuceChannel (
size_t bus,
size_t channel)
const noexcept
402 const auto baseOffset = busOffset[bus];
403 return baseOffset != unknownChannelCount ? channels[baseOffset + channel]
428 static bool isPluginAUv3 (
const AudioComponentDescription& desc)
430 if (@available (macOS 10.11, *))
431 return (desc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0;
437static bool hasARAExtension ([[maybe_unused]] AudioUnit audioUnit)
439 #if JUCE_PLUGINHOST_ARA
440 UInt32 propertySize =
sizeof (ARA::ARAAudioUnitFactory);
441 Boolean isWriteable = FALSE;
443 OSStatus
status = AudioUnitGetPropertyInfo (audioUnit,
444 ARA::kAudioUnitProperty_ARAFactory,
445 kAudioUnitScope_Global,
450 if ((status == noErr) && (propertySize ==
sizeof (ARA::ARAAudioUnitFactory)) && ! isWriteable)
457struct AudioUnitDeleter
459 void operator() (AudioUnit au)
const { AudioComponentInstanceDispose (au); }
468 #if JUCE_PLUGINHOST_ARA
469 jassert (audioUnit !=
nullptr);
471 UInt32 propertySize =
sizeof (ARA::ARAAudioUnitFactory);
472 ARA::ARAAudioUnitFactory audioUnitFactory { ARA::kARAAudioUnitMagic,
nullptr };
474 if (hasARAExtension (audioUnit.get()))
476 OSStatus
status = AudioUnitGetProperty (audioUnit.get(),
477 ARA::kAudioUnitProperty_ARAFactory,
478 kAudioUnitScope_Global,
483 if ((status == noErr)
484 && (propertySize ==
sizeof (ARA::ARAAudioUnitFactory))
485 && (audioUnitFactory.inOutMagicNumber == ARA::kARAAudioUnitMagic))
487 jassert (audioUnitFactory.outFactory !=
nullptr);
488 return getOrCreateARAFactory (audioUnitFactory.outFactory,
489 [owningAuPtr = std::move (audioUnit)]() {});
497struct VersionedAudioComponent
499 AudioComponent audioComponent =
nullptr;
502 bool operator< (
const VersionedAudioComponent& other)
const {
return audioComponent < other.audioComponent; }
505using AudioUnitCreationCallback =
std::function<void (AudioUnit, OSStatus)>;
507static void createAudioUnit (VersionedAudioComponent versionedComponent, AudioUnitCreationCallback callback)
509 if (versionedComponent.isAUv3)
511 if (@available (macOS 10.11, *))
513 AudioComponentInstantiate (versionedComponent.audioComponent,
514 kAudioComponentInstantiation_LoadOutOfProcess,
515 ^(AudioComponentInstance audioUnit, OSStatus err)
517 callback (audioUnit, err);
524 AudioComponentInstance audioUnit;
525 auto err = AudioComponentInstanceNew (versionedComponent.audioComponent, &audioUnit);
526 callback (err != noErr ?
nullptr : audioUnit, err);
529struct AudioComponentResult
531 explicit AudioComponentResult (String error) : errorMessage (
std::
move (error)) {}
533 explicit AudioComponentResult (VersionedAudioComponent auComponent) : component (
std::
move (auComponent)) {}
535 bool isValid()
const {
return component.audioComponent !=
nullptr; }
537 VersionedAudioComponent component;
541static AudioComponentResult getAudioComponent (AudioUnitPluginFormat& format,
const PluginDescription& desc)
543 using namespace AudioUnitFormatHelpers;
545 AudioUnitPluginFormat audioUnitPluginFormat;
547 if (!
format.fileMightContainThisPluginType (desc.fileOrIdentifier))
548 return AudioComponentResult {
NEEDS_TRANS (
"Plug-in description is not an AudioUnit plug-in") };
550 String pluginName, version, manufacturer;
551 AudioComponentDescription componentDesc;
552 AudioComponent auComponent;
553 String errMessage =
NEEDS_TRANS (
"Cannot find AudioUnit from description");
555 if (! getComponentDescFromIdentifier (desc.fileOrIdentifier, componentDesc, pluginName, version, manufacturer)
556 && ! getComponentDescFromFile (desc.fileOrIdentifier, componentDesc, pluginName, version, manufacturer))
558 return AudioComponentResult { errMessage };
561 if ((auComponent = AudioComponentFindNext (
nullptr, &componentDesc)) ==
nullptr)
563 return AudioComponentResult { errMessage };
566 if (AudioComponentGetDescription (auComponent, &componentDesc) != noErr)
568 return AudioComponentResult { errMessage };
571 const bool isAUv3 = AudioUnitFormatHelpers::isPluginAUv3 (componentDesc);
573 return AudioComponentResult { { auComponent, isAUv3 } };
576static void getOrCreateARAAudioUnit (VersionedAudioComponent auComponent,
std::function<
void (AudioUnitSharedPtr)> callback)
580 if (
auto audioUnit = audioUnitARACache[auComponent].
lock())
582 callback (std::move (audioUnit));
586 createAudioUnit (auComponent, [auComponent, cb = std::move (callback)] (AudioUnit audioUnit, OSStatus err)
588 cb ([auComponent, audioUnit, err]() -> AudioUnitSharedPtr
593 AudioUnitSharedPtr auPtr { AudioUnitUniquePtr { audioUnit } };
594 audioUnitARACache[auComponent] = auPtr;
601class AudioUnitPluginWindowCocoa;
604class AudioUnitPluginInstance final :
public AudioPluginInstance
607 struct AUInstanceParameter final :
public Parameter
609 AUInstanceParameter (AudioUnitPluginInstance& parent,
611 const String& parameterName,
612 AudioUnitParameterValue minParameterValue,
613 AudioUnitParameterValue maxParameterValue,
614 AudioUnitParameterValue defaultParameterValue,
615 bool parameterIsAutomatable,
616 bool parameterIsDiscrete,
617 int numParameterSteps,
620 bool parameterValuesHaveStrings)
621 : pluginInstance (parent),
622 paramID (parameterID),
623 name (parameterName),
624 minValue (minParameterValue),
625 maxValue (maxParameterValue),
626 range (maxValue - minValue),
627 automatable (parameterIsAutomatable),
628 discrete (parameterIsDiscrete),
629 numSteps (numParameterSteps),
630 valuesHaveStrings (parameterValuesHaveStrings),
631 isSwitch (isBoolean),
633 defaultValue (normaliseParamValue (defaultParameterValue))
635 auValueStrings = Parameter::getAllValueStrings();
638 float getValue()
const override
642 AudioUnitParameterValue value = 0;
644 if (
auto* au = pluginInstance.audioUnit)
646 AudioUnitGetParameter (au, paramID, kAudioUnitScope_Global, 0, &value);
647 value = normaliseParamValue (value);
653 void setValue (
float newValue)
override
657 if (
auto* au = pluginInstance.audioUnit)
659 AudioUnitSetParameter (au, paramID, kAudioUnitScope_Global,
660 0, scaleParamValue (newValue), 0);
662 sendParameterChangeEvent();
666 float getDefaultValue()
const override
671 String getName (
int )
const override {
return name; }
672 String getLabel()
const override {
return valueLabel; }
674 String getText (
float value,
int maximumLength)
const override
676 if (! auValueStrings.isEmpty())
678 auto index =
roundToInt (jlimit (0.0f, 1.0f, value) * (
float) (auValueStrings.size() - 1));
679 return auValueStrings[index];
682 auto scaledValue = scaleParamValue (value);
684 if (valuesHaveStrings)
686 if (
auto* au = pluginInstance.audioUnit)
688 AudioUnitParameterStringFromValue stringValue;
689 stringValue.inParamID = paramID;
690 stringValue.inValue = &scaledValue;
691 stringValue.outString =
nullptr;
693 UInt32 propertySize =
sizeof (stringValue);
695 auto err = AudioUnitGetProperty (au,
696 kAudioUnitProperty_ParameterStringFromValue,
697 kAudioUnitScope_Global,
702 if (! err && stringValue.outString !=
nullptr)
703 return String::fromCFString (stringValue.outString).substring (0, maximumLength);
707 return Parameter::getText (scaledValue, maximumLength);
710 float getValueForText (
const String& text)
const override
712 if (! auValueStrings.isEmpty())
714 auto index = auValueStrings.indexOf (text);
717 return ((
float) index) / (
float) (auValueStrings.size() - 1);
720 if (valuesHaveStrings)
722 if (
auto* au = pluginInstance.audioUnit)
724 AudioUnitParameterValueFromString pvfs;
725 pvfs.inParamID = paramID;
726 CFUniquePtr<CFStringRef> cfString (text.toCFString());
727 pvfs.inString = cfString.get();
728 UInt32 propertySize =
sizeof (pvfs);
730 auto err = AudioUnitGetProperty (au,
731 kAudioUnitProperty_ParameterValueFromString,
732 kAudioUnitScope_Global,
738 return normaliseParamValue (pvfs.outValue);
742 return Parameter::getValueForText (text);
745 bool isAutomatable()
const override {
return automatable; }
746 bool isDiscrete()
const override {
return discrete; }
747 bool isBoolean()
const override {
return isSwitch; }
748 int getNumSteps()
const override {
return numSteps; }
750 StringArray getAllValueStrings()
const override
752 return auValueStrings;
755 String getParameterID()
const override
757 return String (paramID);
760 void sendParameterChangeEvent()
763 jassert (pluginInstance.audioUnit !=
nullptr);
766 ev.mEventType = kAudioUnitEvent_ParameterValueChange;
767 ev.mArgument.mParameter.mAudioUnit = pluginInstance.audioUnit;
768 ev.mArgument.mParameter.mParameterID = paramID;
769 ev.mArgument.mParameter.mScope = kAudioUnitScope_Global;
770 ev.mArgument.mParameter.mElement = 0;
772 AUEventListenerNotify (pluginInstance.eventListenerRef,
nullptr, &ev);
776 float normaliseParamValue (
float scaledValue)
const noexcept
778 return (scaledValue - minValue) / range;
781 float scaleParamValue (
float normalisedValue)
const noexcept
783 return minValue + (range * normalisedValue);
786 UInt32 getRawParamID()
const {
return paramID; }
788 void setName (String&& newName) { name = std::move (newName); }
789 void setLabel (String&& newLabel) { valueLabel = std::move (newLabel); }
792 AudioUnitPluginInstance& pluginInstance;
793 const UInt32 paramID;
795 const AudioUnitParameterValue minValue, maxValue, range;
796 const bool automatable, discrete;
798 const bool valuesHaveStrings, isSwitch;
800 const AudioUnitParameterValue defaultValue;
801 StringArray auValueStrings;
804 AudioUnitPluginInstance (AudioComponentInstance au)
805 : AudioPluginInstance (getBusesProperties (au)),
806 auComponent (AudioComponentInstanceGetComponent (au)),
809 eventListenerRef (nullptr),
811 midiConcatenator (2048)
813 using namespace AudioUnitFormatHelpers;
815 AudioComponentGetDescription (auComponent, &componentDesc);
817 isAUv3 = AudioUnitFormatHelpers::isPluginAUv3 (componentDesc);
819 wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice
820 || componentDesc.componentType == kAudioUnitType_MusicEffect
821 || componentDesc.componentType == kAudioUnitType_MIDIProcessor;
823 isMidiEffectPlugin = (componentDesc.componentType == kAudioUnitType_MIDIProcessor);
824 AudioComponentDescription ignore;
825 getComponentDescFromIdentifier (createPluginIdentifier (componentDesc), ignore, pluginName, version, manufacturer);
826 updateSupportedLayouts();
829 ~AudioUnitPluginInstance()
override
836 jassert (AudioUnitFormatHelpers::insideCallback.
get() == 0);
839 if (audioUnit !=
nullptr)
841 struct AUDeleter final :
public CallbackMessage
843 AUDeleter (AudioUnitPluginInstance& inInstance, WaitableEvent& inEvent)
844 : auInstance (inInstance), completionSignal (inEvent)
847 void messageCallback()
override
849 auInstance.cleanup();
850 completionSignal.signal();
853 AudioUnitPluginInstance& auInstance;
854 WaitableEvent& completionSignal;
857 if (MessageManager::getInstance()->isThisTheMessageThread())
863 WaitableEvent completionEvent;
864 (
new AUDeleter (*
this, completionEvent))->post();
865 completionEvent.wait();
874 disposeEventListener();
880 AudioComponentInstanceDispose (audioUnit);
884 bool initialise (
double rate,
int blockSize)
886 producesMidiMessages = canProduceMidiOutput();
887 setRateAndBufferSizeDetails (rate, blockSize);
888 setLatencySamples (0);
889 refreshParameterList();
890 setPluginCallbacks();
893 createEventListener();
900 bool canAddBus (
bool isInput)
const override {
return isBusCountWritable (isInput); }
901 bool canRemoveBus (
bool isInput)
const override {
return isBusCountWritable (isInput); }
903 bool canApplyBusCountChange (
bool isInput,
bool isAdding, BusProperties& outProperties)
override
905 auto currentCount = (UInt32) getBusCount (isInput);
906 auto newCount = (UInt32) ((
int) currentCount + (isAdding ? 1 : -1));
907 AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
909 if (AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ElementCount, scope, 0, &newCount,
sizeof (newCount)) == noErr)
911 getBusProperties (isInput, currentCount, outProperties.busName, outProperties.defaultLayout);
912 outProperties.isActivatedByDefault =
true;
913 updateSupportedLayouts();
922 bool isBusesLayoutSupported (
const BusesLayout& layouts)
const override
924 if (layouts == getBusesLayout())
927 for (
int dir = 0; dir < 2; ++dir)
929 const bool isInput = (dir == 0);
930 auto& requestedLayouts = (isInput ? layouts.inputBuses : layouts.outputBuses);
931 auto& oppositeRequestedLayouts = (isInput ? layouts.outputBuses : layouts.inputBuses);
932 auto& supported = (isInput ? supportedInLayouts : supportedOutLayouts);
933 const int n = getBusCount (isInput);
935 for (
int busIdx = 0; busIdx < n; ++busIdx)
937 auto& requested = requestedLayouts.getReference (busIdx);
938 const int oppositeBusIdx =
jmin (getBusCount (! isInput) - 1, busIdx);
939 const bool hasOppositeBus = (oppositeBusIdx >= 0);
940 auto oppositeRequested = (hasOppositeBus ? oppositeRequestedLayouts.getReference (oppositeBusIdx) : AudioChannelSet());
941 auto& possible = supported.getReference (busIdx);
943 if (requested.isDisabled())
946 if (possible.size() > 0 && ! possible.contains (requested))
950 for (i = 0; i < numChannelInfos; ++i)
952 auto& info = channelInfos[i];
953 auto& thisChannels = (isInput ? info.inChannels : info.outChannels);
954 auto& opChannels = (isInput ? info.outChannels : info.inChannels);
957 if (thisChannels == 0)
continue;
958 else if (thisChannels > 0 && requested.size() != thisChannels)
continue;
959 else if (thisChannels < -2 && requested.size() > (thisChannels * -1))
continue;
962 if (opChannels == 0 && hasOppositeBus)
continue;
963 else if (opChannels > 0 && oppositeRequested.size() != opChannels)
continue;
964 else if (opChannels < -2 && oppositeRequested.size() > (opChannels * -1))
continue;
967 if (thisChannels == -2 && opChannels == -2)
continue;
968 if (thisChannels == -1 && opChannels == -1)
970 int numOppositeBuses = getBusCount (! isInput);
973 for (j = 0; j < numOppositeBuses; ++j)
974 if (requested.size() != oppositeRequestedLayouts.getReference (j).size())
977 if (j < numOppositeBuses)
984 if (i >= numChannelInfos)
992 bool syncBusLayouts (
const BusesLayout& layouts,
bool isInitialized,
bool& layoutHasChanged)
const
994 layoutHasChanged =
false;
996 for (
int dir = 0; dir < 2; ++dir)
998 const bool isInput = (dir == 0);
999 const AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
1000 const int n = getBusCount (isInput);
1002 if (getElementCount (scope) != n && isBusCountWritable (isInput))
1004 auto newCount =
static_cast<UInt32
> (n);
1005 layoutHasChanged =
true;
1007 [[maybe_unused]]
auto err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ElementCount, scope, 0, &newCount,
sizeof (newCount));
1011 for (
int i = 0; i < n; ++i)
1014 UInt32 sampleRateSize =
sizeof (sampleRate);
1016 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope,
static_cast<UInt32
> (i), &sampleRate, &sampleRateSize);
1018 const AudioChannelSet& set = layouts.getChannelSet (isInput, i);
1019 const int requestedNumChannels = set.size();
1022 AudioStreamBasicDescription stream;
1023 UInt32 dataSize =
sizeof (stream);
1024 auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope,
static_cast<UInt32
> (i), &stream, &dataSize);
1026 if (err != noErr || dataSize <
sizeof (stream))
1029 const int actualNumChannels =
static_cast<int> (stream.mChannelsPerFrame);
1031 if (actualNumChannels != requestedNumChannels)
1033 layoutHasChanged =
true;
1035 stream.mSampleRate = sampleRate;
1036 stream.mFormatID = kAudioFormatLinearPCM;
1037 stream.mFormatFlags = (
int) kAudioFormatFlagsNativeFloatPacked | (
int) kAudioFormatFlagIsNonInterleaved | (
int) kAudioFormatFlagsNativeEndian;
1038 stream.mFramesPerPacket = 1;
1039 stream.mBytesPerPacket = 4;
1040 stream.mBytesPerFrame = 4;
1041 stream.mBitsPerChannel = 32;
1042 stream.mChannelsPerFrame =
static_cast<UInt32
> (requestedNumChannels);
1044 err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope,
static_cast<UInt32
> (i), &stream,
sizeof (stream));
1045 if (err != noErr)
return false;
1049 if (! set.isDiscreteLayout())
1051 const AudioChannelLayoutTag requestedTag = CoreAudioLayouts::toCoreAudio (set);
1053 AudioChannelLayout layout;
1054 const UInt32 minDataSize =
sizeof (layout) -
sizeof (AudioChannelDescription);
1055 UInt32 dataSize = minDataSize;
1057 AudioChannelLayoutTag actualTag = kAudioChannelLayoutTag_Unknown;
1058 auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, scope,
static_cast<UInt32
> (i), &layout, &dataSize);
1059 bool supportsLayouts = (err == noErr && dataSize >= minDataSize);
1061 if (supportsLayouts)
1063 const UInt32 expectedSize =
1064 minDataSize + (
sizeof (AudioChannelDescription) * layout.mNumberChannelDescriptions);
1066 HeapBlock<AudioChannelLayout> layoutBuffer;
1067 layoutBuffer.malloc (1, expectedSize);
1068 dataSize = expectedSize;
1070 err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, scope,
1071 static_cast<UInt32
> (i), layoutBuffer.get(), &dataSize);
1073 if (err != noErr || dataSize < expectedSize)
1077 actualTag = CoreAudioLayouts::toCoreAudio (CoreAudioLayouts::fromCoreAudio (layout));
1079 if (actualTag != requestedTag)
1082 layout.mChannelLayoutTag = requestedTag;
1084 err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, scope,
static_cast<UInt32
> (i), &layout, minDataSize);
1088 if (err != noErr && supportsLayouts && isInitialized)
1099 bool canApplyBusesLayout (
const BusesLayout& layouts)
const override
1105 bool layoutHasChanged =
false;
1107 if (! syncBusLayouts (layouts,
false, layoutHasChanged))
1111 if (! layoutHasChanged)
1115 const auto success = (AudioUnitInitialize (audioUnit) == noErr)
1116 && syncBusLayouts (layouts,
true, layoutHasChanged);
1118 AudioUnitUninitialize (audioUnit);
1122 syncBusLayouts (getBusesLayout(),
false, layoutHasChanged);
1130 void fillInPluginDescription (PluginDescription& desc)
const override
1132 desc.name = pluginName;
1133 desc.descriptiveName = pluginName;
1134 desc.fileOrIdentifier = AudioUnitFormatHelpers::createPluginIdentifier (componentDesc);
1135 desc.uniqueId = desc.deprecatedUid = ((
int) componentDesc.componentType)
1136 ^ ((
int) componentDesc.componentSubType)
1137 ^ ((
int) componentDesc.componentManufacturer);
1138 desc.lastFileModTime = Time();
1139 desc.lastInfoUpdateTime = Time::getCurrentTime();
1140 desc.pluginFormatName =
"AudioUnit";
1141 desc.category = AudioUnitFormatHelpers::getCategory (componentDesc.componentType);
1142 desc.manufacturerName = manufacturer;
1143 desc.version = version;
1144 desc.numInputChannels = getTotalNumInputChannels();
1145 desc.numOutputChannels = getTotalNumOutputChannels();
1146 desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice);
1148 #if JUCE_PLUGINHOST_ARA
1149 desc.hasARAExtension = [&]
1151 UInt32 propertySize =
sizeof (ARA::ARAAudioUnitFactory);
1152 Boolean isWriteable = FALSE;
1154 OSStatus
status = AudioUnitGetPropertyInfo (audioUnit,
1155 ARA::kAudioUnitProperty_ARAFactory,
1156 kAudioUnitScope_Global,
1161 return (status == noErr) && (propertySize ==
sizeof (ARA::ARAAudioUnitFactory)) && ! isWriteable;
1166 void getExtensions (ExtensionsVisitor& visitor)
const override
1168 struct Extensions final :
public ExtensionsVisitor::AudioUnitClient
1170 explicit Extensions (
const AudioUnitPluginInstance* instanceIn) : instance (instanceIn) {}
1172 AudioUnit getAudioUnitHandle() const noexcept
override {
return instance->audioUnit; }
1174 const AudioUnitPluginInstance* instance =
nullptr;
1177 visitor.visitAudioUnitClient (Extensions {
this });
1179 #ifdef JUCE_PLUGINHOST_ARA
1180 struct ARAExtensions final :
public ExtensionsVisitor::ARAClient
1182 explicit ARAExtensions (
const AudioUnitPluginInstance* instanceIn) : instance (instanceIn) {}
1186 getOrCreateARAAudioUnit ({ instance->auComponent, instance->isAUv3 },
1187 [origCb = std::move (cb)] (
auto dylibKeepAliveAudioUnit)
1189 origCb ([&]() -> ARAFactoryWrapper
1191 if (dylibKeepAliveAudioUnit !=
nullptr)
1192 return ARAFactoryWrapper { ::juce::getARAFactory (std::move (dylibKeepAliveAudioUnit)) };
1194 return ARAFactoryWrapper {
nullptr };
1199 const AudioUnitPluginInstance* instance =
nullptr;
1202 if (hasARAExtension (audioUnit))
1203 visitor.visitARAClient (ARAExtensions (
this));
1207 void* getPlatformSpecificData()
override {
return audioUnit; }
1208 const String getName()
const override {
return pluginName; }
1210 double getTailLengthSeconds()
const override
1213 UInt32 tailSize =
sizeof (tail);
1215 if (audioUnit !=
nullptr)
1216 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_TailTime, kAudioUnitScope_Global,
1217 0, &tail, &tailSize);
1222 bool acceptsMidi()
const override {
return wantsMidiMessages; }
1223 bool producesMidi()
const override {
return producesMidiMessages; }
1228 void prepareToPlay (
double newSampleRate,
int estimatedSamplesPerBlock)
override
1230 if (audioUnit !=
nullptr)
1233 setPluginCallbacks();
1235 for (
int dir = 0; dir < 2; ++dir)
1237 const bool isInput = (dir == 0);
1238 const AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
1239 const int n = getBusCount (isInput);
1241 for (
int i = 0; i < n; ++i)
1244 UInt32 sampleRateSize =
sizeof (sampleRate);
1245 const Float64 sr = newSampleRate;
1247 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope,
static_cast<UInt32
> (i), &sampleRate, &sampleRateSize);
1249 if (! approximatelyEqual (sampleRate, sr))
1253 AudioStreamBasicDescription stream;
1254 UInt32 dataSize =
sizeof (stream);
1255 auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope,
static_cast<UInt32
> (i), &stream, &dataSize);
1257 if (err == noErr && dataSize ==
sizeof (stream))
1259 stream.mSampleRate = sr;
1260 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope,
static_cast<UInt32
> (i), &stream,
sizeof (stream));
1265 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope,
static_cast<UInt32
> (i), &sr,
sizeof (sr));
1271 AURenderCallbackStruct info;
1274 info.inputProcRefCon =
this;
1275 info.inputProc = renderGetInputCallback;
1277 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
1278 static_cast<UInt32
> (i), &info,
sizeof (info));
1282 outputBufferList.add (
new AUBuffer (
static_cast<size_t> (getChannelCountOfBus (
false, i))));
1287 UInt32 frameSize = (UInt32) estimatedSamplesPerBlock;
1288 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0,
1289 &frameSize,
sizeof (frameSize));
1291 setRateAndBufferSizeDetails ((
double) newSampleRate, estimatedSamplesPerBlock);
1294 timeStamp.mSampleTime = 0;
1295 timeStamp.mHostTime = mach_absolute_time();
1296 timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
1304 if (! syncBusLayouts (getBusesLayout(),
false, ignore))
1309 if (AudioUnitInitialize (audioUnit) != noErr)
1312 if (! haveParameterList)
1313 refreshParameterList();
1315 if (! syncBusLayouts (getBusesLayout(),
true, ignore))
1317 AudioUnitUninitialize (audioUnit);
1325 inMapping .setUpMapping (audioUnit,
true);
1326 outMapping.setUpMapping (audioUnit,
false);
1328 preparedChannels =
jmax (getTotalNumInputChannels(), getTotalNumOutputChannels());
1329 preparedSamples = estimatedSamplesPerBlock;
1330 inputBuffer.setSize (preparedChannels, preparedSamples);
1334 void releaseResources()
override
1339 AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
1340 AudioUnitUninitialize (audioUnit);
1342 outputBufferList.clear();
1346 incomingMidi.clear();
1351 for (
int i = 0; i < getBusCount (true); ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, static_cast<UInt32> (i));
1352 for (
int i = 0; i < getBusCount (false); ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, static_cast<UInt32> (i));
1355 void processAudio (AudioBuffer<float>& buffer, MidiBuffer& midiMessages,
bool processBlockBypassedCalled)
1357 auto* playhead = getPlayHead();
1358 const auto position = playhead !=
nullptr ? playhead->getPosition() : nullopt;
1360 if (
const auto hostTimeNs = position.hasValue() ? position->getHostTimeNs() : nullopt)
1362 timeStamp.mHostTime = *hostTimeNs;
1363 timeStamp.mFlags |= kAudioTimeStampHostTimeValid;
1367 timeStamp.mHostTime = 0;
1368 timeStamp.mFlags &= ~kAudioTimeStampHostTimeValid;
1372 jassert (buffer.getNumChannels() <= preparedChannels);
1373 jassert (buffer.getNumSamples() <= preparedSamples);
1377 inputBuffer.makeCopyOf (buffer,
true);
1379 const auto numSamples = buffer.getNumSamples();
1381 if (auSupportsBypass)
1383 updateBypass (processBlockBypassedCalled);
1385 else if (processBlockBypassedCalled)
1387 AudioProcessor::processBlockBypassed (buffer, midiMessages);
1393 const auto numOutputBuses = getBusCount (
false);
1395 for (
int i = 0; i < numOutputBuses; ++i)
1397 if (AUBuffer* buf = outputBufferList[i])
1399 AudioBufferList& abl = *buf;
1400 const auto* bus = getBus (
false, i);
1401 const auto channelCount = bus !=
nullptr ? bus->getNumberOfChannels() : 0;
1403 for (
auto juceChannel = 0; juceChannel < channelCount; ++juceChannel)
1405 const auto auChannel = outMapping.getAuIndexForJuceChannel ((
size_t) i, (
size_t) juceChannel);
1406 abl.mBuffers[auChannel].mNumberChannels = 1;
1407 abl.mBuffers[auChannel].mDataByteSize = (UInt32) ((
size_t) numSamples *
sizeof (
float));
1408 abl.mBuffers[auChannel].mData = buffer.getWritePointer (bus->getChannelIndexInProcessBlockBuffer (juceChannel));
1413 if (wantsMidiMessages)
1415 for (
const auto metadata : midiMessages)
1417 if (metadata.numBytes <= 3)
1418 MusicDeviceMIDIEvent (audioUnit,
1419 metadata.data[0], metadata.data[1], metadata.data[2],
1420 (UInt32) metadata.samplePosition);
1422 MusicDeviceSysEx (audioUnit, metadata.data, (UInt32) metadata.numBytes);
1425 midiMessages.clear();
1428 for (
int i = 0; i < numOutputBuses; ++i)
1430 AudioUnitRenderActionFlags flags = 0;
1432 if (AUBuffer* buf = outputBufferList[i])
1433 AudioUnitRender (audioUnit, &flags, &timeStamp,
static_cast<UInt32
> (i), (UInt32) numSamples, buf->bufferList.get());
1436 timeStamp.mSampleTime += numSamples;
1441 for (
int i = getTotalNumOutputChannels(); --i >= 0;)
1442 buffer.clear (i, 0, buffer.getNumSamples());
1445 if (producesMidiMessages)
1448 midiMessages.swapWith (incomingMidi);
1449 incomingMidi.clear();
1453 void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
override
1455 processAudio (buffer, midiMessages,
false);
1458 void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
override
1460 processAudio (buffer, midiMessages,
true);
1464 AudioProcessorParameter* getBypassParameter()
const override {
return auSupportsBypass ? bypassParam.get() :
nullptr; }
1466 bool hasEditor()
const override
1474 return (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_RequestViewController,
1475 kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
1476 && dataSize ==
sizeof (uintptr_t) && isWritable != 0);
1480 AudioProcessorEditor* createEditor()
override;
1482 static AudioProcessor::BusesProperties getBusesProperties (AudioComponentInstance comp)
1484 AudioProcessor::BusesProperties busProperties;
1486 for (
int dir = 0; dir < 2; ++dir)
1488 const auto isInput = (dir == 0);
1489 const auto n = AudioUnitFormatHelpers::getElementCount (comp, isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output);
1491 for (UInt32 i = 0; i < n; ++i)
1494 AudioChannelSet currentLayout;
1496 getBusProperties (comp, isInput, i, busName, currentLayout);
1497 jassert (! currentLayout.isDisabled());
1499 busProperties.addBus (isInput, busName, currentLayout,
true);
1503 return busProperties;
1507 const String getInputChannelName (
int index)
const override
1509 if (isPositiveAndBelow (index, getTotalNumInputChannels()))
1510 return "Input " + String (index + 1);
1515 const String getOutputChannelName (
int index)
const override
1517 if (isPositiveAndBelow (index, getTotalNumOutputChannels()))
1518 return "Output " + String (index + 1);
1523 bool isInputChannelStereoPair (
int index)
const override {
return isPositiveAndBelow (index, getTotalNumInputChannels()); }
1524 bool isOutputChannelStereoPair (
int index)
const override {
return isPositiveAndBelow (index, getTotalNumOutputChannels()); }
1527 void sendAllParametersChangedEvents()
1530 jassert (audioUnit !=
nullptr);
1532 AudioUnitParameter param;
1533 param.mAudioUnit = audioUnit;
1534 param.mParameterID = kAUParameterListener_AnyParameter;
1536 AUParameterListenerNotify (
nullptr,
nullptr, ¶m);
1541 class ScopedFactoryPresets
1544 ScopedFactoryPresets (AudioUnit& au)
1546 UInt32 sz =
sizeof (CFArrayRef);
1547 AudioUnitGetProperty (au, kAudioUnitProperty_FactoryPresets,
1548 kAudioUnitScope_Global, 0, &presets.object, &sz);
1551 CFArrayRef
get() const noexcept
1553 return presets.object;
1557 CFObjectHolder<CFArrayRef> presets;
1560 int getNumPrograms()
override
1562 ScopedFactoryPresets factoryPresets { audioUnit };
1564 if (factoryPresets.get() !=
nullptr)
1565 return (
int) CFArrayGetCount (factoryPresets.get());
1570 int getCurrentProgram()
override
1573 current.presetNumber = 0;
1574 UInt32 sz =
sizeof (AUPreset);
1576 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_PresentPreset,
1577 kAudioUnitScope_Global, 0, ¤t, &sz);
1579 return current.presetNumber;
1582 void setCurrentProgram (
int newIndex)
override
1584 ScopedFactoryPresets factoryPresets { audioUnit };
1586 if (factoryPresets.get() !=
nullptr
1587 && newIndex < (
int) CFArrayGetCount (factoryPresets.get()))
1590 current.presetNumber = newIndex;
1592 if (
auto* p =
static_cast<const AUPreset*
> (CFArrayGetValueAtIndex (factoryPresets.get(), newIndex)))
1593 current.presetName = p->presetName;
1595 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_PresentPreset,
1596 kAudioUnitScope_Global, 0, ¤t,
sizeof (AUPreset));
1598 sendAllParametersChangedEvents();
1602 const String getProgramName (
int index)
override
1607 current.presetNumber = -1;
1608 current.presetName = CFSTR (
"");
1610 UInt32 prstsz =
sizeof (AUPreset);
1612 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_PresentPreset,
1613 kAudioUnitScope_Global, 0, ¤t, &prstsz);
1615 return String::fromCFString (current.presetName);
1618 ScopedFactoryPresets factoryPresets { audioUnit };
1620 if (factoryPresets.get() !=
nullptr)
1622 for (CFIndex i = 0; i < CFArrayGetCount (factoryPresets.get()); ++i)
1623 if (
auto* p =
static_cast<const AUPreset*
> (CFArrayGetValueAtIndex (factoryPresets.get(), i)))
1624 if (p->presetNumber == index)
1625 return String::fromCFString (p->presetName);
1631 void changeProgramName (
int ,
const String& )
override
1637 void updateTrackProperties (
const TrackProperties& properties)
override
1639 if (properties.name.isNotEmpty())
1641 CFObjectHolder<CFStringRef> contextName { properties.name.toCFString() };
1642 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ContextName, kAudioUnitScope_Global,
1643 0, &contextName.object, sizeof (contextName.object));
1648 void getStateInformation (MemoryBlock& destData)
override
1650 getCurrentProgramStateInformation (destData);
1653 void getCurrentProgramStateInformation (MemoryBlock& destData)
override
1655 CFObjectHolder<CFPropertyListRef> propertyList;
1656 UInt32 sz =
sizeof (propertyList.object);
1658 if (AudioUnitGetProperty (audioUnit,
1659 kAudioUnitProperty_ClassInfo,
1660 kAudioUnitScope_Global,
1661 0, &propertyList.object, &sz) == noErr)
1663 CFUniquePtr<CFWriteStreamRef> stream (CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault));
1664 CFWriteStreamOpen (stream.get());
1666 CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList.object, stream.get(), kCFPropertyListBinaryFormat_v1_0,
nullptr);
1667 CFWriteStreamClose (stream.get());
1669 CFUniquePtr<CFDataRef>
data ((CFDataRef) CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten));
1671 destData.setSize ((
size_t) bytesWritten);
1672 destData.copyFrom (CFDataGetBytePtr (
data.get()), 0, destData.getSize());
1676 void setStateInformation (
const void* data,
int sizeInBytes)
override
1678 setCurrentProgramStateInformation (data, sizeInBytes);
1681 void setCurrentProgramStateInformation (
const void* data,
int sizeInBytes)
override
1683 CFUniquePtr<CFReadStreamRef> stream (CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, (
const UInt8*) data,
1684 sizeInBytes, kCFAllocatorNull));
1685 CFReadStreamOpen (stream.get());
1687 CFPropertyListFormat
format = kCFPropertyListBinaryFormat_v1_0;
1688 CFObjectHolder<CFPropertyListRef> propertyList { CFPropertyListCreateFromStream (kCFAllocatorDefault, stream.get(), 0,
1689 kCFPropertyListImmutable, &format,
nullptr) };
1691 if (propertyList.object !=
nullptr)
1693 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global,
1694 0, &propertyList.object, sizeof (propertyList.object));
1696 sendAllParametersChangedEvents();
1700 void refreshParameterList()
override
1702 paramIDToParameter.clear();
1703 AudioProcessorParameterGroup newParameterTree;
1705 if (audioUnit !=
nullptr)
1707 UInt32 paramListSize = 0;
1708 auto err = AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
1709 0, ¶mListSize,
nullptr);
1711 haveParameterList = (paramListSize > 0 && err == noErr);
1713 if (! haveParameterList)
1716 if (paramListSize > 0)
1718 const size_t numParams = paramListSize /
sizeof (
int);
1722 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
1723 0, ids.data(), ¶mListSize);
1727 for (
size_t i = 0; i < numParams; ++i)
1729 const ScopedAudioUnitParameterInfo info { audioUnit, ids[i] };
1731 if (! info.isValid())
1734 const auto paramName = getParamName (info.get());
1735 const auto label = getParamLabel (info.get());
1736 const auto isDiscrete = (info.get().unit == kAudioUnitParameterUnit_Indexed
1737 || info.get().unit == kAudioUnitParameterUnit_Boolean);
1738 const auto isBoolean = info.get().unit == kAudioUnitParameterUnit_Boolean;
1743 info.get().minValue,
1744 info.get().maxValue,
1745 info.get().defaultValue,
1746 (info.get().flags & kAudioUnitParameterFlag_NonRealTime) == 0,
1748 isDiscrete ? (
int) (info.get().maxValue - info.get().minValue + 1.0f) : AudioProcessor::getDefaultNumParameterSteps(),
1751 (info.
get().flags & kAudioUnitParameterFlag_ValuesHaveStrings) != 0);
1753 paramIDToParameter.emplace (ids[i], parameter.get());
1755 if (info.get().flags & kAudioUnitParameterFlag_HasClump)
1757 auto groupInfo = groupIDMap.
find (info.get().clumpID);
1759 if (groupInfo == groupIDMap.
end())
1761 const auto clumpName = [
this, &info]
1763 AudioUnitParameterNameInfo clumpNameInfo;
1764 UInt32 clumpSz =
sizeof (clumpNameInfo);
1766 clumpNameInfo.inID = info.get().clumpID;
1767 clumpNameInfo.inDesiredLength = (SInt32) 256;
1769 if (AudioUnitGetProperty (audioUnit,
1770 kAudioUnitProperty_ParameterClumpName,
1771 kAudioUnitScope_Global,
1775 return String::fromCFString (clumpNameInfo.outName);
1777 return String (info.get().clumpID);
1781 clumpName, String());
1782 group->addChild (std::move (parameter));
1783 groupIDMap[info.get().clumpID] = group.get();
1784 newParameterTree.addChild (std::move (group));
1788 groupInfo->second->addChild (std::move (parameter));
1793 newParameterTree.addChild (std::move (parameter));
1799 setHostedParameterTree (std::move (newParameterTree));
1801 UInt32 propertySize = 0;
1802 Boolean writable =
false;
1804 auSupportsBypass = (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_BypassEffect,
1805 kAudioUnitScope_Global, 0, &propertySize, &writable) == noErr
1806 && propertySize >=
sizeof (UInt32) && writable);
1807 bypassParam.reset (
new AUBypassParameter (*
this));
1810 void updateLatency()
1812 Float64 latencySecs = 0.0;
1813 UInt32 latencySize =
sizeof (latencySecs);
1814 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
1815 0, &latencySecs, &latencySize);
1817 setLatencySamples (roundToInt (latencySecs * getSampleRate()));
1820 void handleIncomingMidiMessage (
void*,
const MidiMessage& message)
1823 incomingMidi.addEvent (message, 0);
1826 void handlePartialSysexMessage (
void*,
const uint8*,
int,
double) {}
1828 bool isMidiEffect()
const override {
return isMidiEffectPlugin; }
1832 friend class AudioUnitPluginWindowCocoa;
1833 friend class AudioUnitPluginFormat;
1835 CoreAudioTimeConversions timeConversions;
1837 AudioComponentDescription componentDesc;
1838 AudioComponent auComponent;
1839 String pluginName, manufacturer, version;
1840 String fileOrIdentifier;
1841 CriticalSection
lock;
1843 bool wantsMidiMessages =
false, producesMidiMessages =
false,
1844 wasPlaying =
false, prepared =
false,
1845 isAUv3 =
false, isMidiEffectPlugin =
false;
1849 AUBuffer (
size_t numBuffers)
1851 bufferList.calloc (1, (
sizeof (AudioBufferList) -
sizeof (::AudioBuffer)) + (
sizeof (::AudioBuffer) * numBuffers));
1852 AudioBufferList& buffer = *bufferList.get();
1854 buffer.mNumberBuffers =
static_cast<UInt32
> (numBuffers);
1857 operator AudioBufferList&()
1859 return *bufferList.get();
1862 HeapBlock<AudioBufferList> bufferList;
1866 struct AUBypassParameter final :
public Parameter
1868 AUBypassParameter (AudioUnitPluginInstance& effectToUse)
1869 : parent (effectToUse), currentValue (getCurrentHostValue())
1872 bool getCurrentHostValue()
1874 if (parent.auSupportsBypass)
1876 UInt32 dataSize =
sizeof (UInt32);
1879 if (AudioUnitGetProperty (parent.audioUnit, kAudioUnitProperty_BypassEffect,
1880 kAudioUnitScope_Global, 0, &value, &dataSize) == noErr
1881 && dataSize ==
sizeof (UInt32))
1888 float getValue()
const override
1890 return currentValue ? 1.0f : 0.0f;
1893 void setValue (
float newValue)
override
1895 auto newBypassValue = (newValue != 0.0f);
1899 if (newBypassValue != currentValue)
1901 currentValue = newBypassValue;
1903 if (parent.auSupportsBypass)
1905 UInt32 value = (newValue != 0.0f ? 1 : 0);
1906 AudioUnitSetProperty (parent.audioUnit, kAudioUnitProperty_BypassEffect,
1907 kAudioUnitScope_Global, 0, &value, sizeof (UInt32));
1910 jassert (parent.audioUnit !=
nullptr);
1913 ev.mEventType = kAudioUnitEvent_PropertyChange;
1914 ev.mArgument.mProperty.mAudioUnit = parent.audioUnit;
1915 ev.mArgument.mProperty.mPropertyID = kAudioUnitProperty_BypassEffect;
1916 ev.mArgument.mProperty.mScope = kAudioUnitScope_Global;
1917 ev.mArgument.mProperty.mElement = 0;
1919 AUEventListenerNotify (parent.eventListenerRef,
nullptr, &ev);
1925 float getValueForText (
const String& text)
const override
1927 String lowercaseText (text.toLowerCase());
1929 for (
auto& testText : auOnStrings)
1930 if (lowercaseText == testText)
1933 for (
auto& testText : auOffStrings)
1934 if (lowercaseText == testText)
1937 return text.getIntValue() != 0 ? 1.0f : 0.0f;
1940 float getDefaultValue()
const override {
return 0.0f; }
1941 String getName (
int )
const override {
return "Bypass"; }
1942 String getText (
float value,
int)
const override {
return (value != 0.0f ?
TRANS (
"On") :
TRANS (
"Off")); }
1943 bool isAutomatable()
const override {
return true; }
1944 bool isDiscrete()
const override {
return true; }
1945 bool isBoolean()
const override {
return true; }
1946 int getNumSteps()
const override {
return 2; }
1947 StringArray getAllValueStrings()
const override {
return values; }
1948 String getLabel()
const override {
return {}; }
1950 String getParameterID()
const override {
return {}; }
1952 AudioUnitPluginInstance& parent;
1953 const StringArray auOnStrings {
TRANS (
"on"),
TRANS (
"yes"),
TRANS (
"true") };
1954 const StringArray auOffStrings {
TRANS (
"off"),
TRANS (
"no"),
TRANS (
"false") };
1955 const StringArray values {
TRANS (
"Off"),
TRANS (
"On") };
1957 bool currentValue =
false;
1960 OwnedArray<AUBuffer> outputBufferList;
1961 AudioTimeStamp timeStamp;
1962 AudioBuffer<float> inputBuffer;
1963 Array<Array<AudioChannelSet>> supportedInLayouts, supportedOutLayouts;
1965 int numChannelInfos, preparedChannels = 0, preparedSamples = 0;
1966 HeapBlock<AUChannelInfo> channelInfos;
1968 AudioUnit audioUnit;
1970 AUEventListenerRef eventListenerRef;
1975 AudioUnitFormatHelpers::SingleDirectionChannelMapping inMapping, outMapping;
1976 MidiDataConcatenator midiConcatenator;
1977 CriticalSection midiInLock;
1978 MidiBuffer incomingMidi;
1980 bool lastProcessBlockCallWasBypass =
false, auSupportsBypass =
false;
1981 bool haveParameterList =
false;
1983 void setPluginCallbacks()
1985 if (audioUnit !=
nullptr)
1988 if (producesMidiMessages)
1990 AUMIDIOutputCallbackStruct info;
1993 info.userData =
this;
1994 info.midiOutputCallback = renderMidiOutputCallback;
1996 producesMidiMessages = (AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallback,
1997 kAudioUnitScope_Global, 0, &info,
sizeof (info)) == noErr);
2001 HostCallbackInfo info;
2004 info.hostUserData =
this;
2005 info.beatAndTempoProc = getBeatAndTempoCallback;
2006 info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
2007 info.transportStateProc = getTransportStateCallback;
2009 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks,
2010 kAudioUnitScope_Global, 0, &info,
sizeof (info));
2015 void disposeEventListener()
2017 if (eventListenerRef !=
nullptr)
2019 AUListenerDispose (eventListenerRef);
2020 eventListenerRef =
nullptr;
2024 void createEventListener()
2026 if (audioUnit ==
nullptr)
2029 disposeEventListener();
2031 AUEventListenerCreate (eventListenerCallback,
this, CFRunLoopGetMain(),
2032 kCFRunLoopDefaultMode, 0, 0, &eventListenerRef);
2034 for (
auto* param : getParameters())
2036 jassert (
dynamic_cast<AUInstanceParameter*
> (param) !=
nullptr);
2038 AudioUnitEvent event;
2039 event.mArgument.mParameter.mAudioUnit = audioUnit;
2040 event.mArgument.mParameter.mParameterID =
static_cast<AUInstanceParameter*
> (param)->getRawParamID();
2041 event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
2042 event.mArgument.mParameter.mElement = 0;
2044 event.mEventType = kAudioUnitEvent_ParameterValueChange;
2045 AUEventListenerAddEventType (eventListenerRef,
nullptr, &event);
2047 event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture;
2048 AUEventListenerAddEventType (eventListenerRef,
nullptr, &event);
2050 event.mEventType = kAudioUnitEvent_EndParameterChangeGesture;
2051 AUEventListenerAddEventType (eventListenerRef,
nullptr, &event);
2054 addPropertyChangeListener (kAudioUnitProperty_PresentPreset);
2055 addPropertyChangeListener (kAudioUnitProperty_ParameterList);
2056 addPropertyChangeListener (kAudioUnitProperty_Latency);
2057 addPropertyChangeListener (kAudioUnitProperty_BypassEffect);
2060 void addPropertyChangeListener (AudioUnitPropertyID type)
const
2062 AudioUnitEvent event;
2063 event.mEventType = kAudioUnitEvent_PropertyChange;
2064 event.mArgument.mProperty.mPropertyID = type;
2065 event.mArgument.mProperty.mAudioUnit = audioUnit;
2066 event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
2067 event.mArgument.mProperty.mElement = 0;
2068 AUEventListenerAddEventType (eventListenerRef,
nullptr, &event);
2071 void eventCallback (
const AudioUnitEvent& event, AudioUnitParameterValue newValue)
2073 if (event.mEventType == kAudioUnitEvent_PropertyChange)
2075 respondToPropertyChange (event.mArgument.mProperty);
2079 const auto iter = paramIDToParameter.
find (event.mArgument.mParameter.mParameterID);
2080 auto* param = iter != paramIDToParameter.
end() ? iter->second :
nullptr;
2083 if (param ==
nullptr)
2086 if (event.mEventType == kAudioUnitEvent_ParameterValueChange)
2087 param->sendValueChangedMessageToListeners (param->normaliseParamValue (newValue));
2088 else if (event.mEventType == kAudioUnitEvent_BeginParameterChangeGesture)
2089 param->beginChangeGesture();
2090 else if (event.mEventType == kAudioUnitEvent_EndParameterChangeGesture)
2091 param->endChangeGesture();
2094 void respondToPropertyChange (
const AudioUnitProperty& prop)
2096 switch (prop.mPropertyID)
2098 case kAudioUnitProperty_ParameterList:
2099 updateParameterInfo();
2100 updateHostDisplay (AudioProcessorListener::ChangeDetails().withParameterInfoChanged (
true));
2103 case kAudioUnitProperty_PresentPreset:
2104 sendAllParametersChangedEvents();
2105 updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (
true));
2108 case kAudioUnitProperty_Latency:
2112 case kAudioUnitProperty_BypassEffect:
2113 if (bypassParam !=
nullptr)
2114 bypassParam->setValueNotifyingHost (bypassParam->getValue());
2120 static void eventListenerCallback (
void* userRef,
void*,
const AudioUnitEvent* event,
2121 UInt64, AudioUnitParameterValue value)
2125 static_cast<AudioUnitPluginInstance*
> (userRef)->eventCallback (*event, value);
2128 void updateParameterInfo()
2130 for (
const auto& idAndParam : paramIDToParameter)
2132 const auto&
id = idAndParam.first;
2133 const auto& param = idAndParam.second;
2135 const ScopedAudioUnitParameterInfo info { audioUnit,
id };
2137 if (! info.isValid())
2140 param->setName (getParamName (info.get()));
2141 param->setLabel (getParamLabel (info.get()));
2149 class ScopedAudioUnitParameterInfo
2152 ScopedAudioUnitParameterInfo (AudioUnit au, UInt32 paramId)
2154 auto sz = (UInt32)
sizeof (info);
2155 valid = noErr == AudioUnitGetProperty (au,
2156 kAudioUnitProperty_ParameterInfo,
2157 kAudioUnitScope_Global,
2163 ScopedAudioUnitParameterInfo (
const ScopedAudioUnitParameterInfo&) =
delete;
2164 ScopedAudioUnitParameterInfo (ScopedAudioUnitParameterInfo&&) =
delete;
2165 ScopedAudioUnitParameterInfo& operator= (
const ScopedAudioUnitParameterInfo&) =
delete;
2166 ScopedAudioUnitParameterInfo& operator= (ScopedAudioUnitParameterInfo&&) =
delete;
2168 ~ScopedAudioUnitParameterInfo() noexcept
2170 if ((info.flags & kAudioUnitParameterFlag_CFNameRelease) == 0)
2173 if (info.cfNameString !=
nullptr)
2174 CFRelease (info.cfNameString);
2176 if (info.unit == kAudioUnitParameterUnit_CustomUnit && info.unitName !=
nullptr)
2177 CFRelease (info.unitName);
2180 bool isValid()
const {
return valid; }
2182 const AudioUnitParameterInfo&
get() const noexcept {
return info; }
2185 AudioUnitParameterInfo info;
2189 static String getParamName (
const AudioUnitParameterInfo& info)
2191 if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) == 0)
2192 return { info.name,
sizeof (info.name) };
2194 return String::fromCFString (info.cfNameString);
2197 static String getParamLabel (
const AudioUnitParameterInfo& info)
2199 if (info.unit == kAudioUnitParameterUnit_CustomUnit)
return String::fromCFString (info.unitName);
2200 if (info.unit == kAudioUnitParameterUnit_Percent)
return "%";
2201 if (info.unit == kAudioUnitParameterUnit_Seconds)
return "s";
2202 if (info.unit == kAudioUnitParameterUnit_Hertz)
return "Hz";
2203 if (info.unit == kAudioUnitParameterUnit_Decibels)
return "dB";
2204 if (info.unit == kAudioUnitParameterUnit_Milliseconds)
return "ms";
2210 OSStatus renderGetInput (AudioUnitRenderActionFlags*,
2211 const AudioTimeStamp*,
2213 UInt32 inNumberFrames,
2214 AudioBufferList* ioData)
2216 if (inputBuffer.getNumChannels() <= 0)
2223 if (inputBuffer.getNumSamples() != (
int) inNumberFrames)
2229 const auto buffer =
static_cast<int> (inBusNumber) < getBusCount (
true)
2230 ? getBusBuffer (inputBuffer,
true,
static_cast<int> (inBusNumber))
2231 : AudioBuffer<
float>();
2233 for (
int juceChannel = 0; juceChannel < buffer.getNumChannels(); ++juceChannel)
2235 const auto auChannel = (
int) inMapping.getAuIndexForJuceChannel (inBusNumber, (
size_t) juceChannel);
2237 if (auChannel < buffer.getNumChannels())
2238 memcpy (ioData->mBuffers[auChannel].mData, buffer.getReadPointer (juceChannel),
sizeof (
float) * inNumberFrames);
2240 zeromem (ioData->mBuffers[auChannel].mData, sizeof (
float) * inNumberFrames);
2246 OSStatus renderMidiOutput (
const MIDIPacketList* pktlist)
2248 if (pktlist !=
nullptr && pktlist->numPackets)
2250 auto time = Time::getMillisecondCounterHiRes() * 0.001;
2251 const MIDIPacket* packet = &pktlist->packet[0];
2253 for (UInt32 i = 0; i < pktlist->numPackets; ++i)
2255 midiConcatenator.pushMidiData (packet->data, (
int) packet->length, time, (
void*)
nullptr, *
this);
2256 packet = MIDIPacketNext (packet);
2263 template <
typename Type1,
typename Type2>
2264 static void setIfNotNull (Type1* p, Type2 value)
noexcept
2266 if (p !=
nullptr) *p = value;
2279 template <
typename Result>
2280 Result getFromPlayHead (Result (AudioPlayHead::PositionInfo::* member)() const) const
2282 if (
auto* ph = getPlayHead())
2283 if (
const auto pos = ph->getPosition())
2284 return ((*pos).*member)();
2289 OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo)
const
2291 setIfNotNull (outCurrentBeat, getFromPlayHead (&AudioPlayHead::PositionInfo::getPpqPosition).orFallback (0));
2292 setIfNotNull (outCurrentTempo, getFromPlayHead (&AudioPlayHead::PositionInfo::getBpm).orFallback (120.0));
2296 OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
2297 UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat)
const
2299 setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0);
2300 setIfNotNull (outCurrentMeasureDownBeat, getFromPlayHead (&AudioPlayHead::PositionInfo::getPpqPositionOfLastBarStart).orFallback (0.0));
2302 const auto signature = getFromPlayHead (&AudioPlayHead::PositionInfo::getTimeSignature).orFallback (AudioPlayHead::TimeSignature{});
2303 setIfNotNull (outTimeSig_Numerator, (Float32) signature.numerator);
2304 setIfNotNull (outTimeSig_Denominator, (UInt32) signature.denominator);
2309 OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
2310 Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
2311 Float64* outCycleStartBeat, Float64* outCycleEndBeat)
2313 const auto nowPlaying = getFromPlayHead (&AudioPlayHead::PositionInfo::getIsPlaying);
2314 setIfNotNull (outIsPlaying, nowPlaying);
2315 setIfNotNull (outTransportStateChanged,
std::exchange (wasPlaying, nowPlaying) != nowPlaying);
2316 setIfNotNull (outCurrentSampleInTimeLine, (
double) getFromPlayHead (&AudioPlayHead::PositionInfo::getTimeInSamples).orFallback (0));
2317 setIfNotNull (outIsCycling, getFromPlayHead (&AudioPlayHead::PositionInfo::getIsLooping));
2319 const auto loopPoints = getFromPlayHead (&AudioPlayHead::PositionInfo::getLoopPoints).orFallback (AudioPlayHead::LoopPoints{});
2320 setIfNotNull (outCycleStartBeat, loopPoints.ppqStart);
2321 setIfNotNull (outCycleEndBeat, loopPoints.ppqEnd);
2327 static OSStatus renderGetInputCallback (
void* hostRef, AudioUnitRenderActionFlags* ioActionFlags,
2328 const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
2329 UInt32 inNumberFrames, AudioBufferList* ioData)
2331 return static_cast<AudioUnitPluginInstance*
> (hostRef)
2332 ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
2335 static OSStatus renderMidiOutputCallback (
void* hostRef,
const AudioTimeStamp*, UInt32 ,
2336 const MIDIPacketList* pktlist)
2338 return static_cast<AudioUnitPluginInstance*
> (hostRef)->renderMidiOutput (pktlist);
2341 static OSStatus getBeatAndTempoCallback (
void* hostRef, Float64* outCurrentBeat, Float64* outCurrentTempo)
2343 return static_cast<AudioUnitPluginInstance*
> (hostRef)->getBeatAndTempo (outCurrentBeat, outCurrentTempo);
2346 static OSStatus getMusicalTimeLocationCallback (
void* hostRef, UInt32* outDeltaSampleOffsetToNextBeat,
2347 Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
2348 Float64* outCurrentMeasureDownBeat)
2350 return static_cast<AudioUnitPluginInstance*
> (hostRef)
2351 ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
2352 outTimeSig_Denominator, outCurrentMeasureDownBeat);
2355 static OSStatus getTransportStateCallback (
void* hostRef, Boolean* outIsPlaying, Boolean* outTransportStateChanged,
2356 Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
2357 Float64* outCycleStartBeat, Float64* outCycleEndBeat)
2359 return static_cast<AudioUnitPluginInstance*
> (hostRef)
2360 ->getTransportState (outIsPlaying, outTransportStateChanged, outCurrentSampleInTimeLine,
2361 outIsCycling, outCycleStartBeat, outCycleEndBeat);
2365 bool isBusCountWritable (
bool isInput)
const noexcept
2369 AudioUnitScope scope = (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output);
2371 auto err = AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_ElementCount, scope, 0, &countSize, &writable);
2373 return (err == noErr && writable != 0 && countSize ==
sizeof (UInt32));
2377 int getElementCount (AudioUnitScope scope)
const noexcept
2379 return static_cast<int> (AudioUnitFormatHelpers::getElementCount (audioUnit, scope));
2383 void getBusProperties (
bool isInput, UInt32 busIdx, String& busName, AudioChannelSet& currentLayout)
const
2385 getBusProperties (audioUnit, isInput, busIdx, busName, currentLayout);
2388 static void getBusProperties (AudioUnit comp,
bool isInput, UInt32 busIdx, String& busName, AudioChannelSet& currentLayout)
2390 const AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
2391 busName = (isInput ?
"Input #" :
"Output #") + String (busIdx + 1);
2394 CFObjectHolder<CFStringRef> busNameCF;
2395 UInt32 propertySize =
sizeof (busNameCF.object);
2397 if (AudioUnitGetProperty (comp, kAudioUnitProperty_ElementName, scope, busIdx, &busNameCF.object, &propertySize) == noErr)
2398 if (busNameCF.object !=
nullptr)
2399 busName = nsStringToJuce ((NSString*) busNameCF.object);
2402 AudioChannelLayout auLayout;
2403 propertySize =
sizeof (auLayout);
2405 if (AudioUnitGetProperty (comp, kAudioUnitProperty_AudioChannelLayout, scope, busIdx, &auLayout, &propertySize) == noErr)
2406 currentLayout = CoreAudioLayouts::fromCoreAudio (auLayout);
2409 if (currentLayout.isDisabled())
2411 AudioStreamBasicDescription descr;
2412 propertySize =
sizeof (descr);
2414 if (AudioUnitGetProperty (comp, kAudioUnitProperty_StreamFormat, scope, busIdx, &descr, &propertySize) == noErr)
2415 currentLayout = AudioChannelSet::canonicalChannelSet (
static_cast<int> (descr.mChannelsPerFrame));
2421 void numBusesChanged()
override
2423 updateSupportedLayouts();
2426 void updateSupportedLayouts()
2428 supportedInLayouts.clear();
2429 supportedOutLayouts.clear();
2430 numChannelInfos = 0;
2431 channelInfos.free();
2433 for (
int dir = 0; dir < 2; ++dir)
2435 const bool isInput = (dir == 0);
2436 const AudioUnitScope scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
2437 auto n = getElementCount (scope);
2439 for (
int busIdx = 0; busIdx < n; ++busIdx)
2441 Array<AudioChannelSet> supported;
2442 AudioChannelSet currentLayout;
2445 AudioChannelLayout auLayout;
2446 UInt32 propertySize =
sizeof (auLayout);
2448 if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, scope,
static_cast<UInt32
> (busIdx), &auLayout, &propertySize) == noErr)
2449 currentLayout = CoreAudioLayouts::fromCoreAudio (auLayout);
2452 if (currentLayout.isDisabled())
2454 AudioStreamBasicDescription descr;
2455 UInt32 propertySize =
sizeof (descr);
2457 if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope,
static_cast<UInt32
> (busIdx), &descr, &propertySize) == noErr)
2458 currentLayout = AudioChannelSet::canonicalChannelSet (
static_cast<int> (descr.mChannelsPerFrame));
2463 UInt32 propertySize = 0;
2466 if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_SupportedChannelLayoutTags, scope,
static_cast<UInt32
> (busIdx), &propertySize, &writable) == noErr
2467 && propertySize > 0)
2469 const size_t numElements = propertySize /
sizeof (AudioChannelLayoutTag);
2470 HeapBlock<AudioChannelLayoutTag> layoutTags (numElements);
2471 propertySize =
static_cast<UInt32
> (
sizeof (AudioChannelLayoutTag) * numElements);
2473 if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedChannelLayoutTags, scope,
2474 static_cast<UInt32
> (busIdx), layoutTags.get(), &propertySize) == noErr)
2476 for (
int j = 0; j < static_cast<int> (numElements); ++j)
2478 const AudioChannelLayoutTag tag = layoutTags[j];
2480 if (tag != kAudioChannelLayoutTag_UseChannelDescriptions)
2482 AudioChannelLayout caLayout;
2484 caLayout.mChannelLayoutTag = tag;
2485 supported.addIfNotAlreadyThere (CoreAudioLayouts::fromCoreAudio (caLayout));
2489 if (supported.size() > 0)
2490 supported.addIfNotAlreadyThere (currentLayout);
2495 (isInput ? supportedInLayouts : supportedOutLayouts).add (supported);
2500 UInt32 propertySize = 0;
2503 if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, &propertySize, &writable) == noErr
2504 && propertySize > 0)
2506 numChannelInfos = propertySize /
sizeof (AUChannelInfo);
2507 channelInfos.malloc (
static_cast<size_t> (numChannelInfos));
2508 propertySize =
static_cast<UInt32
> (
sizeof (AUChannelInfo) *
static_cast<size_t> (numChannelInfos));
2510 if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, channelInfos.get(), &propertySize) != noErr)
2511 numChannelInfos = 0;
2515 numChannelInfos = 1;
2516 channelInfos.malloc (
static_cast<size_t> (numChannelInfos));
2517 channelInfos.get()->inChannels = -1;
2518 channelInfos.get()->outChannels = -1;
2523 bool canProduceMidiOutput()
2526 UInt32 dataSize = 0;
2527 Boolean isWritable =
false;
2529 if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo,
2530 kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
2533 CFObjectHolder<CFArrayRef> midiArray;
2535 if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo,
2536 kAudioUnitScope_Global, 0, &midiArray.object, &dataSize) == noErr)
2537 return (CFArrayGetCount (midiArray.object) > 0);
2544 bool supportsMPE()
const override
2546 UInt32 dataSize = 0;
2547 Boolean isWritable =
false;
2549 if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_SupportsMPE,
2550 kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
2551 && dataSize ==
sizeof (UInt32))
2555 if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportsMPE,
2556 kAudioUnitScope_Global, 0, &result, &dataSize) == noErr)
2566 void updateBypass (
bool processBlockBypassedCalled)
2568 if (processBlockBypassedCalled && bypassParam !=
nullptr)
2570 if (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass)
2571 bypassParam->setValue (1.0f);
2575 if (lastProcessBlockCallWasBypass && bypassParam !=
nullptr)
2576 bypassParam->setValue (0.0f);
2579 lastProcessBlockCallWasBypass = processBlockBypassedCalled;
2586class AudioUnitPluginWindowCocoa final :
public AudioProcessorEditor
2589 AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& p,
bool createGenericViewIfNeeded)
2590 : AudioProcessorEditor (&p),
2593 addAndMakeVisible (wrapper);
2599 createView (createGenericViewIfNeeded);
2602 ~AudioUnitPluginWindowCocoa()
override
2604 if (wrapper.getView() != nil)
2606 wrapper.setVisible (
false);
2607 removeChildComponent (&wrapper);
2608 wrapper.setView (nil);
2609 plugin.editorBeingDeleted (
this);
2613 void embedViewController (JUCE_IOS_MAC_VIEW* pluginView, [[maybe_unused]]
const CGSize& size)
2615 wrapper.setView (pluginView);
2616 waitingForViewCallback =
false;
2619 if (pluginView != nil)
2620 wrapper.resizeToFitView();
2622 [pluginView setBounds: CGRectMake (0.f, 0.f,
static_cast<int> (
size.width),
static_cast<int> (
size.height))];
2623 wrapper.setSize (
static_cast<int> (
size.width),
static_cast<int> (
size.height));
2627 bool isValid()
const {
return wrapper.getView() != nil || waitingForViewCallback; }
2629 void paint (Graphics& g)
override
2631 g.fillAll (Colours::white);
2634 void resized()
override
2636 wrapper.setSize (getWidth(), getHeight());
2639 void childBoundsChanged (Component*)
override
2641 setSize (wrapper.getWidth(), wrapper.getHeight());
2646 AudioUnitPluginInstance& plugin;
2647 AudioUnitFormatHelpers::AutoResizingNSViewComponent wrapper;
2649 typedef void (^ViewControllerCallbackBlock)(AUViewControllerBase *);
2651 bool waitingForViewCallback =
false;
2653 bool createView ([[maybe_unused]]
bool createGenericViewIfNeeded)
2655 JUCE_IOS_MAC_VIEW* pluginView = nil;
2656 UInt32 dataSize = 0;
2657 Boolean isWritable =
false;
2660 if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
2661 0, &dataSize, &isWritable) == noErr
2663 && AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
2664 0, &dataSize, &isWritable) == noErr)
2666 HeapBlock<AudioUnitCocoaViewInfo> info;
2667 info.calloc (dataSize, 1);
2669 if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
2670 0, info, &dataSize) == noErr)
2672 NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]);
2673 CFUniquePtr<CFStringRef> path (CFURLCopyPath (info->mCocoaAUViewBundleLocation));
2674 NSString* unescapedPath = (NSString*) CFURLCreateStringByReplacingPercentEscapes (
nullptr, path.get(), CFSTR (
""));
2675 NSBundle* viewBundle = [NSBundle bundleWithPath: [unescapedPath autorelease]];
2676 Class viewClass = [viewBundle classNamed: viewClassName];
2678 if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)]
2679 && [viewClass instancesRespondToSelector: @selector (interfaceVersion)]
2680 && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)])
2682 id factory = [[[viewClass alloc] init] autorelease];
2683 pluginView = [factory uiViewForAudioUnit: plugin.audioUnit
2684 withSize: NSMakeSize (getWidth(), getHeight())];
2687 for (
int i = (dataSize -
sizeof (CFURLRef)) /
sizeof (CFStringRef); --i >= 0;)
2688 CFRelease (info->mCocoaAUViewClass[i]);
2690 CFRelease (info->mCocoaAUViewBundleLocation);
2698 if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global,
2699 0, &dataSize, &isWritable) == noErr
2700 && dataSize == sizeof (ViewControllerCallbackBlock))
2702 waitingForViewCallback =
true;
2703 auto callback = ^(AUViewControllerBase* controller) { this->requestViewControllerCallback (controller); };
2705 if (noErr == AudioUnitSetProperty (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, 0, &callback, dataSize))
2708 waitingForViewCallback =
false;
2712 if (createGenericViewIfNeeded && (pluginView == nil))
2716 AudioComponentDescription desc;
2717 String name, version, manufacturer;
2718 AudioUnitFormatHelpers::getComponentDescFromIdentifier (
"AudioUnit:Output/auou,genr,appl",
2719 desc, name, version, manufacturer);
2722 pluginView = [[AUGenericView alloc] initWithAudioUnit: plugin.audioUnit];
2726 wrapper.setView (pluginView);
2728 if (pluginView != nil)
2729 wrapper.resizeToFitView();
2731 return pluginView != nil;
2734 void requestViewControllerCallback (AUViewControllerBase* controller)
2736 const auto viewSize = [&controller]
2738 auto size = CGSizeZero;
2740 if (@available (macOS 10.11, *))
2741 size = [controller preferredContentSize];
2744 size = controller.view.frame.size;
2746 return CGSizeMake (jmax ((CGFloat) 20.0f,
size.width),
2747 jmax ((CGFloat) 20.0f,
size.height));
2750 if (! MessageManager::getInstance()->isThisTheMessageThread())
2752 struct AsyncViewControllerCallback final :
public CallbackMessage
2754 AudioUnitPluginWindowCocoa* owner;
2755 JUCE_IOS_MAC_VIEW* controllerView;
2758 AsyncViewControllerCallback (AudioUnitPluginWindowCocoa* plugInWindow, JUCE_IOS_MAC_VIEW* inView,
2759 const CGSize& preferredSize)
2760 : owner (plugInWindow), controllerView ([inView retain]),
size (preferredSize)
2763 void messageCallback()
override
2765 owner->embedViewController (controllerView, size);
2766 [controllerView release];
2770 (
new AsyncViewControllerCallback (
this, [controller view], viewSize))->post();
2774 embedViewController ([controller view], viewSize);
2780AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
2784 if (!
static_cast<AudioUnitPluginWindowCocoa*
> (w.get())->isValid())
2785 w.reset (
new AudioUnitPluginWindowCocoa (*
this,
true));
2791AudioUnitPluginFormat::AudioUnitPluginFormat()
2795AudioUnitPluginFormat::~AudioUnitPluginFormat()
2800 const String& fileOrIdentifier)
2805 PluginDescription desc;
2806 desc.fileOrIdentifier = fileOrIdentifier;
2807 desc.uniqueId = desc.deprecatedUid = 0;
2810 && requiresUnblockedMessageThreadDuringCreation (desc))
2817 if (
auto auInstance =
dynamic_cast<AudioUnitPluginInstance*
> (createdInstance.get()))
2818 results.add (
new PluginDescription (auInstance->getPluginDescription()));
2826void AudioUnitPluginFormat::createPluginInstance (
const PluginDescription& desc,
2827 double rate,
int blockSize,
2828 PluginCreationCallback callback)
2830 auto auComponentResult = getAudioComponent (*
this, desc);
2832 if (! auComponentResult.isValid())
2834 callback (
nullptr, std::move (auComponentResult.errorMessage));
2838 createAudioUnit (auComponentResult.component,
2839 [rate, blockSize, origCallback = std::move (callback)] (AudioUnit audioUnit, OSStatus err)
2843 auto instance = std::make_unique<AudioUnitPluginInstance> (audioUnit);
2845 if (instance->initialise (rate, blockSize))
2846 origCallback (std::move (instance), {});
2848 origCallback (nullptr, NEEDS_TRANS (
"Unable to initialise the AudioUnit plug-in"));
2852 auto errMsg = TRANS (
"An OS error occurred during initialisation of the plug-in (XXX)");
2853 origCallback (nullptr, errMsg.replace (
"XXX", String (err)));
2858void AudioUnitPluginFormat::createARAFactoryAsync (
const PluginDescription& desc, ARAFactoryCreationCallback callback)
2860 auto auComponentResult = getAudioComponent (*
this, desc);
2862 if (! auComponentResult.isValid())
2864 callback ({ {},
"Failed to create AudioComponent for " + desc.descriptiveName });
2868 getOrCreateARAAudioUnit (auComponentResult.component, [cb = std::move (callback)] (
auto dylibKeepAliveAudioUnit)
2870 cb ([&]() -> ARAFactoryResult
2872 if (dylibKeepAliveAudioUnit != nullptr)
2873 return { ARAFactoryWrapper { ::juce::getARAFactory (std::move (dylibKeepAliveAudioUnit)) },
"" };
2875 return { {},
"Failed to create ARAFactory from the provided AudioUnit" };
2880bool AudioUnitPluginFormat::requiresUnblockedMessageThreadDuringCreation (
const PluginDescription& desc)
const
2882 String pluginName, version, manufacturer;
2883 AudioComponentDescription componentDesc;
2885 if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (desc.fileOrIdentifier, componentDesc,
2886 pluginName, version, manufacturer)
2887 || AudioUnitFormatHelpers::getComponentDescFromFile (desc.fileOrIdentifier, componentDesc,
2888 pluginName, version, manufacturer))
2890 if (AudioComponent auComp = AudioComponentFindNext (
nullptr, &componentDesc))
2892 if (AudioComponentGetDescription (auComp, &componentDesc) == noErr)
2893 return AudioUnitFormatHelpers::isPluginAUv3 (componentDesc);
2900StringArray AudioUnitPluginFormat::searchPathsForPlugins (
const FileSearchPath&,
bool ,
bool allowPluginsWhichRequireAsynchronousInstantiation)
2903 AudioComponent comp =
nullptr;
2907 AudioComponentDescription desc;
2910 comp = AudioComponentFindNext (comp, &desc);
2912 if (comp ==
nullptr)
2915 if (AudioComponentGetDescription (comp, &desc) != noErr)
2918 if (desc.componentType == kAudioUnitType_MusicDevice
2919 || desc.componentType == kAudioUnitType_MusicEffect
2920 || desc.componentType == kAudioUnitType_Effect
2921 || desc.componentType == kAudioUnitType_Generator
2922 || desc.componentType == kAudioUnitType_Panner
2923 || desc.componentType == kAudioUnitType_Mixer
2924 || desc.componentType == kAudioUnitType_MIDIProcessor)
2926 if (allowPluginsWhichRequireAsynchronousInstantiation || ! AudioUnitFormatHelpers::isPluginAUv3 (desc))
2927 result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc));
2934bool AudioUnitPluginFormat::fileMightContainThisPluginType (
const String& fileOrIdentifier)
2936 AudioComponentDescription desc;
2937 String name, version, manufacturer;
2939 if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer))
2940 return AudioComponentFindNext (
nullptr, &desc) !=
nullptr;
2942 auto f = File::createFileWithoutCheckingPath (fileOrIdentifier);
2944 return (f.hasFileExtension (
".component") || f.hasFileExtension (
".appex"))
2948String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (
const String& fileOrIdentifier)
2950 AudioComponentDescription desc;
2951 String name, version, manufacturer;
2952 AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer);
2955 name = fileOrIdentifier;
2960bool AudioUnitPluginFormat::pluginNeedsRescanning (
const PluginDescription& desc)
2962 AudioComponentDescription newDesc;
2963 String name, version, manufacturer;
2965 return ! (AudioUnitFormatHelpers::getComponentDescFromIdentifier (desc.fileOrIdentifier, newDesc,
2966 name, version, manufacturer)
2967 && version == desc.version);
2970bool AudioUnitPluginFormat::doesPluginStillExist (
const PluginDescription& desc)
2972 if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix))
2973 return fileMightContainThisPluginType (desc.fileOrIdentifier);
2975 return File (desc.fileOrIdentifier).exists();
2978FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
2987JUCE_END_IGNORE_WARNINGS_GCC_LIKE
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
#define NEEDS_TRANS(stringLiteral)
A dummy version of the TRANS macro, used to indicate a string literal that should be added to the tra...
#define JUCE_ASSERT_MESSAGE_THREAD
This macro is used to catch unsafe use of functions which expect to only be called on the message thr...
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
void createARAFactoryAsync(AudioPluginInstance &instance, std::function< void(ARAFactoryWrapper)> cb)
Calls the provided callback with an ARAFactoryWrapper object obtained from the provided AudioPluginIn...
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.