JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_AudioUnitPluginFormat.mm
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)
27
28JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
29
30#if JUCE_MAC
31#include <AudioUnit/AUCocoaUIView.h>
32#include <CoreAudioKit/AUGenericView.h>
33#include <AudioToolbox/AudioUnitUtilities.h>
34
35#if JUCE_PLUGINHOST_ARA
36 #include <ARA_API/ARAAudioUnit.h>
37#endif
38
39#endif
40
41#include <CoreMIDI/MIDIServices.h>
42
43#include <CoreAudioKit/AUViewController.h>
44
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"
48
49namespace juce
50{
51
52// Change this to disable logging of various activities
53#ifndef AU_LOGGING
54 #define AU_LOGGING 1
55#endif
56
57#if AU_LOGGING
58 #define JUCE_AU_LOG(a) Logger::writeToLog(a);
59#else
60 #define JUCE_AU_LOG(a)
61#endif
62
64{
65 #if JUCE_DEBUG
67 #endif
68
69 static String osTypeToString (OSType type) noexcept
70 {
71 const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff),
72 (juce_wchar) ((type >> 16) & 0xff),
73 (juce_wchar) ((type >> 8) & 0xff),
74 (juce_wchar) (type & 0xff) };
75 return String (s, 4);
76 }
77
78 static OSType stringToOSType (String s)
79 {
80 if (s.trim().length() >= 4) // (to avoid trimming leading spaces)
81 s = s.trim();
82
83 s += " ";
84
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]);
89 }
90
91 static const char* auIdentifierPrefix = "AudioUnit:";
92
93 static String createPluginIdentifier (const AudioComponentDescription& desc)
94 {
95 String s (auIdentifierPrefix);
96
97 if (desc.componentType == kAudioUnitType_MusicDevice)
98 s << "Synths/";
99 else if (desc.componentType == kAudioUnitType_MusicEffect
100 || desc.componentType == kAudioUnitType_Effect)
101 s << "Effects/";
102 else if (desc.componentType == kAudioUnitType_Generator)
103 s << "Generators/";
104 else if (desc.componentType == kAudioUnitType_Panner)
105 s << "Panners/";
106 else if (desc.componentType == kAudioUnitType_Mixer)
107 s << "Mixers/";
108 else if (desc.componentType == kAudioUnitType_MIDIProcessor)
109 s << "MidiEffects/";
110
111 s << osTypeToString (desc.componentType) << ","
112 << osTypeToString (desc.componentSubType) << ","
113 << osTypeToString (desc.componentManufacturer);
114
115 return s;
116 }
117
118 static void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer)
119 {
121
122 if (AudioComponentCopyName (comp, &cfName.object) == noErr)
123 name = String::fromCFString (cfName.object);
124
125 if (name.containsChar (':'))
126 {
127 manufacturer = name.upToFirstOccurrenceOf (":", false, false).trim();
128 name = name.fromFirstOccurrenceOf (":", false, false).trim();
129 }
130
131 if (name.isEmpty())
132 name = "<Unknown>";
133 }
134
135 static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, AudioComponentDescription& desc,
136 String& name, String& version, String& manufacturer)
137 {
138 if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
139 {
140 String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'),
141 fileOrIdentifier.lastIndexOfChar ('/')) + 1));
142
143 StringArray tokens;
144 tokens.addTokens (s, ",", StringRef());
145 tokens.removeEmptyStrings();
146
147 if (tokens.size() == 3)
148 {
149 zerostruct (desc);
150 desc.componentType = stringToOSType (tokens[0]);
151 desc.componentSubType = stringToOSType (tokens[1]);
152 desc.componentManufacturer = stringToOSType (tokens[2]);
153
154 if (AudioComponent comp = AudioComponentFindNext (nullptr, &desc))
155 {
156 getNameAndManufacturer (comp, name, manufacturer);
157
158 if (manufacturer.isEmpty())
159 manufacturer = tokens[2];
160
161 if (version.isEmpty())
162 {
164
166 {
167 version << (int) (versionNum >> 16) << "."
168 << (int) ((versionNum >> 8) & 0xff) << "."
169 << (int) (versionNum & 0xff);
170 }
171 }
172
173 return true;
174 }
175 }
176 }
177
178 return false;
179 }
180
181 static bool getComponentDescFromFile ([[maybe_unused]] const String& fileOrIdentifier,
183 [[maybe_unused]] String& name,
184 [[maybe_unused]] String& version,
185 [[maybe_unused]] String& manufacturer)
186 {
187 zerostruct (desc);
188
189 #if JUCE_IOS
190 return false;
191 #else
192 const File file (fileOrIdentifier);
193 if (! file.hasFileExtension (".component") && ! file.hasFileExtension (".appex"))
194 return false;
195
196 const char* const utf8 = fileOrIdentifier.toUTF8();
197
199 (CFIndex) strlen (utf8), file.isDirectory())))
200 {
202 {
204
205 if (bundleName != nullptr && CFGetTypeID (bundleName) == CFStringGetTypeID())
207
208 if (name.isEmpty())
209 name = file.getFileNameWithoutExtension();
210
212
215
216 CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef.get(), CFSTR ("CFBundleGetInfoString"));
217
218 if (manuString != nullptr && CFGetTypeID (manuString) == CFStringGetTypeID())
220
222 {
223 public:
224 explicit ScopedBundleResourceMap (CFBundleRef refIn) : ref (refIn),
225 resFileId (CFBundleOpenBundleResourceMap (ref)),
226 valid (resFileId != -1)
227 {
228 if (valid)
229 UseResFile (resFileId);
230 }
231
232 ~ScopedBundleResourceMap()
233 {
234 if (valid)
235 CFBundleCloseBundleResourceMap (ref, resFileId);
236 }
237
238 bool isValid() const noexcept
239 {
240 return valid;
241 }
242
243 private:
244 const CFBundleRef ref;
245 const ResFileRefNum resFileId;
246 const bool valid;
247 };
248
250
251 const OSType thngType = stringToOSType ("thng");
253
254 if (resourceMap.isValid() && numResources > 0)
255 {
256 for (ResourceIndex i = 1; i <= numResources; ++i)
257 {
258 if (Handle h = Get1IndResource (thngType, i))
259 {
260 HLock (h);
261 uint32 types[3];
262 std::memcpy (types, *h, sizeof (types));
263
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)
271 {
272 desc.componentType = types[0];
273 desc.componentSubType = types[1];
274 desc.componentManufacturer = types[2];
275
276 if (AudioComponent comp = AudioComponentFindNext (nullptr, &desc))
277 getNameAndManufacturer (comp, name, manufacturer);
278
279 break;
280 }
281
282 HUnlock (h);
283 ReleaseResource (h);
284 }
285 }
286 }
287 else
288 {
289 NSBundle* bundle = [[NSBundle alloc] initWithPath: (NSString*) fileOrIdentifier.toCFString()];
290
291 NSArray* audioComponents = [bundle objectForInfoDictionaryKey: @"AudioComponents"];
293
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"]));
297
298 [bundle release];
299 }
300 }
301 }
302
303 return desc.componentType != 0 && desc.componentSubType != 0;
304 #endif
305 }
306
307 static const char* getCategory (OSType type) noexcept
308 {
309 switch (type)
310 {
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";
318 default: break;
319 }
320
321 return nullptr;
322 }
323
324 // Audio Unit plugins expect hosts to listen to their view bounds, and to resize
325 // the plugin window/view appropriately.
326 #if JUCE_MAC || JUCE_IOS
327 #if JUCE_IOS
328 #define JUCE_IOS_MAC_VIEW UIView
329 using ViewComponentBaseClass = UIViewComponent;
330 #else
331 #define JUCE_IOS_MAC_VIEW NSView
332 using ViewComponentBaseClass = NSViewComponent;
333 #endif
334
336 private AsyncUpdater
337 {
338 void childBoundsChanged (Component*) override { triggerAsyncUpdate(); }
339 void handleAsyncUpdate() override { resizeToFitView(); }
340 };
341 #endif
342
343 template <typename Value>
344 static Optional<Value> tryGetProperty (AudioUnit inUnit,
348 {
349 Value data;
350 auto size = (UInt32) sizeof (Value);
351
352 if (AudioUnitGetProperty (inUnit, inID, inScope, inElement, &data, &size) == noErr)
353 return data;
354
355 return {};
356 }
357
358 static UInt32 getElementCount (AudioUnit comp, AudioUnitScope scope) noexcept
359 {
360 const auto count = tryGetProperty<UInt32> (comp, kAudioUnitProperty_ElementCount, scope, 0);
361 jassert (count);
362 return *count;
363 }
364
365 /* The plugin may expect its channels in a particular order, reported to the host
366 using kAudioUnitProperty_AudioChannelLayout.
367 This remapper allows us to respect the channel order requested by the plugin,
368 while still using the JUCE channel ordering for the AudioBuffer argument
369 of AudioProcessor::processBlock.
370 */
372 {
373 public:
374 void setUpMapping (AudioUnit comp, bool isInput)
375 {
376 channels.clear();
377 busOffset.clear();
378
379 const auto scope = isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output;
380 const auto n = getElementCount (comp, scope);
381
382 for (UInt32 busIndex = 0; busIndex < n; ++busIndex)
383 {
384 std::vector<size_t> busMap;
385
386 if (const auto layout = tryGetProperty<AudioChannelLayout> (comp, kAudioUnitProperty_AudioChannelLayout, scope, busIndex))
387 {
388 const auto juceChannelOrder = CoreAudioLayouts::fromCoreAudio (*layout);
389 const auto auChannelOrder = CoreAudioLayouts::getCoreAudioLayoutChannels (*layout);
390
391 for (auto juceChannelIndex = 0; juceChannelIndex < juceChannelOrder.size(); ++juceChannelIndex)
392 busMap.push_back ((size_t) auChannelOrder.indexOf (juceChannelOrder.getTypeOfChannel (juceChannelIndex)));
393 }
394
395 busOffset.push_back (busMap.empty() ? unknownChannelCount : channels.size());
396 channels.insert (channels.end(), busMap.begin(), busMap.end());
397 }
398 }
399
400 size_t getAuIndexForJuceChannel (size_t bus, size_t channel) const noexcept
401 {
402 const auto baseOffset = busOffset[bus];
403 return baseOffset != unknownChannelCount ? channels[baseOffset + channel]
404 : channel;
405 }
406
407 private:
408 static constexpr size_t unknownChannelCount = std::numeric_limits<size_t>::max();
409
410 /* The index (in the channels vector) of the first channel in each bus.
411 e.g the index of the first channel in the second bus can be found at busOffset[1].
412 It's possible for a bus not to report its channel layout, and in this case a value
413 of unknownChannelCount will be stored for that bus.
414 */
415 std::vector<size_t> busOffset;
416
417 /* The index in a collection of JUCE channels of the AU channel with a matching channel
418 type. The mappings for all buses are stored in bus order. To find the start offset for a
419 particular bus, use the busOffset vector.
420 e.g. the index of the AU channel with the same type as the fifth channel of the third bus
421 in JUCE layout is found at channels[busOffset[2] + 4].
422 If the busOffset for the bus is unknownChannelCount, then assume there is no mapping
423 between JUCE/AU channel layouts.
424 */
425 std::vector<size_t> channels;
426 };
427
428 static bool isPluginAUv3 (const AudioComponentDescription& desc)
429 {
430 if (@available (macOS 10.11, *))
431 return (desc.componentFlags & kAudioComponentFlag_IsV3AudioUnit) != 0;
432
433 return false;
434 }
435}
436
437static bool hasARAExtension ([[maybe_unused]] AudioUnit audioUnit)
438{
439 #if JUCE_PLUGINHOST_ARA
440 UInt32 propertySize = sizeof (ARA::ARAAudioUnitFactory);
441 Boolean isWriteable = FALSE;
442
444 ARA::kAudioUnitProperty_ARAFactory,
446 0,
448 &isWriteable);
449
450 if ((status == noErr) && (propertySize == sizeof (ARA::ARAAudioUnitFactory)) && ! isWriteable)
451 return true;
452 #endif
453
454 return false;
455}
456
457struct AudioUnitDeleter
458{
459 void operator() (AudioUnit au) const { AudioComponentInstanceDispose (au); }
460};
461
465
467{
468 #if JUCE_PLUGINHOST_ARA
469 jassert (audioUnit != nullptr);
470
471 UInt32 propertySize = sizeof (ARA::ARAAudioUnitFactory);
472 ARA::ARAAudioUnitFactory audioUnitFactory { ARA::kARAAudioUnitMagic, nullptr };
473
474 if (hasARAExtension (audioUnit.get()))
475 {
477 ARA::kAudioUnitProperty_ARAFactory,
479 0,
481 &propertySize);
482
483 if ((status == noErr)
484 && (propertySize == sizeof (ARA::ARAAudioUnitFactory))
485 && (audioUnitFactory.inOutMagicNumber == ARA::kARAAudioUnitMagic))
486 {
487 jassert (audioUnitFactory.outFactory != nullptr);
488 return getOrCreateARAFactory (audioUnitFactory.outFactory,
489 [owningAuPtr = std::move (audioUnit)]() {});
490 }
491 }
492 #endif
493
494 return {};
495}
496
498{
500 bool isAUv3 = false;
501
502 bool operator< (const VersionedAudioComponent& other) const { return audioComponent < other.audioComponent; }
503};
504
506
508{
509 if (versionedComponent.isAUv3)
510 {
511 if (@available (macOS 10.11, *))
512 {
515 ^(AudioComponentInstance audioUnit, OSStatus err)
516 {
517 callback (audioUnit, err);
518 });
519
520 return;
521 }
522 }
523
524 AudioComponentInstance audioUnit;
526 callback (err != noErr ? nullptr : audioUnit, err);
527}
528
530{
531 explicit AudioComponentResult (String error) : errorMessage (std::move (error)) {}
532
534
535 bool isValid() const { return component.audioComponent != nullptr; }
536
537 VersionedAudioComponent component;
538 String errorMessage;
539};
540
541static AudioComponentResult getAudioComponent (AudioUnitPluginFormat& format, const PluginDescription& desc)
542{
543 using namespace AudioUnitFormatHelpers;
544
545 AudioUnitPluginFormat audioUnitPluginFormat;
546
547 if (! format.fileMightContainThisPluginType (desc.fileOrIdentifier))
548 return AudioComponentResult { NEEDS_TRANS ("Plug-in description is not an AudioUnit plug-in") };
549
550 String pluginName, version, manufacturer;
553 String errMessage = NEEDS_TRANS ("Cannot find AudioUnit from description");
554
555 if (! getComponentDescFromIdentifier (desc.fileOrIdentifier, componentDesc, pluginName, version, manufacturer)
556 && ! getComponentDescFromFile (desc.fileOrIdentifier, componentDesc, pluginName, version, manufacturer))
557 {
559 }
560
561 if ((auComponent = AudioComponentFindNext (nullptr, &componentDesc)) == nullptr)
562 {
564 }
565
567 {
569 }
570
571 const bool isAUv3 = AudioUnitFormatHelpers::isPluginAUv3 (componentDesc);
572
574}
575
577{
579
581 {
582 callback (std::move (audioUnit));
583 return;
584 }
585
586 createAudioUnit (auComponent, [auComponent, cb = std::move (callback)] (AudioUnit audioUnit, OSStatus err)
587 {
589 {
590 if (err != noErr)
591 return nullptr;
592
595 return auPtr;
596 }());
597 });
598}
599
600//==============================================================================
602
603//==============================================================================
604class AudioUnitPluginInstance final : public AudioPluginInstance
605{
606public:
607 struct AUInstanceParameter final : public Parameter
608 {
609 AUInstanceParameter (AudioUnitPluginInstance& parent,
610 UInt32 parameterID,
611 const String& parameterName,
612 AudioUnitParameterValue minParameterValue,
613 AudioUnitParameterValue maxParameterValue,
614 AudioUnitParameterValue defaultParameterValue,
615 bool parameterIsAutomatable,
616 bool parameterIsDiscrete,
617 int numParameterSteps,
618 bool isBoolean,
619 const String& label,
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),
632 valueLabel (label),
633 defaultValue (normaliseParamValue (defaultParameterValue))
634 {
635 auValueStrings = Parameter::getAllValueStrings();
636 }
637
638 float getValue() const override
639 {
640 const ScopedLock sl (pluginInstance.lock);
641
642 AudioUnitParameterValue value = 0;
643
644 if (auto* au = pluginInstance.audioUnit)
645 {
646 AudioUnitGetParameter (au, paramID, kAudioUnitScope_Global, 0, &value);
647 value = normaliseParamValue (value);
648 }
649
650 return value;
651 }
652
653 void setValue (float newValue) override
654 {
655 const ScopedLock sl (pluginInstance.lock);
656
657 if (auto* au = pluginInstance.audioUnit)
658 {
659 AudioUnitSetParameter (au, paramID, kAudioUnitScope_Global,
660 0, scaleParamValue (newValue), 0);
661
662 sendParameterChangeEvent();
663 }
664 }
665
666 float getDefaultValue() const override
667 {
668 return defaultValue;
669 }
670
671 String getName (int /*maximumStringLength*/) const override { return name; }
672 String getLabel() const override { return valueLabel; }
673
674 String getText (float value, int maximumLength) const override
675 {
676 if (! auValueStrings.isEmpty())
677 {
678 auto index = roundToInt (jlimit (0.0f, 1.0f, value) * (float) (auValueStrings.size() - 1));
679 return auValueStrings[index];
680 }
681
682 auto scaledValue = scaleParamValue (value);
683
684 if (valuesHaveStrings)
685 {
686 if (auto* au = pluginInstance.audioUnit)
687 {
688 AudioUnitParameterStringFromValue stringValue;
689 stringValue.inParamID = paramID;
690 stringValue.inValue = &scaledValue;
691 stringValue.outString = nullptr;
692
693 UInt32 propertySize = sizeof (stringValue);
694
695 auto err = AudioUnitGetProperty (au,
696 kAudioUnitProperty_ParameterStringFromValue,
697 kAudioUnitScope_Global,
698 0,
699 &stringValue,
700 &propertySize);
701
702 if (! err && stringValue.outString != nullptr)
703 return String::fromCFString (stringValue.outString).substring (0, maximumLength);
704 }
705 }
706
707 return Parameter::getText (scaledValue, maximumLength);
708 }
709
710 float getValueForText (const String& text) const override
711 {
712 if (! auValueStrings.isEmpty())
713 {
714 auto index = auValueStrings.indexOf (text);
715
716 if (index != -1)
717 return ((float) index) / (float) (auValueStrings.size() - 1);
718 }
719
720 if (valuesHaveStrings)
721 {
722 if (auto* au = pluginInstance.audioUnit)
723 {
724 AudioUnitParameterValueFromString pvfs;
725 pvfs.inParamID = paramID;
726 CFUniquePtr<CFStringRef> cfString (text.toCFString());
727 pvfs.inString = cfString.get();
728 UInt32 propertySize = sizeof (pvfs);
729
730 auto err = AudioUnitGetProperty (au,
731 kAudioUnitProperty_ParameterValueFromString,
732 kAudioUnitScope_Global,
733 0,
734 &pvfs,
735 &propertySize);
736
737 if (! err)
738 return normaliseParamValue (pvfs.outValue);
739 }
740 }
741
742 return Parameter::getValueForText (text);
743 }
744
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; }
749
750 StringArray getAllValueStrings() const override
751 {
752 return auValueStrings;
753 }
754
755 String getParameterID() const override
756 {
757 return String (paramID);
758 }
759
760 void sendParameterChangeEvent()
761 {
762 #if JUCE_MAC
763 jassert (pluginInstance.audioUnit != nullptr);
764
765 AudioUnitEvent ev;
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;
771
772 AUEventListenerNotify (pluginInstance.eventListenerRef, nullptr, &ev);
773 #endif
774 }
775
776 float normaliseParamValue (float scaledValue) const noexcept
777 {
778 return (scaledValue - minValue) / range;
779 }
780
781 float scaleParamValue (float normalisedValue) const noexcept
782 {
783 return minValue + (range * normalisedValue);
784 }
785
786 UInt32 getRawParamID() const { return paramID; }
787
788 void setName (String&& newName) { name = std::move (newName); }
789 void setLabel (String&& newLabel) { valueLabel = std::move (newLabel); }
790
791 private:
792 AudioUnitPluginInstance& pluginInstance;
793 const UInt32 paramID;
794 String name;
795 const AudioUnitParameterValue minValue, maxValue, range;
796 const bool automatable, discrete;
797 const int numSteps;
798 const bool valuesHaveStrings, isSwitch;
799 String valueLabel;
800 const AudioUnitParameterValue defaultValue;
801 StringArray auValueStrings;
802 };
803
804 AudioUnitPluginInstance (AudioComponentInstance au)
805 : AudioPluginInstance (getBusesProperties (au)),
807 audioUnit (au),
808 #if JUCE_MAC
810 #endif
811 midiConcatenator (2048)
812 {
813 using namespace AudioUnitFormatHelpers;
814
816
817 isAUv3 = AudioUnitFormatHelpers::isPluginAUv3 (componentDesc);
818
820 || componentDesc.componentType == kAudioUnitType_MusicEffect
822
827 }
828
829 ~AudioUnitPluginInstance() override
830 {
831 const ScopedLock sl (lock);
832
833 #if JUCE_DEBUG
834 // this indicates that some kind of recursive call is getting triggered that's
835 // deleting this plugin while it's still under construction.
836 jassert (AudioUnitFormatHelpers::insideCallback.get() == 0);
837 #endif
838
839 if (audioUnit != nullptr)
840 {
841 struct AUDeleter final : public CallbackMessage
842 {
843 AUDeleter (AudioUnitPluginInstance& inInstance, WaitableEvent& inEvent)
844 : auInstance (inInstance), completionSignal (inEvent)
845 {}
846
847 void messageCallback() override
848 {
849 auInstance.cleanup();
850 completionSignal.signal();
851 }
852
853 AudioUnitPluginInstance& auInstance;
854 WaitableEvent& completionSignal;
855 };
856
857 if (MessageManager::getInstance()->isThisTheMessageThread())
858 {
859 cleanup();
860 }
861 else
862 {
863 WaitableEvent completionEvent;
864 (new AUDeleter (*this, completionEvent))->post();
865 completionEvent.wait();
866 }
867 }
868 }
869
870 // called from the destructor above
871 void cleanup()
872 {
873 #if JUCE_MAC
875 #endif
876
877 if (prepared)
878 releaseResources();
879
881 audioUnit = nullptr;
882 }
883
884 bool initialise (double rate, int blockSize)
885 {
887 setRateAndBufferSizeDetails (rate, blockSize);
888 setLatencySamples (0);
889 refreshParameterList();
891
892 #if JUCE_MAC
894 #endif
895
896 return true;
897 }
898
899 //==============================================================================
900 bool canAddBus (bool isInput) const override { return isBusCountWritable (isInput); }
901 bool canRemoveBus (bool isInput) const override { return isBusCountWritable (isInput); }
902
903 bool canApplyBusCountChange (bool isInput, bool isAdding, BusProperties& outProperties) override
904 {
905 auto currentCount = (UInt32) getBusCount (isInput);
906 auto newCount = (UInt32) ((int) currentCount + (isAdding ? 1 : -1));
908
910 {
911 getBusProperties (isInput, currentCount, outProperties.busName, outProperties.defaultLayout);
912 outProperties.isActivatedByDefault = true;
914
915 return true;
916 }
917
918 return false;
919 }
920
921 //==============================================================================
922 bool isBusesLayoutSupported (const BusesLayout& layouts) const override
923 {
924 if (layouts == getBusesLayout())
925 return true;
926
927 for (int dir = 0; dir < 2; ++dir)
928 {
929 const bool isInput = (dir == 0);
930 auto& requestedLayouts = (isInput ? layouts.inputBuses : layouts.outputBuses);
931 auto& oppositeRequestedLayouts = (isInput ? layouts.outputBuses : layouts.inputBuses);
933 const int n = getBusCount (isInput);
934
935 for (int busIdx = 0; busIdx < n; ++busIdx)
936 {
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);
942
943 if (requested.isDisabled())
944 return false;
945
946 if (possible.size() > 0 && ! possible.contains (requested))
947 return false;
948
949 int i;
950 for (i = 0; i < numChannelInfos; ++i)
951 {
952 auto& info = channelInfos[i];
953 auto& thisChannels = (isInput ? info.inChannels : info.outChannels);
954 auto& opChannels = (isInput ? info.outChannels : info.inChannels);
955
956 // this bus
957 if (thisChannels == 0) continue;
958 else if (thisChannels > 0 && requested.size() != thisChannels) continue;
959 else if (thisChannels < -2 && requested.size() > (thisChannels * -1)) continue;
960
961 // opposite bus
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;
965
966 // both buses
967 if (thisChannels == -2 && opChannels == -2) continue;
968 if (thisChannels == -1 && opChannels == -1)
969 {
970 int numOppositeBuses = getBusCount (! isInput);
971 int j;
972
973 for (j = 0; j < numOppositeBuses; ++j)
974 if (requested.size() != oppositeRequestedLayouts.getReference (j).size())
975 break;
976
977 if (j < numOppositeBuses)
978 continue;
979 }
980
981 break;
982 }
983
984 if (i >= numChannelInfos)
985 return false;
986 }
987 }
988
989 return true;
990 }
991
992 bool syncBusLayouts (const BusesLayout& layouts, bool isInitialized, bool& layoutHasChanged) const
993 {
994 layoutHasChanged = false;
995
996 for (int dir = 0; dir < 2; ++dir)
997 {
998 const bool isInput = (dir == 0);
1000 const int n = getBusCount (isInput);
1001
1002 if (getElementCount (scope) != n && isBusCountWritable (isInput))
1003 {
1004 auto newCount = static_cast<UInt32> (n);
1005 layoutHasChanged = true;
1006
1008 jassert (err == noErr);
1009 }
1010
1011 for (int i = 0; i < n; ++i)
1012 {
1013 Float64 sampleRate;
1014 UInt32 sampleRateSize = sizeof (sampleRate);
1015
1016 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope, static_cast<UInt32> (i), &sampleRate, &sampleRateSize);
1017
1018 const AudioChannelSet& set = layouts.getChannelSet (isInput, i);
1019 const int requestedNumChannels = set.size();
1020
1021 {
1023 UInt32 dataSize = sizeof (stream);
1024 auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope, static_cast<UInt32> (i), &stream, &dataSize);
1025
1026 if (err != noErr || dataSize < sizeof (stream))
1027 return false;
1028
1029 const int actualNumChannels = static_cast<int> (stream.mChannelsPerFrame);
1030
1032 {
1033 layoutHasChanged = true;
1034 zerostruct (stream); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
1035 stream.mSampleRate = sampleRate;
1036 stream.mFormatID = kAudioFormatLinearPCM;
1038 stream.mFramesPerPacket = 1;
1039 stream.mBytesPerPacket = 4;
1040 stream.mBytesPerFrame = 4;
1041 stream.mBitsPerChannel = 32;
1042 stream.mChannelsPerFrame = static_cast<UInt32> (requestedNumChannels);
1043
1044 err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope, static_cast<UInt32> (i), &stream, sizeof (stream));
1045 if (err != noErr) return false;
1046 }
1047 }
1048
1049 if (! set.isDiscreteLayout())
1050 {
1051 const AudioChannelLayoutTag requestedTag = CoreAudioLayouts::toCoreAudio (set);
1052
1053 AudioChannelLayout layout;
1054 const UInt32 minDataSize = sizeof (layout) - sizeof (AudioChannelDescription);
1055 UInt32 dataSize = minDataSize;
1056
1058 auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, scope, static_cast<UInt32> (i), &layout, &dataSize);
1059 bool supportsLayouts = (err == noErr && dataSize >= minDataSize);
1060
1061 if (supportsLayouts)
1062 {
1063 const UInt32 expectedSize =
1064 minDataSize + (sizeof (AudioChannelDescription) * layout.mNumberChannelDescriptions);
1065
1067 layoutBuffer.malloc (1, expectedSize);
1068 dataSize = expectedSize;
1069
1071 static_cast<UInt32> (i), layoutBuffer.get(), &dataSize);
1072
1073 if (err != noErr || dataSize < expectedSize)
1074 return false;
1075
1076 // try to convert the layout into a tag
1077 actualTag = CoreAudioLayouts::toCoreAudio (CoreAudioLayouts::fromCoreAudio (layout));
1078
1079 if (actualTag != requestedTag)
1080 {
1081 zerostruct (layout);
1082 layout.mChannelLayoutTag = requestedTag;
1083
1085
1086 // only bail out if the plug-in claims to support layouts
1087 // See AudioUnit headers on kAudioUnitProperty_AudioChannelLayout
1089 return false;
1090 }
1091 }
1092 }
1093 }
1094 }
1095
1096 return true;
1097 }
1098
1099 bool canApplyBusesLayout (const BusesLayout& layouts) const override
1100 {
1101 // You cannot call setBusesLayout when the AudioProcessor is processing.
1102 // Call releaseResources first!
1103 jassert (! prepared);
1104
1105 bool layoutHasChanged = false;
1106
1107 if (! syncBusLayouts (layouts, false, layoutHasChanged))
1108 return false;
1109
1110 // did anything actually change
1111 if (! layoutHasChanged)
1112 return true;
1113
1114 // Some plug-ins require the LayoutTag to be set after initialization
1115 const auto success = (AudioUnitInitialize (audioUnit) == noErr)
1117
1119
1120 if (! success)
1121 // make sure that the layout is back to its original state
1122 syncBusLayouts (getBusesLayout(), false, layoutHasChanged);
1123
1124 return success;
1125 }
1126
1127 //==============================================================================
1128 // AudioPluginInstance methods:
1129
1130 void fillInPluginDescription (PluginDescription& desc) const override
1131 {
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);
1147
1148 #if JUCE_PLUGINHOST_ARA
1149 desc.hasARAExtension = [&]
1150 {
1151 UInt32 propertySize = sizeof (ARA::ARAAudioUnitFactory);
1152 Boolean isWriteable = FALSE;
1153
1155 ARA::kAudioUnitProperty_ARAFactory,
1157 0,
1158 &propertySize,
1159 &isWriteable);
1160
1161 return (status == noErr) && (propertySize == sizeof (ARA::ARAAudioUnitFactory)) && ! isWriteable;
1162 }();
1163 #endif
1164 }
1165
1166 void getExtensions (ExtensionsVisitor& visitor) const override
1167 {
1168 struct Extensions final : public ExtensionsVisitor::AudioUnitClient
1169 {
1170 explicit Extensions (const AudioUnitPluginInstance* instanceIn) : instance (instanceIn) {}
1171
1172 AudioUnit getAudioUnitHandle() const noexcept override { return instance->audioUnit; }
1173
1174 const AudioUnitPluginInstance* instance = nullptr;
1175 };
1176
1177 visitor.visitAudioUnitClient (Extensions { this });
1178
1179 #ifdef JUCE_PLUGINHOST_ARA
1180 struct ARAExtensions final : public ExtensionsVisitor::ARAClient
1181 {
1182 explicit ARAExtensions (const AudioUnitPluginInstance* instanceIn) : instance (instanceIn) {}
1183
1184 void createARAFactoryAsync (std::function<void (ARAFactoryWrapper)> cb) const override
1185 {
1186 getOrCreateARAAudioUnit ({ instance->auComponent, instance->isAUv3 },
1187 [origCb = std::move (cb)] (auto dylibKeepAliveAudioUnit)
1188 {
1189 origCb ([&]() -> ARAFactoryWrapper
1190 {
1191 if (dylibKeepAliveAudioUnit != nullptr)
1192 return ARAFactoryWrapper { ::juce::getARAFactory (std::move (dylibKeepAliveAudioUnit)) };
1193
1194 return ARAFactoryWrapper { nullptr };
1195 }());
1196 });
1197 }
1198
1199 const AudioUnitPluginInstance* instance = nullptr;
1200 };
1201
1202 if (hasARAExtension (audioUnit))
1203 visitor.visitARAClient (ARAExtensions (this));
1204 #endif
1205 }
1206
1207 void* getPlatformSpecificData() override { return audioUnit; }
1208 const String getName() const override { return pluginName; }
1209
1210 double getTailLengthSeconds() const override
1211 {
1212 Float64 tail = 0;
1213 UInt32 tailSize = sizeof (tail);
1214
1215 if (audioUnit != nullptr)
1217 0, &tail, &tailSize);
1218
1219 return tail;
1220 }
1221
1222 bool acceptsMidi() const override { return wantsMidiMessages; }
1223 bool producesMidi() const override { return producesMidiMessages; }
1224
1225 //==============================================================================
1226 // AudioProcessor methods:
1227
1228 void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override
1229 {
1230 if (audioUnit != nullptr)
1231 {
1232 releaseResources();
1234
1235 for (int dir = 0; dir < 2; ++dir)
1236 {
1237 const bool isInput = (dir == 0);
1239 const int n = getBusCount (isInput);
1240
1241 for (int i = 0; i < n; ++i)
1242 {
1243 Float64 sampleRate;
1244 UInt32 sampleRateSize = sizeof (sampleRate);
1245 const Float64 sr = newSampleRate;
1246
1247 AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope, static_cast<UInt32> (i), &sampleRate, &sampleRateSize);
1248
1249 if (! approximatelyEqual (sampleRate, sr))
1250 {
1251 if (isAUv3) // setting kAudioUnitProperty_SampleRate fails on AUv3s
1252 {
1254 UInt32 dataSize = sizeof (stream);
1255 auto err = AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope, static_cast<UInt32> (i), &stream, &dataSize);
1256
1257 if (err == noErr && dataSize == sizeof (stream))
1258 {
1259 stream.mSampleRate = sr;
1260 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, scope, static_cast<UInt32> (i), &stream, sizeof (stream));
1261 }
1262 }
1263 else
1264 {
1265 AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, scope, static_cast<UInt32> (i), &sr, sizeof (sr));
1266 }
1267 }
1268
1269 if (isInput)
1270 {
1272 zerostruct (info); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct)
1273
1274 info.inputProcRefCon = this;
1275 info.inputProc = renderGetInputCallback;
1276
1278 static_cast<UInt32> (i), &info, sizeof (info));
1279 }
1280 else
1281 {
1282 outputBufferList.add (new AUBuffer (static_cast<size_t> (getChannelCountOfBus (false, i))));
1283 }
1284 }
1285 }
1286
1289 &frameSize, sizeof (frameSize));
1290
1291 setRateAndBufferSizeDetails ((double) newSampleRate, estimatedSamplesPerBlock);
1292
1294 timeStamp.mSampleTime = 0;
1295 timeStamp.mHostTime = mach_absolute_time();
1297
1298 wasPlaying = false;
1299
1300 resetBuses();
1301
1302 bool ignore;
1303
1304 if (! syncBusLayouts (getBusesLayout(), false, ignore))
1305 return;
1306
1307 prepared = [&]
1308 {
1310 return false;
1311
1312 if (! haveParameterList)
1313 refreshParameterList();
1314
1315 if (! syncBusLayouts (getBusesLayout(), true, ignore))
1316 {
1318 return false;
1319 }
1320
1321 updateLatency();
1322 return true;
1323 }();
1324
1325 inMapping .setUpMapping (audioUnit, true);
1326 outMapping.setUpMapping (audioUnit, false);
1327
1328 preparedChannels = jmax (getTotalNumInputChannels(), getTotalNumOutputChannels());
1331 }
1332 }
1333
1334 void releaseResources() override
1335 {
1336 if (prepared)
1337 {
1338 resetBuses();
1341
1342 outputBufferList.clear();
1343 prepared = false;
1344 }
1345
1346 incomingMidi.clear();
1347 }
1348
1349 void resetBuses()
1350 {
1353 }
1354
1355 void processAudio (AudioBuffer<float>& buffer, MidiBuffer& midiMessages, bool processBlockBypassedCalled)
1356 {
1357 auto* playhead = getPlayHead();
1358 const auto position = playhead != nullptr ? playhead->getPosition() : nullopt;
1359
1360 if (const auto hostTimeNs = position.hasValue() ? position->getHostTimeNs() : nullopt)
1361 {
1362 timeStamp.mHostTime = *hostTimeNs;
1364 }
1365 else
1366 {
1367 timeStamp.mHostTime = 0;
1369 }
1370
1371 // If these are hit, we might allocate in the process block!
1372 jassert (buffer.getNumChannels() <= preparedChannels);
1373 jassert (buffer.getNumSamples() <= preparedSamples);
1374 // Copy the input buffer to guard against the case where a bus has more output channels
1375 // than input channels, so rendering the output for that bus might stamp over the input
1376 // to the following bus.
1377 inputBuffer.makeCopyOf (buffer, true);
1378
1379 const auto numSamples = buffer.getNumSamples();
1380
1381 if (auSupportsBypass)
1382 {
1384 }
1386 {
1388 return;
1389 }
1390
1391 if (prepared)
1392 {
1393 const auto numOutputBuses = getBusCount (false);
1394
1395 for (int i = 0; i < numOutputBuses; ++i)
1396 {
1397 if (AUBuffer* buf = outputBufferList[i])
1398 {
1400 const auto* bus = getBus (false, i);
1401 const auto channelCount = bus != nullptr ? bus->getNumberOfChannels() : 0;
1402
1403 for (auto juceChannel = 0; juceChannel < channelCount; ++juceChannel)
1404 {
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));
1409 }
1410 }
1411 }
1412
1414 {
1415 for (const auto metadata : midiMessages)
1416 {
1417 if (metadata.numBytes <= 3)
1419 metadata.data[0], metadata.data[1], metadata.data[2],
1420 (UInt32) metadata.samplePosition);
1421 else
1423 }
1424
1425 midiMessages.clear();
1426 }
1427
1428 for (int i = 0; i < numOutputBuses; ++i)
1429 {
1431
1432 if (AUBuffer* buf = outputBufferList[i])
1433 AudioUnitRender (audioUnit, &flags, &timeStamp, static_cast<UInt32> (i), (UInt32) numSamples, buf->bufferList.get());
1434 }
1435
1436 timeStamp.mSampleTime += numSamples;
1437 }
1438 else
1439 {
1440 // Plugin not working correctly, so just bypass..
1441 for (int i = getTotalNumOutputChannels(); --i >= 0;)
1442 buffer.clear (i, 0, buffer.getNumSamples());
1443 }
1444
1446 {
1447 const ScopedLock sl (midiInLock);
1448 midiMessages.swapWith (incomingMidi);
1449 incomingMidi.clear();
1450 }
1451 }
1452
1453 void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
1454 {
1455 processAudio (buffer, midiMessages, false);
1456 }
1457
1458 void processBlockBypassed (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
1459 {
1460 processAudio (buffer, midiMessages, true);
1461 }
1462
1463 //==============================================================================
1464 AudioProcessorParameter* getBypassParameter() const override { return auSupportsBypass ? bypassParam.get() : nullptr; }
1465
1466 bool hasEditor() const override
1467 {
1468 #if JUCE_MAC
1469 return true;
1470 #else
1471 UInt32 dataSize;
1472 Boolean isWritable;
1473
1475 kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
1476 && dataSize == sizeof (uintptr_t) && isWritable != 0);
1477 #endif
1478 }
1479
1480 AudioProcessorEditor* createEditor() override;
1481
1482 static AudioProcessor::BusesProperties getBusesProperties (AudioComponentInstance comp)
1483 {
1484 AudioProcessor::BusesProperties busProperties;
1485
1486 for (int dir = 0; dir < 2; ++dir)
1487 {
1488 const auto isInput = (dir == 0);
1489 const auto n = AudioUnitFormatHelpers::getElementCount (comp, isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output);
1490
1491 for (UInt32 i = 0; i < n; ++i)
1492 {
1493 String busName;
1494 AudioChannelSet currentLayout;
1495
1496 getBusProperties (comp, isInput, i, busName, currentLayout);
1497 jassert (! currentLayout.isDisabled());
1498
1499 busProperties.addBus (isInput, busName, currentLayout, true);
1500 }
1501 }
1502
1503 return busProperties;
1504 }
1505
1506 //==============================================================================
1507 const String getInputChannelName (int index) const override
1508 {
1509 if (isPositiveAndBelow (index, getTotalNumInputChannels()))
1510 return "Input " + String (index + 1);
1511
1512 return {};
1513 }
1514
1515 const String getOutputChannelName (int index) const override
1516 {
1517 if (isPositiveAndBelow (index, getTotalNumOutputChannels()))
1518 return "Output " + String (index + 1);
1519
1520 return {};
1521 }
1522
1523 bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumInputChannels()); }
1524 bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumOutputChannels()); }
1525
1526 //==============================================================================
1528 {
1529 #if JUCE_MAC
1530 jassert (audioUnit != nullptr);
1531
1532 AudioUnitParameter param;
1533 param.mAudioUnit = audioUnit;
1534 param.mParameterID = kAUParameterListener_AnyParameter;
1535
1536 AUParameterListenerNotify (nullptr, nullptr, &param);
1537 #endif
1538 }
1539
1540 //==============================================================================
1542 {
1543 public:
1544 ScopedFactoryPresets (AudioUnit& au)
1545 {
1546 UInt32 sz = sizeof (CFArrayRef);
1547 AudioUnitGetProperty (au, kAudioUnitProperty_FactoryPresets,
1548 kAudioUnitScope_Global, 0, &presets.object, &sz);
1549 }
1550
1551 CFArrayRef get() const noexcept
1552 {
1553 return presets.object;
1554 }
1555
1556 private:
1557 CFObjectHolder<CFArrayRef> presets;
1558 };
1559
1560 int getNumPrograms() override
1561 {
1563
1564 if (factoryPresets.get() != nullptr)
1565 return (int) CFArrayGetCount (factoryPresets.get());
1566
1567 return 0;
1568 }
1569
1570 int getCurrentProgram() override
1571 {
1572 AUPreset current;
1573 current.presetNumber = 0;
1574 UInt32 sz = sizeof (AUPreset);
1575
1577 kAudioUnitScope_Global, 0, &current, &sz);
1578
1579 return current.presetNumber;
1580 }
1581
1582 void setCurrentProgram (int newIndex) override
1583 {
1585
1586 if (factoryPresets.get() != nullptr
1587 && newIndex < (int) CFArrayGetCount (factoryPresets.get()))
1588 {
1589 AUPreset current;
1590 current.presetNumber = newIndex;
1591
1592 if (auto* p = static_cast<const AUPreset*> (CFArrayGetValueAtIndex (factoryPresets.get(), newIndex)))
1593 current.presetName = p->presetName;
1594
1596 kAudioUnitScope_Global, 0, &current, sizeof (AUPreset));
1597
1599 }
1600 }
1601
1602 const String getProgramName (int index) override
1603 {
1604 if (index == -1)
1605 {
1606 AUPreset current;
1607 current.presetNumber = -1;
1608 current.presetName = CFSTR ("");
1609
1610 UInt32 prstsz = sizeof (AUPreset);
1611
1613 kAudioUnitScope_Global, 0, &current, &prstsz);
1614
1615 return String::fromCFString (current.presetName);
1616 }
1617
1619
1620 if (factoryPresets.get() != nullptr)
1621 {
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);
1626 }
1627
1628 return {};
1629 }
1630
1631 void changeProgramName (int /*index*/, const String& /*newName*/) override
1632 {
1633 jassertfalse; // xxx not implemented!
1634 }
1635
1636 //==============================================================================
1637 void updateTrackProperties (const TrackProperties& properties) override
1638 {
1639 if (properties.name.isNotEmpty())
1640 {
1641 CFObjectHolder<CFStringRef> contextName { properties.name.toCFString() };
1643 0, &contextName.object, sizeof (contextName.object));
1644 }
1645 }
1646
1647 //==============================================================================
1648 void getStateInformation (MemoryBlock& destData) override
1649 {
1650 getCurrentProgramStateInformation (destData);
1651 }
1652
1653 void getCurrentProgramStateInformation (MemoryBlock& destData) override
1654 {
1656 UInt32 sz = sizeof (propertyList.object);
1657
1661 0, &propertyList.object, &sz) == noErr)
1662 {
1664 CFWriteStreamOpen (stream.get());
1665
1666 CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList.object, stream.get(), kCFPropertyListBinaryFormat_v1_0, nullptr);
1667 CFWriteStreamClose (stream.get());
1668
1670
1671 destData.setSize ((size_t) bytesWritten);
1672 destData.copyFrom (CFDataGetBytePtr (data.get()), 0, destData.getSize());
1673 }
1674 }
1675
1676 void setStateInformation (const void* data, int sizeInBytes) override
1677 {
1678 setCurrentProgramStateInformation (data, sizeInBytes);
1679 }
1680
1681 void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override
1682 {
1684 sizeInBytes, kCFAllocatorNull));
1685 CFReadStreamOpen (stream.get());
1686
1689 kCFPropertyListImmutable, &format, nullptr) };
1690
1691 if (propertyList.object != nullptr)
1692 {
1694 0, &propertyList.object, sizeof (propertyList.object));
1695
1697 }
1698 }
1699
1700 void refreshParameterList() override
1701 {
1702 paramIDToParameter.clear();
1703 AudioProcessorParameterGroup newParameterTree;
1704
1705 if (audioUnit != nullptr)
1706 {
1709 0, &paramListSize, nullptr);
1710
1712
1713 if (! haveParameterList)
1714 return;
1715
1716 if (paramListSize > 0)
1717 {
1718 const size_t numParams = paramListSize / sizeof (int);
1719
1721
1723 0, ids.data(), &paramListSize);
1724
1726
1727 for (size_t i = 0; i < numParams; ++i)
1728 {
1729 const ScopedAudioUnitParameterInfo info { audioUnit, ids[i] };
1730
1731 if (! info.isValid())
1732 continue;
1733
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;
1739
1740 auto parameter = std::make_unique<AUInstanceParameter> (*this,
1741 ids[i],
1742 paramName,
1743 info.get().minValue,
1744 info.get().maxValue,
1745 info.get().defaultValue,
1746 (info.get().flags & kAudioUnitParameterFlag_NonRealTime) == 0,
1747 isDiscrete,
1748 isDiscrete ? (int) (info.get().maxValue - info.get().minValue + 1.0f) : AudioProcessor::getDefaultNumParameterSteps(),
1749 isBoolean,
1750 label,
1751 (info.get().flags & kAudioUnitParameterFlag_ValuesHaveStrings) != 0);
1752
1753 paramIDToParameter.emplace (ids[i], parameter.get());
1754
1755 if (info.get().flags & kAudioUnitParameterFlag_HasClump)
1756 {
1757 auto groupInfo = groupIDMap.find (info.get().clumpID);
1758
1759 if (groupInfo == groupIDMap.end())
1760 {
1761 const auto clumpName = [this, &info]
1762 {
1764 UInt32 clumpSz = sizeof (clumpNameInfo);
1766 clumpNameInfo.inID = info.get().clumpID;
1767 clumpNameInfo.inDesiredLength = (SInt32) 256;
1768
1772 0,
1774 &clumpSz) == noErr)
1775 return String::fromCFString (clumpNameInfo.outName);
1776
1777 return String (info.get().clumpID);
1778 }();
1779
1780 auto group = std::make_unique<AudioProcessorParameterGroup> (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));
1785 }
1786 else
1787 {
1788 groupInfo->second->addChild (std::move (parameter));
1789 }
1790 }
1791 else
1792 {
1793 newParameterTree.addChild (std::move (parameter));
1794 }
1795 }
1796 }
1797 }
1798
1799 setHostedParameterTree (std::move (newParameterTree));
1800
1801 UInt32 propertySize = 0;
1802 Boolean writable = false;
1803
1806 && propertySize >= sizeof (UInt32) && writable);
1807 bypassParam.reset (new AUBypassParameter (*this));
1808 }
1809
1810 void updateLatency()
1811 {
1812 Float64 latencySecs = 0.0;
1813 UInt32 latencySize = sizeof (latencySecs);
1815 0, &latencySecs, &latencySize);
1816
1817 setLatencySamples (roundToInt (latencySecs * getSampleRate()));
1818 }
1819
1820 void handleIncomingMidiMessage (void*, const MidiMessage& message)
1821 {
1822 const ScopedLock sl (midiInLock);
1823 incomingMidi.addEvent (message, 0);
1824 }
1825
1826 void handlePartialSysexMessage (void*, const uint8*, int, double) {}
1827
1828 bool isMidiEffect() const override { return isMidiEffectPlugin; }
1829
1830private:
1831 //==============================================================================
1832 friend class AudioUnitPluginWindowCocoa;
1833 friend class AudioUnitPluginFormat;
1834
1836
1839 String pluginName, manufacturer, version;
1840 String fileOrIdentifier;
1841 CriticalSection lock;
1842
1843 bool wantsMidiMessages = false, producesMidiMessages = false,
1844 wasPlaying = false, prepared = false,
1845 isAUv3 = false, isMidiEffectPlugin = false;
1846
1847 struct AUBuffer
1848 {
1849 AUBuffer (size_t numBuffers)
1850 {
1851 bufferList.calloc (1, (sizeof (AudioBufferList) - sizeof (::AudioBuffer)) + (sizeof (::AudioBuffer) * numBuffers));
1852 AudioBufferList& buffer = *bufferList.get();
1853
1854 buffer.mNumberBuffers = static_cast<UInt32> (numBuffers);
1855 }
1856
1857 operator AudioBufferList&()
1858 {
1859 return *bufferList.get();
1860 }
1861
1862 HeapBlock<AudioBufferList> bufferList;
1863 };
1864
1865 //==============================================================================
1866 struct AUBypassParameter final : public Parameter
1867 {
1868 AUBypassParameter (AudioUnitPluginInstance& effectToUse)
1869 : parent (effectToUse), currentValue (getCurrentHostValue())
1870 {}
1871
1872 bool getCurrentHostValue()
1873 {
1874 if (parent.auSupportsBypass)
1875 {
1876 UInt32 dataSize = sizeof (UInt32);
1877 UInt32 value = 0;
1878
1879 if (AudioUnitGetProperty (parent.audioUnit, kAudioUnitProperty_BypassEffect,
1880 kAudioUnitScope_Global, 0, &value, &dataSize) == noErr
1881 && dataSize == sizeof (UInt32))
1882 return value != 0;
1883 }
1884
1885 return false;
1886 }
1887
1888 float getValue() const override
1889 {
1890 return currentValue ? 1.0f : 0.0f;
1891 }
1892
1893 void setValue (float newValue) override
1894 {
1895 auto newBypassValue = (newValue != 0.0f);
1896
1897 const ScopedLock sl (parent.lock);
1898
1899 if (newBypassValue != currentValue)
1900 {
1901 currentValue = newBypassValue;
1902
1903 if (parent.auSupportsBypass)
1904 {
1905 UInt32 value = (newValue != 0.0f ? 1 : 0);
1906 AudioUnitSetProperty (parent.audioUnit, kAudioUnitProperty_BypassEffect,
1907 kAudioUnitScope_Global, 0, &value, sizeof (UInt32));
1908
1909 #if JUCE_MAC
1910 jassert (parent.audioUnit != nullptr);
1911
1912 AudioUnitEvent ev;
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;
1918
1919 AUEventListenerNotify (parent.eventListenerRef, nullptr, &ev);
1920 #endif
1921 }
1922 }
1923 }
1924
1925 float getValueForText (const String& text) const override
1926 {
1927 String lowercaseText (text.toLowerCase());
1928
1929 for (auto& testText : auOnStrings)
1930 if (lowercaseText == testText)
1931 return 1.0f;
1932
1933 for (auto& testText : auOffStrings)
1934 if (lowercaseText == testText)
1935 return 0.0f;
1936
1937 return text.getIntValue() != 0 ? 1.0f : 0.0f;
1938 }
1939
1940 float getDefaultValue() const override { return 0.0f; }
1941 String getName (int /*maximumStringLength*/) 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 {}; }
1949
1950 String getParameterID() const override { return {}; }
1951
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") };
1956
1957 bool currentValue = false;
1958 };
1959
1962 AudioBuffer<float> inputBuffer;
1964
1967
1968 AudioUnit audioUnit;
1969 #if JUCE_MAC
1971 #endif
1972
1974
1975 AudioUnitFormatHelpers::SingleDirectionChannelMapping inMapping, outMapping;
1976 MidiDataConcatenator midiConcatenator;
1977 CriticalSection midiInLock;
1978 MidiBuffer incomingMidi;
1980 bool lastProcessBlockCallWasBypass = false, auSupportsBypass = false;
1981 bool haveParameterList = false;
1982
1983 void setPluginCallbacks()
1984 {
1985 if (audioUnit != nullptr)
1986 {
1987 #if JUCE_MAC
1989 {
1991 zerostruct (info);
1992
1993 info.userData = this;
1994 info.midiOutputCallback = renderMidiOutputCallback;
1995
1997 kAudioUnitScope_Global, 0, &info, sizeof (info)) == noErr);
1998 }
1999 #endif
2000
2001 HostCallbackInfo info;
2002 zerostruct (info);
2003
2004 info.hostUserData = this;
2005 info.beatAndTempoProc = getBeatAndTempoCallback;
2006 info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
2007 info.transportStateProc = getTransportStateCallback;
2008
2010 kAudioUnitScope_Global, 0, &info, sizeof (info));
2011 }
2012 }
2013
2014 #if JUCE_MAC
2016 {
2017 if (eventListenerRef != nullptr)
2018 {
2020 eventListenerRef = nullptr;
2021 }
2022 }
2023
2024 void createEventListener()
2025 {
2026 if (audioUnit == nullptr)
2027 return;
2028
2030
2033
2034 for (auto* param : getParameters())
2035 {
2036 jassert (dynamic_cast<AUInstanceParameter*> (param) != nullptr);
2037
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;
2043
2044 event.mEventType = kAudioUnitEvent_ParameterValueChange;
2046
2049
2052 }
2053
2058 }
2059
2061 {
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;
2069 }
2070
2071 void eventCallback (const AudioUnitEvent& event, AudioUnitParameterValue newValue)
2072 {
2073 if (event.mEventType == kAudioUnitEvent_PropertyChange)
2074 {
2075 respondToPropertyChange (event.mArgument.mProperty);
2076 return;
2077 }
2078
2079 const auto iter = paramIDToParameter.find (event.mArgument.mParameter.mParameterID);
2080 auto* param = iter != paramIDToParameter.end() ? iter->second : nullptr;
2081 jassert (param != nullptr); // Invalid parameter index
2082
2083 if (param == nullptr)
2084 return;
2085
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();
2092 }
2093
2095 {
2096 switch (prop.mPropertyID)
2097 {
2100 updateHostDisplay (AudioProcessorListener::ChangeDetails().withParameterInfoChanged (true));
2101 break;
2102
2105 updateHostDisplay (AudioProcessorListener::ChangeDetails().withProgramChanged (true));
2106 break;
2107
2109 updateLatency();
2110 break;
2111
2113 if (bypassParam != nullptr)
2114 bypassParam->setValueNotifyingHost (bypassParam->getValue());
2115
2116 break;
2117 }
2118 }
2119
2120 static void eventListenerCallback (void* userRef, void*, const AudioUnitEvent* event,
2122 {
2124 jassert (event != nullptr);
2125 static_cast<AudioUnitPluginInstance*> (userRef)->eventCallback (*event, value);
2126 }
2127
2128 void updateParameterInfo()
2129 {
2130 for (const auto& idAndParam : paramIDToParameter)
2131 {
2132 const auto& id = idAndParam.first;
2133 const auto& param = idAndParam.second;
2134
2135 const ScopedAudioUnitParameterInfo info { audioUnit, id };
2136
2137 if (! info.isValid())
2138 continue;
2139
2140 param->setName (getParamName (info.get()));
2141 param->setLabel (getParamLabel (info.get()));
2142 }
2143 }
2144 #endif
2145
2146 /* Some fields in the AudioUnitParameterInfo may need to be released after use,
2147 so we'll do that using RAII.
2148 */
2150 {
2151 public:
2152 ScopedAudioUnitParameterInfo (AudioUnit au, UInt32 paramId)
2153 {
2154 auto sz = (UInt32) sizeof (info);
2155 valid = noErr == AudioUnitGetProperty (au,
2156 kAudioUnitProperty_ParameterInfo,
2157 kAudioUnitScope_Global,
2158 paramId,
2159 &info,
2160 &sz);
2161 }
2162
2163 ScopedAudioUnitParameterInfo (const ScopedAudioUnitParameterInfo&) = delete;
2164 ScopedAudioUnitParameterInfo (ScopedAudioUnitParameterInfo&&) = delete;
2165 ScopedAudioUnitParameterInfo& operator= (const ScopedAudioUnitParameterInfo&) = delete;
2166 ScopedAudioUnitParameterInfo& operator= (ScopedAudioUnitParameterInfo&&) = delete;
2167
2168 ~ScopedAudioUnitParameterInfo() noexcept
2169 {
2170 if ((info.flags & kAudioUnitParameterFlag_CFNameRelease) == 0)
2171 return;
2172
2173 if (info.cfNameString != nullptr)
2174 CFRelease (info.cfNameString);
2175
2176 if (info.unit == kAudioUnitParameterUnit_CustomUnit && info.unitName != nullptr)
2177 CFRelease (info.unitName);
2178 }
2179
2180 bool isValid() const { return valid; }
2181
2182 const AudioUnitParameterInfo& get() const noexcept { return info; }
2183
2184 private:
2185 AudioUnitParameterInfo info;
2186 bool valid = false;
2187 };
2188
2189 static String getParamName (const AudioUnitParameterInfo& info)
2190 {
2191 if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) == 0)
2192 return { info.name, sizeof (info.name) };
2193
2194 return String::fromCFString (info.cfNameString);
2195 }
2196
2197 static String getParamLabel (const AudioUnitParameterInfo& info)
2198 {
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";
2205
2206 return {};
2207 }
2208
2209 //==============================================================================
2211 const AudioTimeStamp*,
2215 {
2216 if (inputBuffer.getNumChannels() <= 0)
2217 {
2219 return noErr;
2220 }
2221
2222 // if this ever happens, might need to add extra handling
2223 if (inputBuffer.getNumSamples() != (int) inNumberFrames)
2224 {
2226 return noErr;
2227 }
2228
2229 const auto buffer = static_cast<int> (inBusNumber) < getBusCount (true)
2230 ? getBusBuffer (inputBuffer, true, static_cast<int> (inBusNumber))
2231 : AudioBuffer<float>();
2232
2233 for (int juceChannel = 0; juceChannel < buffer.getNumChannels(); ++juceChannel)
2234 {
2235 const auto auChannel = (int) inMapping.getAuIndexForJuceChannel (inBusNumber, (size_t) juceChannel);
2236
2237 if (auChannel < buffer.getNumChannels())
2238 memcpy (ioData->mBuffers[auChannel].mData, buffer.getReadPointer (juceChannel), sizeof (float) * inNumberFrames);
2239 else
2240 zeromem (ioData->mBuffers[auChannel].mData, sizeof (float) * inNumberFrames);
2241 }
2242
2243 return noErr;
2244 }
2245
2247 {
2248 if (pktlist != nullptr && pktlist->numPackets)
2249 {
2250 auto time = Time::getMillisecondCounterHiRes() * 0.001;
2251 const MIDIPacket* packet = &pktlist->packet[0];
2252
2253 for (UInt32 i = 0; i < pktlist->numPackets; ++i)
2254 {
2255 midiConcatenator.pushMidiData (packet->data, (int) packet->length, time, (void*) nullptr, *this);
2257 }
2258 }
2259
2260 return noErr;
2261 }
2262
2263 template <typename Type1, typename Type2>
2264 static void setIfNotNull (Type1* p, Type2 value) noexcept
2265 {
2266 if (p != nullptr) *p = value;
2267 }
2268
2269 /* If the AudioPlayHead is available, and has valid PositionInfo, this will return the result
2270 of calling the specified getter on that PositionInfo. Otherwise, this will return a
2271 default-constructed instance of the same type.
2272
2273 For getters that return an Optional, this function will return a nullopt if the playhead or
2274 position info is invalid.
2275
2276 For getters that return a bool, this function will return false if the playhead or position
2277 info is invalid.
2278 */
2279 template <typename Result>
2280 Result getFromPlayHead (Result (AudioPlayHead::PositionInfo::* member)() const) const
2281 {
2282 if (auto* ph = getPlayHead())
2283 if (const auto pos = ph->getPosition())
2284 return ((*pos).*member)();
2285
2286 return {};
2287 }
2288
2290 {
2293 return noErr;
2294 }
2295
2298 {
2301
2302 const auto signature = getFromPlayHead (&AudioPlayHead::PositionInfo::getTimeSignature).orFallback (AudioPlayHead::TimeSignature{});
2305
2306 return noErr;
2307 }
2308
2312 {
2318
2319 const auto loopPoints = getFromPlayHead (&AudioPlayHead::PositionInfo::getLoopPoints).orFallback (AudioPlayHead::LoopPoints{});
2320 setIfNotNull (outCycleStartBeat, loopPoints.ppqStart);
2321 setIfNotNull (outCycleEndBeat, loopPoints.ppqEnd);
2322
2323 return noErr;
2324 }
2325
2326 //==============================================================================
2330 {
2331 return static_cast<AudioUnitPluginInstance*> (hostRef)
2333 }
2334
2335 static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp*, UInt32 /*midiOutNum*/,
2336 const MIDIPacketList* pktlist)
2337 {
2338 return static_cast<AudioUnitPluginInstance*> (hostRef)->renderMidiOutput (pktlist);
2339 }
2340
2342 {
2344 }
2345
2349 {
2350 return static_cast<AudioUnitPluginInstance*> (hostRef)
2353 }
2354
2358 {
2359 return static_cast<AudioUnitPluginInstance*> (hostRef)
2362 }
2363
2364 //==============================================================================
2365 bool isBusCountWritable (bool isInput) const noexcept
2366 {
2368 Boolean writable;
2370
2372
2373 return (err == noErr && writable != 0 && countSize == sizeof (UInt32));
2374 }
2375
2376 //==============================================================================
2377 int getElementCount (AudioUnitScope scope) const noexcept
2378 {
2379 return static_cast<int> (AudioUnitFormatHelpers::getElementCount (audioUnit, scope));
2380 }
2381
2382 //==============================================================================
2383 void getBusProperties (bool isInput, UInt32 busIdx, String& busName, AudioChannelSet& currentLayout) const
2384 {
2385 getBusProperties (audioUnit, isInput, busIdx, busName, currentLayout);
2386 }
2387
2388 static void getBusProperties (AudioUnit comp, bool isInput, UInt32 busIdx, String& busName, AudioChannelSet& currentLayout)
2389 {
2391 busName = (isInput ? "Input #" : "Output #") + String (busIdx + 1);
2392
2393 {
2395 UInt32 propertySize = sizeof (busNameCF.object);
2396
2398 if (busNameCF.object != nullptr)
2399 busName = nsStringToJuce ((NSString*) busNameCF.object);
2400
2401 {
2403 propertySize = sizeof (auLayout);
2404
2406 currentLayout = CoreAudioLayouts::fromCoreAudio (auLayout);
2407 }
2408
2409 if (currentLayout.isDisabled())
2410 {
2412 propertySize = sizeof (descr);
2413
2415 currentLayout = AudioChannelSet::canonicalChannelSet (static_cast<int> (descr.mChannelsPerFrame));
2416 }
2417 }
2418 }
2419
2420 //==============================================================================
2421 void numBusesChanged() override
2422 {
2424 }
2425
2427 {
2428 supportedInLayouts.clear();
2429 supportedOutLayouts.clear();
2430 numChannelInfos = 0;
2431 channelInfos.free();
2432
2433 for (int dir = 0; dir < 2; ++dir)
2434 {
2435 const bool isInput = (dir == 0);
2437 auto n = getElementCount (scope);
2438
2439 for (int busIdx = 0; busIdx < n; ++busIdx)
2440 {
2442 AudioChannelSet currentLayout;
2443
2444 {
2446 UInt32 propertySize = sizeof (auLayout);
2447
2449 currentLayout = CoreAudioLayouts::fromCoreAudio (auLayout);
2450 }
2451
2452 if (currentLayout.isDisabled())
2453 {
2455 UInt32 propertySize = sizeof (descr);
2456
2458 currentLayout = AudioChannelSet::canonicalChannelSet (static_cast<int> (descr.mChannelsPerFrame));
2459 }
2460
2461 supported.clear();
2462 {
2463 UInt32 propertySize = 0;
2464 Boolean writable;
2465
2467 && propertySize > 0)
2468 {
2469 const size_t numElements = propertySize / sizeof (AudioChannelLayoutTag);
2471 propertySize = static_cast<UInt32> (sizeof (AudioChannelLayoutTag) * numElements);
2472
2474 static_cast<UInt32> (busIdx), layoutTags.get(), &propertySize) == noErr)
2475 {
2476 for (int j = 0; j < static_cast<int> (numElements); ++j)
2477 {
2478 const AudioChannelLayoutTag tag = layoutTags[j];
2479
2481 {
2483
2484 caLayout.mChannelLayoutTag = tag;
2485 supported.addIfNotAlreadyThere (CoreAudioLayouts::fromCoreAudio (caLayout));
2486 }
2487 }
2488
2489 if (supported.size() > 0)
2490 supported.addIfNotAlreadyThere (currentLayout);
2491 }
2492 }
2493 }
2494
2496 }
2497 }
2498
2499 {
2500 UInt32 propertySize = 0;
2501 Boolean writable;
2502
2504 && propertySize > 0)
2505 {
2507 channelInfos.malloc (static_cast<size_t> (numChannelInfos));
2508 propertySize = static_cast<UInt32> (sizeof (AUChannelInfo) * static_cast<size_t> (numChannelInfos));
2509
2511 numChannelInfos = 0;
2512 }
2513 else
2514 {
2515 numChannelInfos = 1;
2516 channelInfos.malloc (static_cast<size_t> (numChannelInfos));
2517 channelInfos.get()->inChannels = -1;
2518 channelInfos.get()->outChannels = -1;
2519 }
2520 }
2521 }
2522
2524 {
2525 #if JUCE_MAC
2526 UInt32 dataSize = 0;
2527 Boolean isWritable = false;
2528
2530 kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
2531 && dataSize != 0)
2532 {
2534
2536 kAudioUnitScope_Global, 0, &midiArray.object, &dataSize) == noErr)
2537 return (CFArrayGetCount (midiArray.object) > 0);
2538 }
2539 #endif
2540
2541 return false;
2542 }
2543
2544 bool supportsMPE() const override
2545 {
2546 UInt32 dataSize = 0;
2547 Boolean isWritable = false;
2548
2550 kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr
2551 && dataSize == sizeof (UInt32))
2552 {
2553 UInt32 result = 0;
2554
2556 kAudioUnitScope_Global, 0, &result, &dataSize) == noErr)
2557 {
2558 return result > 0;
2559 }
2560 }
2561
2562 return false;
2563 }
2564
2565 //==============================================================================
2567 {
2568 if (processBlockBypassedCalled && bypassParam != nullptr)
2569 {
2570 if (bypassParam->getValue() == 0.0f || ! lastProcessBlockCallWasBypass)
2571 bypassParam->setValue (1.0f);
2572 }
2573 else
2574 {
2576 bypassParam->setValue (0.0f);
2577 }
2578
2580 }
2581
2583};
2584
2585//==============================================================================
2586class AudioUnitPluginWindowCocoa final : public AudioProcessorEditor
2587{
2588public:
2590 : AudioProcessorEditor (&p),
2591 plugin (p)
2592 {
2593 addAndMakeVisible (wrapper);
2594
2595 setOpaque (true);
2596 setVisible (true);
2597 setSize (100, 100);
2598
2599 createView (createGenericViewIfNeeded);
2600 }
2601
2603 {
2604 if (wrapper.getView() != nil)
2605 {
2606 wrapper.setVisible (false);
2607 removeChildComponent (&wrapper);
2608 wrapper.setView (nil);
2609 plugin.editorBeingDeleted (this);
2610 }
2611 }
2612
2614 {
2615 wrapper.setView (pluginView);
2616 waitingForViewCallback = false;
2617
2618 #if JUCE_MAC
2619 if (pluginView != nil)
2620 wrapper.resizeToFitView();
2621 #else
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));
2624 #endif
2625 }
2626
2627 bool isValid() const { return wrapper.getView() != nil || waitingForViewCallback; }
2628
2629 void paint (Graphics& g) override
2630 {
2631 g.fillAll (Colours::white);
2632 }
2633
2634 void resized() override
2635 {
2636 wrapper.setSize (getWidth(), getHeight());
2637 }
2638
2639 void childBoundsChanged (Component*) override
2640 {
2641 setSize (wrapper.getWidth(), wrapper.getHeight());
2642 }
2643
2644private:
2645
2647 AudioUnitFormatHelpers::AutoResizingNSViewComponent wrapper;
2648
2650
2651 bool waitingForViewCallback = false;
2652
2653 bool createView ([[maybe_unused]] bool createGenericViewIfNeeded)
2654 {
2656 UInt32 dataSize = 0;
2657 Boolean isWritable = false;
2658
2659 #if JUCE_MAC
2661 0, &dataSize, &isWritable) == noErr
2662 && dataSize != 0
2664 0, &dataSize, &isWritable) == noErr)
2665 {
2667 info.calloc (dataSize, 1);
2668
2670 0, info, &dataSize) == noErr)
2671 {
2672 NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]);
2673 CFUniquePtr<CFStringRef> path (CFURLCopyPath (info->mCocoaAUViewBundleLocation));
2677
2680 && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)])
2681 {
2682 id factory = [[[viewClass alloc] init] autorelease];
2683 pluginView = [factory uiViewForAudioUnit: plugin.audioUnit
2684 withSize: NSMakeSize (getWidth(), getHeight())];
2685 }
2686
2687 for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;)
2688 CFRelease (info->mCocoaAUViewClass[i]);
2689
2690 CFRelease (info->mCocoaAUViewBundleLocation);
2691 }
2692 }
2693 #endif
2694
2695 dataSize = 0;
2696 isWritable = false;
2697
2699 0, &dataSize, &isWritable) == noErr
2700 && dataSize == sizeof (ViewControllerCallbackBlock))
2701 {
2703 auto callback = ^(AUViewControllerBase* controller) { this->requestViewControllerCallback (controller); };
2704
2706 return true;
2707
2708 waitingForViewCallback = false;
2709 }
2710
2711 #if JUCE_MAC
2713 {
2714 {
2715 // This forces CoreAudio.component to be loaded, otherwise the AUGenericView will assert
2717 String name, version, manufacturer;
2718 AudioUnitFormatHelpers::getComponentDescFromIdentifier ("AudioUnit:Output/auou,genr,appl",
2719 desc, name, version, manufacturer);
2720 }
2721
2723 }
2724 #endif
2725
2726 wrapper.setView (pluginView);
2727
2728 if (pluginView != nil)
2729 wrapper.resizeToFitView();
2730
2731 return pluginView != nil;
2732 }
2733
2735 {
2736 const auto viewSize = [&controller]
2737 {
2738 auto size = CGSizeZero;
2739
2740 if (@available (macOS 10.11, *))
2741 size = [controller preferredContentSize];
2742
2743 if (approximatelyEqual (size.width, 0.0) || approximatelyEqual (size.height, 0.0))
2744 size = controller.view.frame.size;
2745
2746 return CGSizeMake (jmax ((CGFloat) 20.0f, size.width),
2747 jmax ((CGFloat) 20.0f, size.height));
2748 }();
2749
2751 {
2752 struct AsyncViewControllerCallback final : public CallbackMessage
2753 {
2754 AudioUnitPluginWindowCocoa* owner;
2755 JUCE_IOS_MAC_VIEW* controllerView;
2756 CGSize size;
2757
2758 AsyncViewControllerCallback (AudioUnitPluginWindowCocoa* plugInWindow, JUCE_IOS_MAC_VIEW* inView,
2759 const CGSize& preferredSize)
2760 : owner (plugInWindow), controllerView ([inView retain]), size (preferredSize)
2761 {}
2762
2763 void messageCallback() override
2764 {
2765 owner->embedViewController (controllerView, size);
2766 [controllerView release];
2767 }
2768 };
2769
2770 (new AsyncViewControllerCallback (this, [controller view], viewSize))->post();
2771 }
2772 else
2773 {
2774 embedViewController ([controller view], viewSize);
2775 }
2776 }
2777};
2778
2779//==============================================================================
2780AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
2781{
2783
2784 if (! static_cast<AudioUnitPluginWindowCocoa*> (w.get())->isValid())
2785 w.reset (new AudioUnitPluginWindowCocoa (*this, true)); // use AUGenericView as a fallback
2786
2787 return w.release();
2788}
2789
2790//==============================================================================
2791AudioUnitPluginFormat::AudioUnitPluginFormat()
2792{
2793}
2794
2795AudioUnitPluginFormat::~AudioUnitPluginFormat()
2796{
2797}
2798
2800 const String& fileOrIdentifier)
2801{
2802 if (! fileMightContainThisPluginType (fileOrIdentifier))
2803 return;
2804
2805 PluginDescription desc;
2806 desc.fileOrIdentifier = fileOrIdentifier;
2807 desc.uniqueId = desc.deprecatedUid = 0;
2808
2809 if (MessageManager::getInstance()->isThisTheMessageThread()
2810 && requiresUnblockedMessageThreadDuringCreation (desc))
2811 return;
2812
2813 try
2814 {
2815 auto createdInstance = createInstanceFromDescription (desc, 44100.0, 512);
2816
2817 if (auto auInstance = dynamic_cast<AudioUnitPluginInstance*> (createdInstance.get()))
2818 results.add (new PluginDescription (auInstance->getPluginDescription()));
2819 }
2820 catch (...)
2821 {
2822 // crashed while loading...
2823 }
2824}
2825
2826void AudioUnitPluginFormat::createPluginInstance (const PluginDescription& desc,
2827 double rate, int blockSize,
2828 PluginCreationCallback callback)
2829{
2830 auto auComponentResult = getAudioComponent (*this, desc);
2831
2832 if (! auComponentResult.isValid())
2833 {
2834 callback (nullptr, std::move (auComponentResult.errorMessage));
2835 return;
2836 }
2837
2839 [rate, blockSize, origCallback = std::move (callback)] (AudioUnit audioUnit, OSStatus err)
2840 {
2841 if (err == noErr)
2842 {
2843 auto instance = std::make_unique<AudioUnitPluginInstance> (audioUnit);
2844
2845 if (instance->initialise (rate, blockSize))
2846 origCallback (std::move (instance), {});
2847 else
2848 origCallback (nullptr, NEEDS_TRANS ("Unable to initialise the AudioUnit plug-in"));
2849 }
2850 else
2851 {
2852 auto errMsg = TRANS ("An OS error occurred during initialisation of the plug-in (XXX)");
2853 origCallback (nullptr, errMsg.replace ("XXX", String (err)));
2854 }
2855 });
2856}
2857
2858void AudioUnitPluginFormat::createARAFactoryAsync (const PluginDescription& desc, ARAFactoryCreationCallback callback)
2859{
2860 auto auComponentResult = getAudioComponent (*this, desc);
2861
2862 if (! auComponentResult.isValid())
2863 {
2864 callback ({ {}, "Failed to create AudioComponent for " + desc.descriptiveName });
2865 return;
2866 }
2867
2868 getOrCreateARAAudioUnit (auComponentResult.component, [cb = std::move (callback)] (auto dylibKeepAliveAudioUnit)
2869 {
2870 cb ([&]() -> ARAFactoryResult
2871 {
2872 if (dylibKeepAliveAudioUnit != nullptr)
2873 return { ARAFactoryWrapper { ::juce::getARAFactory (std::move (dylibKeepAliveAudioUnit)) }, "" };
2874
2875 return { {}, "Failed to create ARAFactory from the provided AudioUnit" };
2876 }());
2877 });
2878}
2879
2880bool AudioUnitPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription& desc) const
2881{
2882 String pluginName, version, manufacturer;
2883 AudioComponentDescription componentDesc;
2884
2885 if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (desc.fileOrIdentifier, componentDesc,
2886 pluginName, version, manufacturer)
2887 || AudioUnitFormatHelpers::getComponentDescFromFile (desc.fileOrIdentifier, componentDesc,
2888 pluginName, version, manufacturer))
2889 {
2890 if (AudioComponent auComp = AudioComponentFindNext (nullptr, &componentDesc))
2891 {
2892 if (AudioComponentGetDescription (auComp, &componentDesc) == noErr)
2893 return AudioUnitFormatHelpers::isPluginAUv3 (componentDesc);
2894 }
2895 }
2896
2897 return false;
2898}
2899
2900StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath&, bool /*recursive*/, bool allowPluginsWhichRequireAsynchronousInstantiation)
2901{
2902 StringArray result;
2903 AudioComponent comp = nullptr;
2904
2905 for (;;)
2906 {
2907 AudioComponentDescription desc;
2908 zerostruct (desc);
2909
2910 comp = AudioComponentFindNext (comp, &desc);
2911
2912 if (comp == nullptr)
2913 break;
2914
2915 if (AudioComponentGetDescription (comp, &desc) != noErr)
2916 continue;
2917
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)
2925 {
2926 if (allowPluginsWhichRequireAsynchronousInstantiation || ! AudioUnitFormatHelpers::isPluginAUv3 (desc))
2927 result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc));
2928 }
2929 }
2930
2931 return result;
2932}
2933
2934bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
2935{
2936 AudioComponentDescription desc;
2937 String name, version, manufacturer;
2938
2939 if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer))
2940 return AudioComponentFindNext (nullptr, &desc) != nullptr;
2941
2942 auto f = File::createFileWithoutCheckingPath (fileOrIdentifier);
2943
2944 return (f.hasFileExtension (".component") || f.hasFileExtension (".appex"))
2945 && f.isDirectory();
2946}
2947
2948String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
2949{
2950 AudioComponentDescription desc;
2951 String name, version, manufacturer;
2952 AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer);
2953
2954 if (name.isEmpty())
2955 name = fileOrIdentifier;
2956
2957 return name;
2958}
2959
2960bool AudioUnitPluginFormat::pluginNeedsRescanning (const PluginDescription& desc)
2961{
2962 AudioComponentDescription newDesc;
2963 String name, version, manufacturer;
2964
2965 return ! (AudioUnitFormatHelpers::getComponentDescFromIdentifier (desc.fileOrIdentifier, newDesc,
2966 name, version, manufacturer)
2967 && version == desc.version);
2968}
2969
2970bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc)
2971{
2972 if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix))
2973 return fileMightContainThisPluginType (desc.fileOrIdentifier);
2974
2975 return File (desc.fileOrIdentifier).exists();
2976}
2977
2978FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
2979{
2980 return {};
2981}
2982
2983#undef JUCE_AU_LOG
2984
2985} // namespace juce
2986
2987JUCE_END_IGNORE_WARNINGS_GCC_LIKE
2988
2989#endif
T begin(T... args)
static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet(int numChannels)
Create a canonical channel set for a given number of channels.
Optional< double > getBpm() const
Returns the bpm, if available.
Optional< int64_t > getTimeInSamples() const
Returns the number of samples that have elapsed.
Optional< double > getPpqPositionOfLastBarStart() const
The position of the start of the last bar, in units of quarter-notes.
Optional< double > getPpqPosition() const
The current play position, in units of quarter-notes.
Optional< TimeSignature > getTimeSignature() const
Returns the time signature, if available.
Optional< LoopPoints > getLoopPoints() const
Returns host loop points, if available.
bool getIsPlaying() const
True if the transport is currently playing.
bool getIsLooping() const
True if the transport is currently looping.
std::unique_ptr< AudioPluginInstance > createInstanceFromDescription(const PluginDescription &, double initialSampleRate, int initialBufferSize)
Tries to recreate a type from a previously generated PluginDescription.
virtual void processBlockBypassed(AudioBuffer< float > &buffer, MidiBuffer &midiMessages)
Renders the next block when the processor is being bypassed.
void findAllTypesForFile(OwnedArray< PluginDescription > &, const String &fileOrIdentifier) override
This tries to create descriptions for all the plugin types available in a binary module file.
bool fileMightContainThisPluginType(const String &fileOrIdentifier) override
Should do a quick check to see if this file or directory might be a plugin of this format.
bool isThisTheMessageThread() const noexcept
Returns true if the caller-thread is the message thread.
static MessageManager * getInstance()
Returns the global instance of the MessageManager.
static String fromCFString(CFStringRef cfString)
OSX ONLY - Creates a String from an OSX CFString.
static double getMillisecondCounterHiRes() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Returns a Time object that is set to the current system time.
T count(T... args)
T data(T... args)
T empty(T... args)
T end(T... args)
T exchange(T... args)
T format(T... args)
#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...
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
typedef int
T lock(T... args)
typedef float
T max(T... args)
T memcpy(T... args)
T move(T... args)
JUCE Namespace.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
Definition juce_Memory.h:32
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.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
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...
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
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.
Definition juce_Memory.h:28
T push_back(T... args)
T ref(T... args)
T size(T... args)
time