The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2430 lines
93KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../../juce_core/system/juce_TargetPlatform.h"
  20. #include "../utility/juce_CheckSettingMacros.h"
  21. #if JucePlugin_Build_AU
  22. #if __LP64__
  23. #undef JUCE_SUPPORT_CARBON
  24. #define JUCE_SUPPORT_CARBON 0
  25. #endif
  26. #ifdef __clang__
  27. #pragma clang diagnostic push
  28. #pragma clang diagnostic ignored "-Wshorten-64-to-32"
  29. #pragma clang diagnostic ignored "-Wunused-parameter"
  30. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  31. #pragma clang diagnostic ignored "-Wsign-conversion"
  32. #pragma clang diagnostic ignored "-Wconversion"
  33. #pragma clang diagnostic ignored "-Woverloaded-virtual"
  34. #pragma clang diagnostic ignored "-Wextra-semi"
  35. #endif
  36. #include "../utility/juce_IncludeSystemHeaders.h"
  37. #include <AudioUnit/AUCocoaUIView.h>
  38. #include <AudioUnit/AudioUnit.h>
  39. #include <AudioToolbox/AudioUnitUtilities.h>
  40. #include <CoreMIDI/MIDIServices.h>
  41. #include <QuartzCore/QuartzCore.h>
  42. #include "CoreAudioUtilityClasses/MusicDeviceBase.h"
  43. /** The BUILD_AU_CARBON_UI flag lets you specify whether old-school carbon hosts are supported as
  44. well as ones that can open a cocoa view. If this is enabled, you'll need to also add the AUCarbonBase
  45. files to your project.
  46. */
  47. #if ! (defined (BUILD_AU_CARBON_UI) || JUCE_64BIT)
  48. #define BUILD_AU_CARBON_UI 1
  49. #endif
  50. #ifdef __LP64__
  51. #undef BUILD_AU_CARBON_UI // (not possible in a 64-bit build)
  52. #endif
  53. #if BUILD_AU_CARBON_UI
  54. #include "CoreAudioUtilityClasses/AUCarbonViewBase.h"
  55. #endif
  56. #ifdef __clang__
  57. #pragma clang diagnostic pop
  58. #endif
  59. #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1
  60. #define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
  61. #include "../utility/juce_IncludeModuleHeaders.h"
  62. #include "../utility/juce_FakeMouseMoveGenerator.h"
  63. #include "../utility/juce_CarbonVisibility.h"
  64. #include "../../juce_audio_basics/native/juce_mac_CoreAudioLayouts.h"
  65. #include "../../juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp"
  66. #include "../../juce_audio_processors/format_types/juce_AU_Shared.h"
  67. //==============================================================================
  68. using namespace juce;
  69. static Array<void*> activePlugins, activeUIs;
  70. static const AudioUnitPropertyID juceFilterObjectPropertyID = 0x1a45ffe9;
  71. template <> struct ContainerDeletePolicy<const __CFString> { static void destroy (const __CFString* o) { if (o != nullptr) CFRelease (o); } };
  72. // make sure the audio processor is initialized before the AUBase class
  73. struct AudioProcessorHolder
  74. {
  75. AudioProcessorHolder (bool initialiseGUI)
  76. {
  77. if (initialiseGUI)
  78. {
  79. #if BUILD_AU_CARBON_UI
  80. NSApplicationLoad();
  81. #endif
  82. initialiseJuce_GUI();
  83. }
  84. juceFilter.reset (createPluginFilterOfType (AudioProcessor::wrapperType_AudioUnit));
  85. // audio units do not have a notion of enabled or un-enabled buses
  86. juceFilter->enableAllBuses();
  87. }
  88. ScopedPointer<AudioProcessor> juceFilter;
  89. };
  90. //==============================================================================
  91. class JuceAU : public AudioProcessorHolder,
  92. public MusicDeviceBase,
  93. public AudioProcessorListener,
  94. public AudioPlayHead,
  95. public ComponentListener,
  96. public AudioProcessorParameter::Listener
  97. {
  98. public:
  99. JuceAU (AudioUnit component)
  100. : AudioProcessorHolder(activePlugins.size() + activeUIs.size() == 0),
  101. MusicDeviceBase (component,
  102. (UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), true),
  103. (UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), false)),
  104. isBypassed (false),
  105. mapper (*juceFilter)
  106. {
  107. inParameterChangedCallback = false;
  108. #ifdef JucePlugin_PreferredChannelConfigurations
  109. short configs[][2] = {JucePlugin_PreferredChannelConfigurations};
  110. const int numConfigs = sizeof (configs) / sizeof (short[2]);
  111. jassert (numConfigs > 0 && (configs[0][0] > 0 || configs[0][1] > 0));
  112. juceFilter->setPlayConfigDetails (configs[0][0], configs[0][1], 44100.0, 1024);
  113. for (int i = 0; i < numConfigs; ++i)
  114. {
  115. AUChannelInfo info;
  116. info.inChannels = configs[i][0];
  117. info.outChannels = configs[i][1];
  118. channelInfo.add (info);
  119. }
  120. #else
  121. channelInfo = AudioUnitHelpers::getAUChannelInfo (*juceFilter);
  122. #endif
  123. AddPropertyListener (kAudioUnitProperty_ContextName, auPropertyListenerDispatcher, this);
  124. totalInChannels = juceFilter->getTotalNumInputChannels();
  125. totalOutChannels = juceFilter->getTotalNumOutputChannels();
  126. juceFilter->setPlayHead (this);
  127. juceFilter->addListener (this);
  128. addParameters();
  129. activePlugins.add (this);
  130. zerostruct (auEvent);
  131. auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance();
  132. auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  133. auEvent.mArgument.mParameter.mElement = 0;
  134. zerostruct (midiCallback);
  135. CreateElements();
  136. if (syncAudioUnitWithProcessor() != noErr)
  137. jassertfalse;
  138. }
  139. ~JuceAU()
  140. {
  141. if (bypassParam != nullptr)
  142. bypassParam->removeListener (this);
  143. deleteActiveEditors();
  144. juceFilter = nullptr;
  145. clearPresetsArray();
  146. jassert (activePlugins.contains (this));
  147. activePlugins.removeFirstMatchingValue (this);
  148. if (activePlugins.size() + activeUIs.size() == 0)
  149. shutdownJuce_GUI();
  150. }
  151. //==============================================================================
  152. ComponentResult Initialize() override
  153. {
  154. ComponentResult err;
  155. if ((err = syncProcessorWithAudioUnit()) != noErr)
  156. return err;
  157. if ((err = MusicDeviceBase::Initialize()) != noErr)
  158. return err;
  159. mapper.alloc();
  160. pulledSucceeded.calloc (static_cast<size_t> (AudioUnitHelpers::getBusCount (juceFilter.get(), true)));
  161. prepareToPlay();
  162. return noErr;
  163. }
  164. void Cleanup() override
  165. {
  166. MusicDeviceBase::Cleanup();
  167. pulledSucceeded.free();
  168. mapper.release();
  169. if (juceFilter != nullptr)
  170. juceFilter->releaseResources();
  171. audioBuffer.release();
  172. midiEvents.clear();
  173. incomingEvents.clear();
  174. prepared = false;
  175. }
  176. ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement) override
  177. {
  178. if (! prepared)
  179. prepareToPlay();
  180. if (juceFilter != nullptr)
  181. juceFilter->reset();
  182. return MusicDeviceBase::Reset (inScope, inElement);
  183. }
  184. //==============================================================================
  185. void prepareToPlay()
  186. {
  187. if (juceFilter != nullptr)
  188. {
  189. juceFilter->setRateAndBufferSizeDetails (getSampleRate(), (int) GetMaxFramesPerSlice());
  190. audioBuffer.prepare (totalInChannels, totalOutChannels, (int) GetMaxFramesPerSlice() + 32);
  191. juceFilter->prepareToPlay (getSampleRate(), (int) GetMaxFramesPerSlice());
  192. midiEvents.ensureSize (2048);
  193. midiEvents.clear();
  194. incomingEvents.ensureSize (2048);
  195. incomingEvents.clear();
  196. prepared = true;
  197. }
  198. }
  199. //==============================================================================
  200. static OSStatus ComponentEntryDispatch (ComponentParameters* params, JuceAU* effect)
  201. {
  202. if (effect == nullptr)
  203. return paramErr;
  204. switch (params->what)
  205. {
  206. case kMusicDeviceMIDIEventSelect:
  207. case kMusicDeviceSysExSelect:
  208. return AUMIDIBase::ComponentEntryDispatch (params, effect);
  209. default:
  210. break;
  211. }
  212. return MusicDeviceBase::ComponentEntryDispatch (params, effect);
  213. }
  214. //==============================================================================
  215. bool BusCountWritable (AudioUnitScope scope) override
  216. {
  217. #ifdef JucePlugin_PreferredChannelConfigurations
  218. ignoreUnused (scope);
  219. return false;
  220. #else
  221. bool isInput;
  222. if (scopeToDirection (scope, isInput) != noErr)
  223. return false;
  224. #if JucePlugin_IsMidiEffect
  225. return false;
  226. #elif JucePlugin_IsSynth
  227. if (isInput) return false;
  228. #endif
  229. const int busCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput);
  230. return (juceFilter->canAddBus (isInput) || (busCount > 0 && juceFilter->canRemoveBus (isInput)));
  231. #endif
  232. }
  233. OSStatus SetBusCount (AudioUnitScope scope, UInt32 count) override
  234. {
  235. OSStatus err = noErr;
  236. bool isInput;
  237. if ((err = scopeToDirection (scope, isInput)) != noErr)
  238. return err;
  239. if (count != (UInt32) AudioUnitHelpers::getBusCount (juceFilter.get(), isInput))
  240. {
  241. #ifdef JucePlugin_PreferredChannelConfigurations
  242. return kAudioUnitErr_PropertyNotWritable;
  243. #else
  244. const int busCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput);
  245. if ((! juceFilter->canAddBus (isInput)) && ((busCount == 0) || (! juceFilter->canRemoveBus (isInput))))
  246. return kAudioUnitErr_PropertyNotWritable;
  247. // we need to already create the underlying elements so that we can change their formats
  248. err = MusicDeviceBase::SetBusCount (scope, count);
  249. if (err != noErr)
  250. return err;
  251. // however we do need to update the format tag: we need to do the same thing in SetFormat, for example
  252. const int requestedNumBus = static_cast<int> (count);
  253. {
  254. (isInput ? currentInputLayout : currentOutputLayout).resize (requestedNumBus);
  255. int busNr;
  256. for (busNr = (busCount - 1); busNr != (requestedNumBus - 1); busNr += (requestedNumBus > busCount ? 1 : -1))
  257. {
  258. if (requestedNumBus > busCount)
  259. {
  260. if (! juceFilter->addBus (isInput))
  261. break;
  262. err = syncAudioUnitWithChannelSet (isInput, busNr,
  263. juceFilter->getBus (isInput, busNr + 1)->getDefaultLayout());
  264. if (err != noErr)
  265. break;
  266. }
  267. else
  268. {
  269. if (! juceFilter->removeBus (isInput))
  270. break;
  271. }
  272. }
  273. err = (busNr == (requestedNumBus - 1) ? noErr : kAudioUnitErr_FormatNotSupported);
  274. }
  275. // was there an error?
  276. if (err != noErr)
  277. {
  278. // restore bus state
  279. const int newBusCount = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput);
  280. for (int i = newBusCount; i != busCount; i += (busCount > newBusCount ? 1 : -1))
  281. {
  282. if (busCount > newBusCount)
  283. juceFilter->addBus (isInput);
  284. else
  285. juceFilter->removeBus (isInput);
  286. }
  287. (isInput ? currentInputLayout : currentOutputLayout).resize (busCount);
  288. MusicDeviceBase::SetBusCount (scope, static_cast<UInt32> (busCount));
  289. return kAudioUnitErr_FormatNotSupported;
  290. }
  291. // update total channel count
  292. totalInChannels = juceFilter->getTotalNumInputChannels();
  293. totalOutChannels = juceFilter->getTotalNumOutputChannels();
  294. if (err != noErr)
  295. return err;
  296. #endif
  297. }
  298. return noErr;
  299. }
  300. UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) override
  301. {
  302. if (outInfo != nullptr)
  303. *outInfo = channelInfo.getRawDataPointer();
  304. return (UInt32) channelInfo.size();
  305. }
  306. //==============================================================================
  307. ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,
  308. AudioUnitScope inScope,
  309. AudioUnitElement inElement,
  310. UInt32& outDataSize,
  311. Boolean& outWritable) override
  312. {
  313. if (inScope == kAudioUnitScope_Global)
  314. {
  315. switch (inID)
  316. {
  317. case juceFilterObjectPropertyID:
  318. outWritable = false;
  319. outDataSize = sizeof (void*) * 2;
  320. return noErr;
  321. case kAudioUnitProperty_OfflineRender:
  322. outWritable = true;
  323. outDataSize = sizeof (UInt32);
  324. return noErr;
  325. case kMusicDeviceProperty_InstrumentCount:
  326. outDataSize = sizeof (UInt32);
  327. outWritable = false;
  328. return noErr;
  329. case kAudioUnitProperty_CocoaUI:
  330. outDataSize = sizeof (AudioUnitCocoaViewInfo);
  331. outWritable = true;
  332. return noErr;
  333. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
  334. case kAudioUnitProperty_MIDIOutputCallbackInfo:
  335. outDataSize = sizeof (CFArrayRef);
  336. outWritable = false;
  337. return noErr;
  338. case kAudioUnitProperty_MIDIOutputCallback:
  339. outDataSize = sizeof (AUMIDIOutputCallbackStruct);
  340. outWritable = true;
  341. return noErr;
  342. #endif
  343. case kAudioUnitProperty_ParameterStringFromValue:
  344. outDataSize = sizeof (AudioUnitParameterStringFromValue);
  345. outWritable = false;
  346. return noErr;
  347. case kAudioUnitProperty_ParameterValueFromString:
  348. outDataSize = sizeof (AudioUnitParameterValueFromString);
  349. outWritable = false;
  350. return noErr;
  351. case kAudioUnitProperty_BypassEffect:
  352. outDataSize = sizeof (UInt32);
  353. outWritable = true;
  354. return noErr;
  355. case kAudioUnitProperty_SupportsMPE:
  356. outDataSize = sizeof (UInt32);
  357. outWritable = false;
  358. return noErr;
  359. default: break;
  360. }
  361. }
  362. return MusicDeviceBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
  363. }
  364. ComponentResult GetProperty (AudioUnitPropertyID inID,
  365. AudioUnitScope inScope,
  366. AudioUnitElement inElement,
  367. void* outData) override
  368. {
  369. if (inScope == kAudioUnitScope_Global)
  370. {
  371. switch (inID)
  372. {
  373. case juceFilterObjectPropertyID:
  374. ((void**) outData)[0] = (void*) static_cast<AudioProcessor*> (juceFilter.get());
  375. ((void**) outData)[1] = (void*) this;
  376. return noErr;
  377. case kAudioUnitProperty_OfflineRender:
  378. *(UInt32*) outData = (juceFilter != nullptr && juceFilter->isNonRealtime()) ? 1 : 0;
  379. return noErr;
  380. case kMusicDeviceProperty_InstrumentCount:
  381. *(UInt32*) outData = 1;
  382. return noErr;
  383. case kAudioUnitProperty_BypassEffect:
  384. if (bypassParam != nullptr)
  385. *(UInt32*) outData = (bypassParam->getValue() != 0.0f ? 1 : 0);
  386. else
  387. *(UInt32*) outData = isBypassed ? 1 : 0;
  388. return noErr;
  389. case kAudioUnitProperty_SupportsMPE:
  390. *(UInt32*) outData = (juceFilter != nullptr && juceFilter->supportsMPE()) ? 1 : 0;
  391. return noErr;
  392. case kAudioUnitProperty_CocoaUI:
  393. {
  394. JUCE_AUTORELEASEPOOL
  395. {
  396. static JuceUICreationClass cls;
  397. // (NB: this may be the host's bundle, not necessarily the component's)
  398. NSBundle* bundle = [NSBundle bundleForClass: cls.cls];
  399. AudioUnitCocoaViewInfo* info = static_cast<AudioUnitCocoaViewInfo*> (outData);
  400. info->mCocoaAUViewClass[0] = (CFStringRef) [juceStringToNS (class_getName (cls.cls)) retain];
  401. info->mCocoaAUViewBundleLocation = (CFURLRef) [[NSURL fileURLWithPath: [bundle bundlePath]] retain];
  402. }
  403. return noErr;
  404. }
  405. break;
  406. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
  407. case kAudioUnitProperty_MIDIOutputCallbackInfo:
  408. {
  409. CFStringRef strs[1];
  410. strs[0] = CFSTR ("MIDI Callback");
  411. CFArrayRef callbackArray = CFArrayCreate (nullptr, (const void**) strs, 1, &kCFTypeArrayCallBacks);
  412. *(CFArrayRef*) outData = callbackArray;
  413. return noErr;
  414. }
  415. #endif
  416. case kAudioUnitProperty_ParameterValueFromString:
  417. {
  418. if (AudioUnitParameterValueFromString* pv = (AudioUnitParameterValueFromString*) outData)
  419. {
  420. if (juceFilter != nullptr)
  421. {
  422. if (auto* param = getParameterForAUParameterID (pv->inParamID))
  423. {
  424. const String text (String::fromCFString (pv->inString));
  425. if (LegacyAudioParameter::isLegacy (param))
  426. pv->outValue = text.getFloatValue();
  427. else
  428. pv->outValue = param->getValueForText (text) * getMaximumParameterValue (param);
  429. return noErr;
  430. }
  431. }
  432. }
  433. }
  434. break;
  435. case kAudioUnitProperty_ParameterStringFromValue:
  436. {
  437. if (AudioUnitParameterStringFromValue* pv = (AudioUnitParameterStringFromValue*) outData)
  438. {
  439. if (juceFilter != nullptr)
  440. {
  441. if (auto* param = getParameterForAUParameterID (pv->inParamID))
  442. {
  443. const float value = (float) *(pv->inValue);
  444. String text;
  445. if (LegacyAudioParameter::isLegacy (param))
  446. text = String (value);
  447. else
  448. text = param->getText (value / getMaximumParameterValue (param), 0);
  449. pv->outString = text.toCFString();
  450. return noErr;
  451. }
  452. }
  453. }
  454. }
  455. break;
  456. default:
  457. break;
  458. }
  459. }
  460. return MusicDeviceBase::GetProperty (inID, inScope, inElement, outData);
  461. }
  462. ComponentResult SetProperty (AudioUnitPropertyID inID,
  463. AudioUnitScope inScope,
  464. AudioUnitElement inElement,
  465. const void* inData,
  466. UInt32 inDataSize) override
  467. {
  468. if (inScope == kAudioUnitScope_Global)
  469. {
  470. switch (inID)
  471. {
  472. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
  473. case kAudioUnitProperty_MIDIOutputCallback:
  474. if (inDataSize < sizeof (AUMIDIOutputCallbackStruct))
  475. return kAudioUnitErr_InvalidPropertyValue;
  476. if (AUMIDIOutputCallbackStruct* callbackStruct = (AUMIDIOutputCallbackStruct*) inData)
  477. midiCallback = *callbackStruct;
  478. return noErr;
  479. #endif
  480. case kAudioUnitProperty_BypassEffect:
  481. {
  482. if (inDataSize < sizeof (UInt32))
  483. return kAudioUnitErr_InvalidPropertyValue;
  484. const bool newBypass = *((UInt32*) inData) != 0;
  485. const bool currentlyBypassed = (bypassParam != nullptr ? (bypassParam->getValue() != 0.0f) : isBypassed);
  486. if (newBypass != currentlyBypassed)
  487. {
  488. if (bypassParam != nullptr)
  489. bypassParam->setValueNotifyingHost (newBypass ? 1.0f : 0.0f);
  490. else
  491. isBypassed = newBypass;
  492. if (! currentlyBypassed && IsInitialized()) // turning bypass off and we're initialized
  493. Reset (0, 0);
  494. }
  495. return noErr;
  496. }
  497. case kAudioUnitProperty_OfflineRender:
  498. {
  499. auto shouldBeRealtime = (*reinterpret_cast<const UInt32*> (inData) != 0);
  500. if (juceFilter != nullptr)
  501. {
  502. auto isCurrentlyRealtime = juceFilter->isNonRealtime();
  503. if (isCurrentlyRealtime != shouldBeRealtime)
  504. {
  505. const ScopedLock sl (juceFilter->getCallbackLock());
  506. juceFilter->setNonRealtime (shouldBeRealtime);
  507. juceFilter->prepareToPlay (getSampleRate(), (int) GetMaxFramesPerSlice());
  508. }
  509. }
  510. return noErr;
  511. }
  512. default: break;
  513. }
  514. }
  515. return MusicDeviceBase::SetProperty (inID, inScope, inElement, inData, inDataSize);
  516. }
  517. //==============================================================================
  518. ComponentResult SaveState (CFPropertyListRef* outData) override
  519. {
  520. ComponentResult err = MusicDeviceBase::SaveState (outData);
  521. if (err != noErr)
  522. return err;
  523. jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID());
  524. CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData;
  525. if (juceFilter != nullptr)
  526. {
  527. juce::MemoryBlock state;
  528. juceFilter->getCurrentProgramStateInformation (state);
  529. if (state.getSize() > 0)
  530. {
  531. CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const UInt8*) state.getData(), (CFIndex) state.getSize());
  532. CFStringRef key = CFStringCreateWithCString (kCFAllocatorDefault, JUCE_STATE_DICTIONARY_KEY, kCFStringEncodingUTF8);
  533. CFDictionarySetValue (dict, key, ourState);
  534. CFRelease (key);
  535. CFRelease (ourState);
  536. }
  537. }
  538. return noErr;
  539. }
  540. ComponentResult RestoreState (CFPropertyListRef inData) override
  541. {
  542. {
  543. // Remove the data entry from the state to prevent the superclass loading the parameters
  544. CFMutableDictionaryRef copyWithoutData = CFDictionaryCreateMutableCopy (nullptr, 0, (CFDictionaryRef) inData);
  545. CFDictionaryRemoveValue (copyWithoutData, CFSTR (kAUPresetDataKey));
  546. ComponentResult err = MusicDeviceBase::RestoreState (copyWithoutData);
  547. CFRelease (copyWithoutData);
  548. if (err != noErr)
  549. return err;
  550. }
  551. if (juceFilter != nullptr)
  552. {
  553. CFDictionaryRef dict = (CFDictionaryRef) inData;
  554. CFDataRef data = 0;
  555. CFStringRef key = CFStringCreateWithCString (kCFAllocatorDefault, JUCE_STATE_DICTIONARY_KEY, kCFStringEncodingUTF8);
  556. bool valuePresent = CFDictionaryGetValueIfPresent (dict, key, (const void**) &data);
  557. CFRelease (key);
  558. if (valuePresent)
  559. {
  560. if (data != 0)
  561. {
  562. const int numBytes = (int) CFDataGetLength (data);
  563. const juce::uint8* const rawBytes = CFDataGetBytePtr (data);
  564. if (numBytes > 0)
  565. juceFilter->setCurrentProgramStateInformation (rawBytes, numBytes);
  566. }
  567. }
  568. }
  569. return noErr;
  570. }
  571. //==============================================================================
  572. bool busIgnoresLayout (bool isInput, int busNr) const
  573. {
  574. #ifdef JucePlugin_PreferredChannelConfigurations
  575. ignoreUnused (isInput, busNr);
  576. return true;
  577. #else
  578. if (const AudioProcessor::Bus* bus = juceFilter->getBus (isInput, busNr))
  579. {
  580. AudioChannelSet discreteRangeSet;
  581. const int n = bus->getDefaultLayout().size();
  582. for (int i = 0; i < n; ++i)
  583. discreteRangeSet.addChannel ((AudioChannelSet::ChannelType) (256 + i));
  584. // if the audioprocessor supports this it cannot
  585. // really be interested in the bus layouts
  586. return bus->isLayoutSupported (discreteRangeSet);
  587. }
  588. return true;
  589. #endif
  590. }
  591. UInt32 GetAudioChannelLayout (AudioUnitScope scope, AudioUnitElement element,
  592. AudioChannelLayout* outLayoutPtr, Boolean& outWritable) override
  593. {
  594. bool isInput;
  595. int busNr;
  596. outWritable = false;
  597. if (elementToBusIdx (scope, element, isInput, busNr) != noErr)
  598. return 0;
  599. if (busIgnoresLayout (isInput, busNr))
  600. return 0;
  601. outWritable = true;
  602. const size_t sizeInBytes = sizeof (AudioChannelLayout) - sizeof (AudioChannelDescription);
  603. if (outLayoutPtr != nullptr)
  604. {
  605. zeromem (outLayoutPtr, sizeInBytes);
  606. outLayoutPtr->mChannelLayoutTag = getCurrentLayout (isInput, busNr);
  607. }
  608. return sizeInBytes;
  609. }
  610. UInt32 GetChannelLayoutTags (AudioUnitScope scope, AudioUnitElement element, AudioChannelLayoutTag* outLayoutTags) override
  611. {
  612. bool isInput;
  613. int busNr;
  614. if (elementToBusIdx (scope, element, isInput, busNr) != noErr)
  615. return 0;
  616. if (busIgnoresLayout (isInput, busNr))
  617. return 0;
  618. const Array<AudioChannelLayoutTag>& layouts = getSupportedBusLayouts (isInput, busNr);
  619. if (outLayoutTags != nullptr)
  620. std::copy (layouts.begin(), layouts.end(), outLayoutTags);
  621. return (UInt32) layouts.size();
  622. }
  623. OSStatus SetAudioChannelLayout (AudioUnitScope scope, AudioUnitElement element, const AudioChannelLayout* inLayout) override
  624. {
  625. bool isInput;
  626. int busNr;
  627. OSStatus err;
  628. if ((err = elementToBusIdx (scope, element, isInput, busNr)) != noErr)
  629. return err;
  630. if (busIgnoresLayout (isInput, busNr))
  631. return kAudioUnitErr_PropertyNotWritable;
  632. if (inLayout == nullptr)
  633. return kAudioUnitErr_InvalidPropertyValue;
  634. if (const AUIOElement* ioElement = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, element))
  635. {
  636. const AudioChannelSet newChannelSet = CoreAudioLayouts::fromCoreAudio (*inLayout);
  637. const int currentNumChannels = static_cast<int> (ioElement->GetStreamFormat().NumberChannels());
  638. const int newChannelNum = newChannelSet.size();
  639. if (currentNumChannels != newChannelNum)
  640. return kAudioUnitErr_InvalidPropertyValue;
  641. // check if the new layout could be potentially set
  642. #ifdef JucePlugin_PreferredChannelConfigurations
  643. short configs[][2] = {JucePlugin_PreferredChannelConfigurations};
  644. if (! AudioUnitHelpers::isLayoutSupported (*juceFilter, isInput, busNr, newChannelNum, configs))
  645. return kAudioUnitErr_FormatNotSupported;
  646. #else
  647. if (! juceFilter->getBus (isInput, busNr)->isLayoutSupported (newChannelSet))
  648. return kAudioUnitErr_FormatNotSupported;
  649. #endif
  650. getCurrentLayout (isInput, busNr) = CoreAudioLayouts::toCoreAudio (newChannelSet);
  651. return noErr;
  652. }
  653. else
  654. jassertfalse;
  655. return kAudioUnitErr_InvalidElement;
  656. }
  657. //==============================================================================
  658. // When parameters are discrete we need to use integer values.
  659. float getMaximumParameterValue (AudioProcessorParameter* param)
  660. {
  661. return param->isDiscrete() && (! forceUseLegacyParamIDs) ? (float) (param->getNumSteps() - 1) : 1.0f;
  662. }
  663. ComponentResult GetParameterInfo (AudioUnitScope inScope,
  664. AudioUnitParameterID inParameterID,
  665. AudioUnitParameterInfo& outParameterInfo) override
  666. {
  667. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  668. {
  669. if (auto* param = getParameterForAUParameterID (inParameterID))
  670. {
  671. outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
  672. outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
  673. | kAudioUnitParameterFlag_IsReadable
  674. | kAudioUnitParameterFlag_HasCFNameString
  675. | kAudioUnitParameterFlag_ValuesHaveStrings);
  676. #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
  677. outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
  678. #endif
  679. const String name = param->getName (1024);
  680. // Set whether the param is automatable (unnamed parameters aren't allowed to be automated)
  681. if (name.isEmpty() || ! param->isAutomatable())
  682. outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
  683. const bool isParameterDiscrete = param->isDiscrete();
  684. if (! isParameterDiscrete)
  685. outParameterInfo.flags |= kAudioUnitParameterFlag_CanRamp;
  686. if (param->isMetaParameter())
  687. outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
  688. // Is this a meter?
  689. if (((param->getCategory() & 0xffff0000) >> 16) == 2)
  690. {
  691. outParameterInfo.flags &= ~kAudioUnitParameterFlag_IsWritable;
  692. outParameterInfo.flags |= kAudioUnitParameterFlag_MeterReadOnly | kAudioUnitParameterFlag_DisplayLogarithmic;
  693. outParameterInfo.unit = kAudioUnitParameterUnit_LinearGain;
  694. }
  695. else
  696. {
  697. #if ! JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
  698. if (isParameterDiscrete)
  699. {
  700. outParameterInfo.unit = kAudioUnitParameterUnit_Indexed;
  701. if (param->isBoolean())
  702. outParameterInfo.unit = kAudioUnitParameterUnit_Boolean;
  703. }
  704. #endif
  705. }
  706. MusicDeviceBase::FillInParameterName (outParameterInfo, name.toCFString(), true);
  707. outParameterInfo.minValue = 0.0f;
  708. outParameterInfo.maxValue = getMaximumParameterValue (param);
  709. outParameterInfo.defaultValue = param->getDefaultValue() * getMaximumParameterValue (param);
  710. jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue
  711. && outParameterInfo.defaultValue <= outParameterInfo.maxValue);
  712. return noErr;
  713. }
  714. }
  715. return kAudioUnitErr_InvalidParameter;
  716. }
  717. ComponentResult GetParameterValueStrings (AudioUnitScope inScope,
  718. AudioUnitParameterID inParameterID,
  719. CFArrayRef *outStrings) override
  720. {
  721. if (outStrings == nullptr)
  722. return noErr;
  723. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  724. {
  725. if (auto* param = getParameterForAUParameterID (inParameterID))
  726. {
  727. if (param->isDiscrete())
  728. {
  729. auto index = LegacyAudioParameter::getParamIndex (*juceFilter, param);
  730. if (auto* valueStrings = parameterValueStringArrays[index])
  731. {
  732. *outStrings = CFArrayCreate (NULL,
  733. (const void **) valueStrings->getRawDataPointer(),
  734. valueStrings->size(),
  735. NULL);
  736. return noErr;
  737. }
  738. }
  739. }
  740. }
  741. return kAudioUnitErr_InvalidParameter;
  742. }
  743. ComponentResult GetParameter (AudioUnitParameterID inID,
  744. AudioUnitScope inScope,
  745. AudioUnitElement inElement,
  746. Float32& outValue) override
  747. {
  748. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  749. {
  750. if (auto* param = getParameterForAUParameterID (inID))
  751. {
  752. const auto normValue = param->getValue();
  753. outValue = normValue * getMaximumParameterValue (param);
  754. return noErr;
  755. }
  756. }
  757. return MusicDeviceBase::GetParameter (inID, inScope, inElement, outValue);
  758. }
  759. ComponentResult SetParameter (AudioUnitParameterID inID,
  760. AudioUnitScope inScope,
  761. AudioUnitElement inElement,
  762. Float32 inValue,
  763. UInt32 inBufferOffsetInFrames) override
  764. {
  765. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  766. {
  767. if (auto* param = getParameterForAUParameterID (inID))
  768. {
  769. auto value = inValue / getMaximumParameterValue (param);
  770. param->setValue (value);
  771. inParameterChangedCallback = true;
  772. param->sendValueChangedMessageToListeners (value);
  773. return noErr;
  774. }
  775. }
  776. return MusicDeviceBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames);
  777. }
  778. // No idea what this method actually does or what it should return. Current Apple docs say nothing about it.
  779. // (Note that this isn't marked 'override' in case older versions of the SDK don't include it)
  780. bool CanScheduleParameters() const override { return false; }
  781. //==============================================================================
  782. ComponentResult Version() override { return JucePlugin_VersionCode; }
  783. bool SupportsTail() override { return true; }
  784. Float64 GetTailTime() override { return juceFilter->getTailLengthSeconds(); }
  785. double getSampleRate() { return AudioUnitHelpers::getBusCount (juceFilter.get(), false) > 0 ? GetOutput (0)->GetStreamFormat().mSampleRate : 44100.0; }
  786. Float64 GetLatency() override
  787. {
  788. const double rate = getSampleRate();
  789. jassert (rate > 0);
  790. return rate > 0 ? juceFilter->getLatencySamples() / rate : 0;
  791. }
  792. //==============================================================================
  793. #if BUILD_AU_CARBON_UI
  794. int GetNumCustomUIComponents() override
  795. {
  796. return getHostType().isDigitalPerformer() ? 0 : 1;
  797. }
  798. void GetUIComponentDescs (ComponentDescription* inDescArray) override
  799. {
  800. inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;
  801. inDescArray[0].componentSubType = JucePlugin_AUSubType;
  802. inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode;
  803. inDescArray[0].componentFlags = 0;
  804. inDescArray[0].componentFlagsMask = 0;
  805. }
  806. #endif
  807. //==============================================================================
  808. bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) override
  809. {
  810. info.timeSigNumerator = 0;
  811. info.timeSigDenominator = 0;
  812. info.editOriginTime = 0;
  813. info.ppqPositionOfLastBarStart = 0;
  814. info.isRecording = false;
  815. switch (lastTimeStamp.mSMPTETime.mType)
  816. {
  817. case kSMPTETimeType2398: info.frameRate = AudioPlayHead::fps23976; break;
  818. case kSMPTETimeType24: info.frameRate = AudioPlayHead::fps24; break;
  819. case kSMPTETimeType25: info.frameRate = AudioPlayHead::fps25; break;
  820. case kSMPTETimeType30Drop: info.frameRate = AudioPlayHead::fps30drop; break;
  821. case kSMPTETimeType30: info.frameRate = AudioPlayHead::fps30; break;
  822. case kSMPTETimeType2997: info.frameRate = AudioPlayHead::fps2997; break;
  823. case kSMPTETimeType2997Drop: info.frameRate = AudioPlayHead::fps2997drop; break;
  824. case kSMPTETimeType60: info.frameRate = AudioPlayHead::fps60; break;
  825. case kSMPTETimeType60Drop: info.frameRate = AudioPlayHead::fps60drop; break;
  826. default: info.frameRate = AudioPlayHead::fpsUnknown; break;
  827. }
  828. if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)
  829. {
  830. info.ppqPosition = 0;
  831. info.bpm = 0;
  832. }
  833. UInt32 outDeltaSampleOffsetToNextBeat;
  834. double outCurrentMeasureDownBeat;
  835. float num;
  836. UInt32 den;
  837. if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,
  838. &outCurrentMeasureDownBeat) == noErr)
  839. {
  840. info.timeSigNumerator = (int) num;
  841. info.timeSigDenominator = (int) den;
  842. info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
  843. }
  844. double outCurrentSampleInTimeLine, outCycleStartBeat = 0, outCycleEndBeat = 0;
  845. Boolean playing = false, looping = false, playchanged;
  846. if (CallHostTransportState (&playing,
  847. &playchanged,
  848. &outCurrentSampleInTimeLine,
  849. &looping,
  850. &outCycleStartBeat,
  851. &outCycleEndBeat) != noErr)
  852. {
  853. // If the host doesn't support this callback, then use the sample time from lastTimeStamp:
  854. outCurrentSampleInTimeLine = lastTimeStamp.mSampleTime;
  855. }
  856. info.isPlaying = playing;
  857. info.timeInSamples = (int64) (outCurrentSampleInTimeLine + 0.5);
  858. info.timeInSeconds = info.timeInSamples / getSampleRate();
  859. info.isLooping = looping;
  860. info.ppqLoopStart = outCycleStartBeat;
  861. info.ppqLoopEnd = outCycleEndBeat;
  862. return true;
  863. }
  864. void sendAUEvent (const AudioUnitEventType type, const int juceParamIndex)
  865. {
  866. auEvent.mEventType = type;
  867. auEvent.mArgument.mParameter.mParameterID = getAUParameterIDForIndex (juceParamIndex);
  868. AUEventListenerNotify (0, 0, &auEvent);
  869. }
  870. void audioProcessorParameterChanged (AudioProcessor*, int index, float /*newValue*/) override
  871. {
  872. if (inParameterChangedCallback.get())
  873. {
  874. inParameterChangedCallback = false;
  875. return;
  876. }
  877. sendAUEvent (kAudioUnitEvent_ParameterValueChange, index);
  878. }
  879. void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override
  880. {
  881. sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index);
  882. }
  883. void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override
  884. {
  885. sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
  886. }
  887. void audioProcessorChanged (AudioProcessor*) override
  888. {
  889. PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
  890. PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
  891. PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0);
  892. PropertyChanged (kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0);
  893. refreshCurrentPreset();
  894. PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
  895. }
  896. //==============================================================================
  897. // this will only ever be called by the bypass parameter
  898. void parameterValueChanged (int, float) override
  899. {
  900. PropertyChanged (kAudioUnitProperty_BypassEffect, kAudioUnitScope_Global, 0);
  901. }
  902. void parameterGestureChanged (int, bool) override {}
  903. //==============================================================================
  904. bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) override
  905. {
  906. bool ignore;
  907. int busIdx;
  908. return ((! IsInitialized()) && (elementToBusIdx (scope, element, ignore, busIdx) == noErr));
  909. }
  910. bool ValidFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& format) override
  911. {
  912. bool isInput;
  913. int busNr;
  914. // DSP Quattro incorrectly uses global scope for the ValidFormat call
  915. if (scope == kAudioUnitScope_Global)
  916. return ValidFormat (kAudioUnitScope_Input, element, format)
  917. || ValidFormat (kAudioUnitScope_Output, element, format);
  918. if (elementToBusIdx (scope, element, isInput, busNr) != noErr)
  919. return false;
  920. const int newNumChannels = static_cast<int> (format.NumberChannels());
  921. const int oldNumChannels = juceFilter->getChannelCountOfBus (isInput, busNr);
  922. if (newNumChannels == oldNumChannels)
  923. return true;
  924. if (AudioProcessor::Bus* bus = juceFilter->getBus (isInput, busNr))
  925. {
  926. if (! MusicDeviceBase::ValidFormat (scope, element, format))
  927. return false;
  928. #ifdef JucePlugin_PreferredChannelConfigurations
  929. short configs[][2] = {JucePlugin_PreferredChannelConfigurations};
  930. ignoreUnused (bus);
  931. return AudioUnitHelpers::isLayoutSupported (*juceFilter, isInput, busNr, newNumChannels, configs);
  932. #else
  933. return bus->isNumberOfChannelsSupported (newNumChannels);
  934. #endif
  935. }
  936. return false;
  937. }
  938. // AU requires us to override this for the sole reason that we need to find a default layout tag if the number of channels have changed
  939. OSStatus ChangeStreamFormat (AudioUnitScope scope, AudioUnitElement element, const CAStreamBasicDescription& old, const CAStreamBasicDescription& format) override
  940. {
  941. bool isInput;
  942. int busNr;
  943. OSStatus err = elementToBusIdx (scope, element, isInput, busNr);
  944. if (err != noErr)
  945. return err;
  946. AudioChannelLayoutTag& currentTag = getCurrentLayout (isInput, busNr);
  947. const int newNumChannels = static_cast<int> (format.NumberChannels());
  948. const int oldNumChannels = juceFilter->getChannelCountOfBus (isInput, busNr);
  949. #ifdef JucePlugin_PreferredChannelConfigurations
  950. short configs[][2] = {JucePlugin_PreferredChannelConfigurations};
  951. if (! AudioUnitHelpers::isLayoutSupported (*juceFilter, isInput, busNr, newNumChannels, configs))
  952. return kAudioUnitErr_FormatNotSupported;
  953. #endif
  954. // predict channel layout
  955. AudioChannelSet set = (newNumChannels != oldNumChannels) ? juceFilter->getBus (isInput, busNr)->supportedLayoutWithChannels (newNumChannels)
  956. : juceFilter->getChannelLayoutOfBus (isInput, busNr);
  957. if (set == AudioChannelSet())
  958. return kAudioUnitErr_FormatNotSupported;
  959. err = MusicDeviceBase::ChangeStreamFormat (scope, element, old, format);
  960. if (err == noErr)
  961. currentTag = CoreAudioLayouts::toCoreAudio (set);
  962. return err;
  963. }
  964. //==============================================================================
  965. ComponentResult Render (AudioUnitRenderActionFlags& ioActionFlags,
  966. const AudioTimeStamp& inTimeStamp,
  967. const UInt32 nFrames) override
  968. {
  969. lastTimeStamp = inTimeStamp;
  970. // prepare buffers
  971. {
  972. pullInputAudio (ioActionFlags, inTimeStamp, nFrames);
  973. prepareOutputBuffers (nFrames);
  974. audioBuffer.reset();
  975. }
  976. ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
  977. const int numInputBuses = static_cast<int> (GetScope (kAudioUnitScope_Input) .GetNumberOfElements());
  978. const int numOutputBuses = static_cast<int> (GetScope (kAudioUnitScope_Output).GetNumberOfElements());
  979. // set buffer pointers to minimize copying
  980. {
  981. int chIdx = 0, numChannels = 0;
  982. bool interleaved = false;
  983. AudioBufferList* buffer = nullptr;
  984. // use output pointers
  985. for (int busIdx = 0; busIdx < numOutputBuses; ++busIdx)
  986. {
  987. GetAudioBufferList (false, busIdx, buffer, interleaved, numChannels);
  988. const int* outLayoutMap = mapper.get (false, busIdx);
  989. for (int ch = 0; ch < numChannels; ++ch)
  990. audioBuffer.setBuffer (chIdx++, interleaved ? nullptr : static_cast<float*> (buffer->mBuffers[outLayoutMap[ch]].mData));
  991. }
  992. // use input pointers on remaining channels
  993. for (int busIdx = 0; chIdx < totalInChannels;)
  994. {
  995. int channelIndexInBus = juceFilter->getOffsetInBusBufferForAbsoluteChannelIndex (true, chIdx, busIdx);
  996. const bool badData = ! pulledSucceeded[busIdx];
  997. if (! badData)
  998. GetAudioBufferList (true, busIdx, buffer, interleaved, numChannels);
  999. const int* inLayoutMap = mapper.get (true, busIdx);
  1000. const int n = juceFilter->getChannelCountOfBus (true, busIdx);
  1001. for (int ch = channelIndexInBus; ch < n; ++ch)
  1002. audioBuffer.setBuffer (chIdx++, interleaved || badData ? nullptr : static_cast<float*> (buffer->mBuffers[inLayoutMap[ch]].mData));
  1003. }
  1004. }
  1005. // copy input
  1006. {
  1007. for (int busIdx = 0; busIdx < numInputBuses; ++busIdx)
  1008. {
  1009. if (pulledSucceeded[busIdx])
  1010. {
  1011. audioBuffer.push (GetInput ((UInt32) busIdx)->GetBufferList(), mapper.get (true, busIdx));
  1012. }
  1013. else
  1014. {
  1015. const int n = juceFilter->getChannelCountOfBus (true, busIdx);
  1016. for (int ch = 0; ch < n; ++ch)
  1017. zeromem (audioBuffer.push(), sizeof (float) * nFrames);
  1018. }
  1019. }
  1020. // clear remaining channels
  1021. for (int i = totalInChannels; i < totalOutChannels; ++i)
  1022. zeromem (audioBuffer.push(), sizeof (float) * nFrames);
  1023. }
  1024. // swap midi buffers
  1025. {
  1026. const ScopedLock sl (incomingMidiLock);
  1027. midiEvents.clear();
  1028. incomingEvents.swapWith (midiEvents);
  1029. }
  1030. // process audio
  1031. processBlock (audioBuffer.getBuffer (nFrames), midiEvents);
  1032. // copy back
  1033. {
  1034. for (int busIdx = 0; busIdx < numOutputBuses; ++busIdx)
  1035. audioBuffer.pop (GetOutput ((UInt32) busIdx)->GetBufferList(), mapper.get (false, busIdx));
  1036. }
  1037. // process midi output
  1038. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
  1039. if (! midiEvents.isEmpty() && midiCallback.midiOutputCallback != nullptr)
  1040. pushMidiOutput (nFrames);
  1041. #endif
  1042. midiEvents.clear();
  1043. return noErr;
  1044. }
  1045. //==============================================================================
  1046. ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID*, UInt32, const MusicDeviceNoteParams&) override { return noErr; }
  1047. ComponentResult StopNote (MusicDeviceGroupID, NoteInstanceID, UInt32) override { return noErr; }
  1048. //==============================================================================
  1049. OSStatus HandleMidiEvent (UInt8 nStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2, UInt32 inStartFrame) override
  1050. {
  1051. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  1052. const juce::uint8 data[] = { (juce::uint8) (nStatus | inChannel),
  1053. (juce::uint8) inData1,
  1054. (juce::uint8) inData2 };
  1055. const ScopedLock sl (incomingMidiLock);
  1056. incomingEvents.addEvent (data, 3, (int) inStartFrame);
  1057. return noErr;
  1058. #else
  1059. ignoreUnused (nStatus, inChannel, inData1);
  1060. ignoreUnused (inData2, inStartFrame);
  1061. return kAudioUnitErr_PropertyNotInUse;
  1062. #endif
  1063. }
  1064. OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength) override
  1065. {
  1066. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  1067. const ScopedLock sl (incomingMidiLock);
  1068. incomingEvents.addEvent (inData, (int) inLength, 0);
  1069. return noErr;
  1070. #else
  1071. ignoreUnused (inData, inLength);
  1072. return kAudioUnitErr_PropertyNotInUse;
  1073. #endif
  1074. }
  1075. //==============================================================================
  1076. ComponentResult GetPresets (CFArrayRef* outData) const override
  1077. {
  1078. if (outData != nullptr)
  1079. {
  1080. const int numPrograms = juceFilter->getNumPrograms();
  1081. clearPresetsArray();
  1082. presetsArray.insertMultiple (0, AUPreset(), numPrograms);
  1083. CFMutableArrayRef presetsArrayRef = CFArrayCreateMutable (0, numPrograms, 0);
  1084. for (int i = 0; i < numPrograms; ++i)
  1085. {
  1086. String name (juceFilter->getProgramName(i));
  1087. if (name.isEmpty())
  1088. name = "Untitled";
  1089. AUPreset& p = presetsArray.getReference(i);
  1090. p.presetNumber = i;
  1091. p.presetName = name.toCFString();
  1092. CFArrayAppendValue (presetsArrayRef, &p);
  1093. }
  1094. *outData = (CFArrayRef) presetsArrayRef;
  1095. }
  1096. return noErr;
  1097. }
  1098. OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) override
  1099. {
  1100. const int numPrograms = juceFilter->getNumPrograms();
  1101. const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;
  1102. if (chosenPresetNumber >= numPrograms)
  1103. return kAudioUnitErr_InvalidProperty;
  1104. AUPreset chosenPreset;
  1105. chosenPreset.presetNumber = chosenPresetNumber;
  1106. chosenPreset.presetName = juceFilter->getProgramName (chosenPresetNumber).toCFString();
  1107. juceFilter->setCurrentProgram (chosenPresetNumber);
  1108. SetAFactoryPresetAsCurrent (chosenPreset);
  1109. return noErr;
  1110. }
  1111. void componentMovedOrResized (Component& component, bool /*wasMoved*/, bool /*wasResized*/) override
  1112. {
  1113. NSView* view = (NSView*) component.getWindowHandle();
  1114. NSRect r = [[view superview] frame];
  1115. r.origin.y = r.origin.y + r.size.height - component.getHeight();
  1116. r.size.width = component.getWidth();
  1117. r.size.height = component.getHeight();
  1118. [CATransaction begin];
  1119. [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
  1120. [[view superview] setFrame: r];
  1121. [view setFrame: makeNSRect (component.getLocalBounds())];
  1122. [CATransaction commit];
  1123. [view setNeedsDisplay: YES];
  1124. }
  1125. //==============================================================================
  1126. class EditorCompHolder : public Component
  1127. {
  1128. public:
  1129. EditorCompHolder (AudioProcessorEditor* const editor)
  1130. {
  1131. setSize (editor->getWidth(), editor->getHeight());
  1132. addAndMakeVisible (editor);
  1133. #if ! JucePlugin_EditorRequiresKeyboardFocus
  1134. setWantsKeyboardFocus (false);
  1135. #else
  1136. setWantsKeyboardFocus (true);
  1137. #endif
  1138. ignoreUnused (fakeMouseGenerator);
  1139. }
  1140. ~EditorCompHolder()
  1141. {
  1142. deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
  1143. // have been transferred to another parent which takes over ownership.
  1144. }
  1145. static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
  1146. {
  1147. EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
  1148. NSRect r = makeNSRect (editorCompHolder->getLocalBounds());
  1149. static JuceUIViewClass cls;
  1150. NSView* view = [[cls.createInstance() initWithFrame: r] autorelease];
  1151. JuceUIViewClass::setFilter (view, filter);
  1152. JuceUIViewClass::setAU (view, au);
  1153. JuceUIViewClass::setEditor (view, editorCompHolder);
  1154. [view setHidden: NO];
  1155. [view setPostsFrameChangedNotifications: YES];
  1156. [[NSNotificationCenter defaultCenter] addObserver: view
  1157. selector: @selector (applicationWillTerminate:)
  1158. name: NSApplicationWillTerminateNotification
  1159. object: nil];
  1160. activeUIs.add (view);
  1161. editorCompHolder->addToDesktop (0, (void*) view);
  1162. editorCompHolder->setVisible (view);
  1163. return view;
  1164. }
  1165. void childBoundsChanged (Component*) override
  1166. {
  1167. if (Component* editor = getChildComponent(0))
  1168. {
  1169. const int w = jmax (32, editor->getWidth());
  1170. const int h = jmax (32, editor->getHeight());
  1171. if (getWidth() != w || getHeight() != h)
  1172. setSize (w, h);
  1173. NSView* view = (NSView*) getWindowHandle();
  1174. NSRect r = [[view superview] frame];
  1175. r.size.width = editor->getWidth();
  1176. r.size.height = editor->getHeight();
  1177. [CATransaction begin];
  1178. [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
  1179. [[view superview] setFrame: r];
  1180. [view setFrame: makeNSRect (editor->getLocalBounds())];
  1181. [CATransaction commit];
  1182. [view setNeedsDisplay: YES];
  1183. }
  1184. }
  1185. bool keyPressed (const KeyPress&) override
  1186. {
  1187. if (getHostType().isAbletonLive())
  1188. {
  1189. static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
  1190. NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
  1191. if (lastEventTime != eventTime)
  1192. {
  1193. lastEventTime = eventTime;
  1194. NSView* view = (NSView*) getWindowHandle();
  1195. NSView* hostView = [view superview];
  1196. NSWindow* hostWindow = [hostView window];
  1197. [hostWindow makeFirstResponder: hostView];
  1198. [hostView keyDown: [NSApp currentEvent]];
  1199. [hostWindow makeFirstResponder: view];
  1200. }
  1201. }
  1202. return false;
  1203. }
  1204. private:
  1205. FakeMouseMoveGenerator fakeMouseGenerator;
  1206. JUCE_DECLARE_NON_COPYABLE (EditorCompHolder)
  1207. };
  1208. void deleteActiveEditors()
  1209. {
  1210. for (int i = activeUIs.size(); --i >= 0;)
  1211. {
  1212. id ui = (id) activeUIs.getUnchecked(i);
  1213. if (JuceUIViewClass::getAU (ui) == this)
  1214. JuceUIViewClass::deleteEditor (ui);
  1215. }
  1216. }
  1217. //==============================================================================
  1218. struct JuceUIViewClass : public ObjCClass<NSView>
  1219. {
  1220. JuceUIViewClass() : ObjCClass<NSView> ("JUCEAUView_")
  1221. {
  1222. addIvar<AudioProcessor*> ("filter");
  1223. addIvar<JuceAU*> ("au");
  1224. addIvar<EditorCompHolder*> ("editor");
  1225. addMethod (@selector (dealloc), dealloc, "v@:");
  1226. addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
  1227. addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:");
  1228. addMethod (@selector (mouseDownCanMoveWindow), mouseDownCanMoveWindow, "c@:");
  1229. registerClass();
  1230. }
  1231. static void deleteEditor (id self)
  1232. {
  1233. ScopedPointer<EditorCompHolder> editorComp (getEditor (self));
  1234. if (editorComp != nullptr)
  1235. {
  1236. if (editorComp->getChildComponent(0) != nullptr
  1237. && activePlugins.contains (getAU (self))) // plugin may have been deleted before the UI
  1238. {
  1239. AudioProcessor* const filter = getIvar<AudioProcessor*> (self, "filter");
  1240. filter->editorBeingDeleted ((AudioProcessorEditor*) editorComp->getChildComponent(0));
  1241. }
  1242. editorComp = nullptr;
  1243. setEditor (self, nullptr);
  1244. }
  1245. }
  1246. static JuceAU* getAU (id self) { return getIvar<JuceAU*> (self, "au"); }
  1247. static EditorCompHolder* getEditor (id self) { return getIvar<EditorCompHolder*> (self, "editor"); }
  1248. static void setFilter (id self, AudioProcessor* filter) { object_setInstanceVariable (self, "filter", filter); }
  1249. static void setAU (id self, JuceAU* au) { object_setInstanceVariable (self, "au", au); }
  1250. static void setEditor (id self, EditorCompHolder* e) { object_setInstanceVariable (self, "editor", e); }
  1251. private:
  1252. static void dealloc (id self, SEL)
  1253. {
  1254. if (activeUIs.contains (self))
  1255. shutdown (self);
  1256. sendSuperclassMessage (self, @selector (dealloc));
  1257. }
  1258. static void applicationWillTerminate (id self, SEL, NSNotification*)
  1259. {
  1260. shutdown (self);
  1261. }
  1262. static void shutdown (id self)
  1263. {
  1264. [[NSNotificationCenter defaultCenter] removeObserver: self];
  1265. deleteEditor (self);
  1266. jassert (activeUIs.contains (self));
  1267. activeUIs.removeFirstMatchingValue (self);
  1268. if (activePlugins.size() + activeUIs.size() == 0)
  1269. {
  1270. // there's some kind of component currently modal, but the host
  1271. // is trying to delete our plugin..
  1272. jassert (Component::getCurrentlyModalComponent() == nullptr);
  1273. shutdownJuce_GUI();
  1274. }
  1275. }
  1276. static void viewDidMoveToWindow (id self, SEL)
  1277. {
  1278. if (NSWindow* w = [(NSView*) self window])
  1279. {
  1280. [w setAcceptsMouseMovedEvents: YES];
  1281. if (EditorCompHolder* const editorComp = getEditor (self))
  1282. [w makeFirstResponder: (NSView*) editorComp->getWindowHandle()];
  1283. }
  1284. }
  1285. static BOOL mouseDownCanMoveWindow (id, SEL)
  1286. {
  1287. return NO;
  1288. }
  1289. };
  1290. //==============================================================================
  1291. struct JuceUICreationClass : public ObjCClass<NSObject>
  1292. {
  1293. JuceUICreationClass() : ObjCClass<NSObject> ("JUCE_AUCocoaViewClass_")
  1294. {
  1295. addMethod (@selector (interfaceVersion), interfaceVersion, @encode (unsigned int), "@:");
  1296. addMethod (@selector (description), description, @encode (NSString*), "@:");
  1297. addMethod (@selector (uiViewForAudioUnit:withSize:), uiViewForAudioUnit, @encode (NSView*), "@:", @encode (AudioUnit), @encode (NSSize));
  1298. addProtocol (@protocol (AUCocoaUIBase));
  1299. registerClass();
  1300. }
  1301. private:
  1302. static unsigned int interfaceVersion (id, SEL) { return 0; }
  1303. static NSString* description (id, SEL)
  1304. {
  1305. return [NSString stringWithString: nsStringLiteral (JucePlugin_Name)];
  1306. }
  1307. static NSView* uiViewForAudioUnit (id, SEL, AudioUnit inAudioUnit, NSSize)
  1308. {
  1309. void* pointers[2];
  1310. UInt32 propertySize = sizeof (pointers);
  1311. if (AudioUnitGetProperty (inAudioUnit, juceFilterObjectPropertyID,
  1312. kAudioUnitScope_Global, 0, pointers, &propertySize) == noErr)
  1313. {
  1314. if (AudioProcessor* filter = static_cast<AudioProcessor*> (pointers[0]))
  1315. if (AudioProcessorEditor* editorComp = filter->createEditorIfNeeded())
  1316. return EditorCompHolder::createViewFor (filter, static_cast<JuceAU*> (pointers[1]), editorComp);
  1317. }
  1318. return nil;
  1319. }
  1320. };
  1321. private:
  1322. //==============================================================================
  1323. AudioUnitHelpers::CoreAudioBufferList audioBuffer;
  1324. MidiBuffer midiEvents, incomingEvents;
  1325. bool prepared, isBypassed;
  1326. //==============================================================================
  1327. #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
  1328. static constexpr bool forceUseLegacyParamIDs = true;
  1329. #else
  1330. static constexpr bool forceUseLegacyParamIDs = false;
  1331. #endif
  1332. //==============================================================================
  1333. LegacyAudioParametersWrapper juceParameters;
  1334. HashMap<int32, AudioProcessorParameter*> paramMap;
  1335. Array<AudioUnitParameterID> auParamIDs;
  1336. //==============================================================================
  1337. AudioUnitEvent auEvent;
  1338. mutable Array<AUPreset> presetsArray;
  1339. CriticalSection incomingMidiLock;
  1340. AUMIDIOutputCallbackStruct midiCallback;
  1341. AudioTimeStamp lastTimeStamp;
  1342. int totalInChannels, totalOutChannels;
  1343. HeapBlock<bool> pulledSucceeded;
  1344. ThreadLocalValue<bool> inParameterChangedCallback;
  1345. //==============================================================================
  1346. Array<AUChannelInfo> channelInfo;
  1347. Array<Array<AudioChannelLayoutTag>> supportedInputLayouts, supportedOutputLayouts;
  1348. Array<AudioChannelLayoutTag> currentInputLayout, currentOutputLayout;
  1349. //==============================================================================
  1350. AudioUnitHelpers::ChannelRemapper mapper;
  1351. //==============================================================================
  1352. OwnedArray<OwnedArray<const __CFString>> parameterValueStringArrays;
  1353. //==============================================================================
  1354. AudioProcessorParameter* bypassParam = nullptr;
  1355. //==============================================================================
  1356. void pullInputAudio (AudioUnitRenderActionFlags& flags, const AudioTimeStamp& timestamp, const UInt32 nFrames) noexcept
  1357. {
  1358. const unsigned int numInputBuses = GetScope (kAudioUnitScope_Input).GetNumberOfElements();
  1359. for (unsigned int i = 0; i < numInputBuses; ++i)
  1360. {
  1361. if (AUInputElement* input = GetInput (i))
  1362. {
  1363. const bool succeeded = (input->PullInput (flags, timestamp, i, nFrames) == noErr);
  1364. if ((flags & kAudioUnitRenderAction_OutputIsSilence) != 0 && succeeded)
  1365. AudioUnitHelpers::clearAudioBuffer (input->GetBufferList());
  1366. pulledSucceeded[i] = succeeded;
  1367. }
  1368. }
  1369. }
  1370. void prepareOutputBuffers (const UInt32 nFrames) noexcept
  1371. {
  1372. const unsigned int numOutputBuses = GetScope (kAudioUnitScope_Output).GetNumberOfElements();
  1373. for (unsigned int busIdx = 0; busIdx < numOutputBuses; ++busIdx)
  1374. {
  1375. AUOutputElement* output = GetOutput (busIdx);
  1376. if (output->WillAllocateBuffer())
  1377. output->PrepareBuffer (nFrames);
  1378. }
  1379. }
  1380. void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiBuffer) noexcept
  1381. {
  1382. const ScopedLock sl (juceFilter->getCallbackLock());
  1383. if (juceFilter->isSuspended())
  1384. {
  1385. buffer.clear();
  1386. }
  1387. else if (bypassParam == nullptr && isBypassed)
  1388. {
  1389. juceFilter->processBlockBypassed (buffer, midiBuffer);
  1390. }
  1391. else
  1392. {
  1393. juceFilter->processBlock (buffer, midiBuffer);
  1394. }
  1395. }
  1396. void pushMidiOutput (UInt32 nFrames) noexcept
  1397. {
  1398. UInt32 numPackets = 0;
  1399. size_t dataSize = 0;
  1400. const juce::uint8* midiEventData;
  1401. int midiEventSize, midiEventPosition;
  1402. for (MidiBuffer::Iterator i (midiEvents); i.getNextEvent (midiEventData, midiEventSize, midiEventPosition);)
  1403. {
  1404. jassert (isPositiveAndBelow (midiEventPosition, nFrames));
  1405. ignoreUnused (nFrames);
  1406. dataSize += (size_t) midiEventSize;
  1407. ++numPackets;
  1408. }
  1409. MIDIPacket* p;
  1410. const size_t packetMembersSize = sizeof (MIDIPacket) - sizeof (p->data); // NB: GCC chokes on "sizeof (MidiMessage::data)"
  1411. const size_t packetListMembersSize = sizeof (MIDIPacketList) - sizeof (p->data);
  1412. HeapBlock<MIDIPacketList> packetList;
  1413. packetList.malloc (packetListMembersSize + packetMembersSize * numPackets + dataSize, 1);
  1414. packetList->numPackets = numPackets;
  1415. p = packetList->packet;
  1416. for (MidiBuffer::Iterator i (midiEvents); i.getNextEvent (midiEventData, midiEventSize, midiEventPosition);)
  1417. {
  1418. p->timeStamp = (MIDITimeStamp) midiEventPosition;
  1419. p->length = (UInt16) midiEventSize;
  1420. memcpy (p->data, midiEventData, (size_t) midiEventSize);
  1421. p = MIDIPacketNext (p);
  1422. }
  1423. midiCallback.midiOutputCallback (midiCallback.userData, &lastTimeStamp, 0, packetList);
  1424. }
  1425. void GetAudioBufferList (bool isInput, int busIdx, AudioBufferList*& bufferList, bool& interleaved, int& numChannels)
  1426. {
  1427. AUIOElement* element = GetElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, static_cast<UInt32> (busIdx))->AsIOElement();
  1428. jassert (element != nullptr);
  1429. bufferList = &element->GetBufferList();
  1430. jassert (bufferList->mNumberBuffers > 0);
  1431. interleaved = AudioUnitHelpers::isAudioBufferInterleaved (*bufferList);
  1432. numChannels = static_cast<int> (interleaved ? bufferList->mBuffers[0].mNumberChannels : bufferList->mNumberBuffers);
  1433. }
  1434. //==============================================================================
  1435. static OSStatus scopeToDirection (AudioUnitScope scope, bool& isInput) noexcept
  1436. {
  1437. isInput = (scope == kAudioUnitScope_Input);
  1438. return (scope != kAudioUnitScope_Input
  1439. && scope != kAudioUnitScope_Output)
  1440. ? kAudioUnitErr_InvalidScope : noErr;
  1441. }
  1442. OSStatus elementToBusIdx (AudioUnitScope scope, AudioUnitElement element, bool& isInput, int& busIdx) noexcept
  1443. {
  1444. OSStatus err;
  1445. busIdx = static_cast<int> (element);
  1446. if ((err = scopeToDirection (scope, isInput)) != noErr) return err;
  1447. if (isPositiveAndBelow (busIdx, AudioUnitHelpers::getBusCount (juceFilter.get(), isInput))) return noErr;
  1448. return kAudioUnitErr_InvalidElement;
  1449. }
  1450. //==============================================================================
  1451. void addParameters()
  1452. {
  1453. juceParameters.update (*juceFilter, forceUseLegacyParamIDs);
  1454. // check if all parameters are managed?
  1455. const int numParams = juceParameters.getNumParameters();
  1456. for (auto* param : juceParameters.params)
  1457. {
  1458. const AudioUnitParameterID auParamID = generateAUParameterID (param);
  1459. // Consider yourself very unlucky if you hit this assertion. The hash code of your
  1460. // parameter ids are not unique.
  1461. jassert (! paramMap.contains (static_cast<int32> (auParamID)));
  1462. auParamIDs.add (auParamID);
  1463. paramMap.set (static_cast<int32> (auParamID), param);
  1464. if (juceParameters.isUsingManagedParameters())
  1465. Globals()->SetParameter (auParamID, param->getValue());
  1466. }
  1467. if (! juceParameters.isUsingManagedParameters())
  1468. Globals()->UseIndexedParameters (numParams);
  1469. #if JUCE_DEBUG
  1470. // Some hosts can't handle the huge numbers of discrete parameter values created when
  1471. // using the default number of steps.
  1472. for (auto* param : juceParameters.params)
  1473. if (param->isDiscrete())
  1474. jassert (param->getNumSteps() != AudioProcessor::getDefaultNumParameterSteps());
  1475. #endif
  1476. parameterValueStringArrays.ensureStorageAllocated (numParams);
  1477. for (auto* param : juceParameters.params)
  1478. {
  1479. OwnedArray<const __CFString>* stringValues = nullptr;
  1480. auto initialValue = param->getValue();
  1481. if (param->isDiscrete() && (! forceUseLegacyParamIDs))
  1482. {
  1483. const auto numSteps = param->getNumSteps();
  1484. stringValues = new OwnedArray<const __CFString>();
  1485. stringValues->ensureStorageAllocated (numSteps);
  1486. const auto maxValue = getMaximumParameterValue (param);
  1487. for (int i = 0; i < numSteps; ++i)
  1488. {
  1489. auto value = (float) i / maxValue;
  1490. // Once legacy parameters are deprecated this can be replaced by getText
  1491. param->setValue (value);
  1492. stringValues->add (CFStringCreateCopy (nullptr, (param->getCurrentValueAsText().toCFString())));
  1493. }
  1494. }
  1495. param->setValue (initialValue);
  1496. parameterValueStringArrays.add (stringValues);
  1497. }
  1498. if ((bypassParam = juceFilter->getBypassParameter()) != nullptr)
  1499. bypassParam->addListener (this);
  1500. }
  1501. //==============================================================================
  1502. AudioUnitParameterID generateAUParameterID (AudioProcessorParameter* param) const
  1503. {
  1504. const String& juceParamID = LegacyAudioParameter::getParamID (param, forceUseLegacyParamIDs);
  1505. AudioUnitParameterID paramHash = static_cast<AudioUnitParameterID> (juceParamID.hashCode());
  1506. #if JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS
  1507. // studio one doesn't like negative parameters
  1508. paramHash &= ~(1 << (sizeof (AudioUnitParameterID) * 8 - 1));
  1509. #endif
  1510. return juceParameters.isUsingManagedParameters() ? paramHash
  1511. : static_cast<AudioUnitParameterID> (juceParamID.getIntValue());
  1512. }
  1513. inline AudioUnitParameterID getAUParameterIDForIndex (int paramIndex) const noexcept
  1514. {
  1515. return juceParameters.isUsingManagedParameters() ? auParamIDs.getReference (paramIndex)
  1516. : static_cast<AudioUnitParameterID> (paramIndex);
  1517. }
  1518. AudioProcessorParameter* getParameterForAUParameterID (AudioUnitParameterID address) const noexcept
  1519. {
  1520. return paramMap[static_cast<int32> (address)];
  1521. }
  1522. //==============================================================================
  1523. OSStatus syncAudioUnitWithProcessor()
  1524. {
  1525. OSStatus err = noErr;
  1526. const int enabledInputs = AudioUnitHelpers::getBusCount (juceFilter.get(), true);
  1527. const int enabledOutputs = AudioUnitHelpers::getBusCount (juceFilter.get(), false);
  1528. if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Input, static_cast<UInt32> (enabledInputs))) != noErr)
  1529. return err;
  1530. if ((err = MusicDeviceBase::SetBusCount (kAudioUnitScope_Output, static_cast<UInt32> (enabledOutputs))) != noErr)
  1531. return err;
  1532. addSupportedLayoutTags();
  1533. for (int i = 0; i < enabledInputs; ++i)
  1534. if ((err = syncAudioUnitWithChannelSet (true, i, juceFilter->getChannelLayoutOfBus (true, i))) != noErr) return err;
  1535. for (int i = 0; i < enabledOutputs; ++i)
  1536. if ((err = syncAudioUnitWithChannelSet (false, i, juceFilter->getChannelLayoutOfBus (false, i))) != noErr) return err;
  1537. return noErr;
  1538. }
  1539. OSStatus syncProcessorWithAudioUnit()
  1540. {
  1541. const int numInputBuses = AudioUnitHelpers::getBusCount (juceFilter.get(), true);
  1542. const int numOutputBuses = AudioUnitHelpers::getBusCount (juceFilter.get(), false);
  1543. const int numInputElements = static_cast<int> (GetScope(kAudioUnitScope_Input). GetNumberOfElements());
  1544. const int numOutputElements = static_cast<int> (GetScope(kAudioUnitScope_Output).GetNumberOfElements());
  1545. AudioProcessor::BusesLayout requestedLayouts;
  1546. for (int dir = 0; dir < 2; ++dir)
  1547. {
  1548. const bool isInput = (dir == 0);
  1549. const int n = (isInput ? numInputBuses : numOutputBuses);
  1550. const int numAUElements = (isInput ? numInputElements : numOutputElements);
  1551. Array<AudioChannelSet>& requestedBuses = (isInput ? requestedLayouts.inputBuses : requestedLayouts.outputBuses);
  1552. for (int busIdx = 0; busIdx < n; ++busIdx)
  1553. {
  1554. const AUIOElement* element = (busIdx < numAUElements ? GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, (UInt32) busIdx) : nullptr);
  1555. const int numChannels = (element != nullptr ? static_cast<int> (element->GetStreamFormat().NumberChannels()) : 0);
  1556. AudioChannelLayoutTag currentLayoutTag = isInput ? currentInputLayout[busIdx] : currentOutputLayout[busIdx];
  1557. const int tagNumChannels = currentLayoutTag & 0xffff;
  1558. if (numChannels != tagNumChannels)
  1559. return kAudioUnitErr_FormatNotSupported;
  1560. requestedBuses.add (CoreAudioLayouts::fromCoreAudio (currentLayoutTag));
  1561. }
  1562. }
  1563. #ifdef JucePlugin_PreferredChannelConfigurations
  1564. short configs[][2] = {JucePlugin_PreferredChannelConfigurations};
  1565. if (! AudioProcessor::containsLayout (requestedLayouts, configs))
  1566. return kAudioUnitErr_FormatNotSupported;
  1567. #endif
  1568. if (! AudioUnitHelpers::setBusesLayout (juceFilter.get(), requestedLayouts))
  1569. return kAudioUnitErr_FormatNotSupported;
  1570. // update total channel count
  1571. totalInChannels = juceFilter->getTotalNumInputChannels();
  1572. totalOutChannels = juceFilter->getTotalNumOutputChannels();
  1573. return noErr;
  1574. }
  1575. OSStatus syncAudioUnitWithChannelSet (bool isInput, int busNr, const AudioChannelSet& channelSet)
  1576. {
  1577. const int numChannels = channelSet.size();
  1578. getCurrentLayout (isInput, busNr) = CoreAudioLayouts::toCoreAudio (channelSet);
  1579. // is this bus activated?
  1580. if (numChannels == 0)
  1581. return noErr;
  1582. if (AUIOElement* element = GetIOElement (isInput ? kAudioUnitScope_Input : kAudioUnitScope_Output, (UInt32) busNr))
  1583. {
  1584. element->SetName ((CFStringRef) juceStringToNS (juceFilter->getBus (isInput, busNr)->getName()));
  1585. CAStreamBasicDescription streamDescription;
  1586. streamDescription.mSampleRate = getSampleRate();
  1587. streamDescription.SetCanonical ((UInt32) numChannels, false);
  1588. return element->SetStreamFormat (streamDescription);
  1589. }
  1590. else
  1591. jassertfalse;
  1592. return kAudioUnitErr_InvalidElement;
  1593. }
  1594. //==============================================================================
  1595. void clearPresetsArray() const
  1596. {
  1597. for (int i = presetsArray.size(); --i >= 0;)
  1598. CFRelease (presetsArray.getReference(i).presetName);
  1599. presetsArray.clear();
  1600. }
  1601. void refreshCurrentPreset()
  1602. {
  1603. // this will make the AU host re-read and update the current preset name
  1604. // in case it was changed here in the plug-in:
  1605. const int currentProgramNumber = juceFilter->getCurrentProgram();
  1606. const String currentProgramName = juceFilter->getProgramName (currentProgramNumber);
  1607. AUPreset currentPreset;
  1608. currentPreset.presetNumber = currentProgramNumber;
  1609. currentPreset.presetName = currentProgramName.toCFString();
  1610. SetAFactoryPresetAsCurrent (currentPreset);
  1611. }
  1612. //==============================================================================
  1613. Array<AudioChannelLayoutTag>& getSupportedBusLayouts (bool isInput, int bus) noexcept { return (isInput ? supportedInputLayouts : supportedOutputLayouts).getReference (bus); }
  1614. const Array<AudioChannelLayoutTag>& getSupportedBusLayouts (bool isInput, int bus) const noexcept { return (isInput ? supportedInputLayouts : supportedOutputLayouts).getReference (bus); }
  1615. AudioChannelLayoutTag& getCurrentLayout (bool isInput, int bus) noexcept { return (isInput ? currentInputLayout : currentOutputLayout).getReference (bus); }
  1616. AudioChannelLayoutTag getCurrentLayout (bool isInput, int bus) const noexcept { return (isInput ? currentInputLayout : currentOutputLayout)[bus]; }
  1617. //==============================================================================
  1618. void addSupportedLayoutTagsForBus (bool isInput, int busNum, Array<AudioChannelLayoutTag>& tags)
  1619. {
  1620. if (AudioProcessor::Bus* bus = juceFilter->getBus (isInput, busNum))
  1621. {
  1622. #ifndef JucePlugin_PreferredChannelConfigurations
  1623. auto& knownTags = CoreAudioLayouts::getKnownCoreAudioTags();
  1624. for (auto tag : knownTags)
  1625. if (bus->isLayoutSupported (CoreAudioLayouts::fromCoreAudio (tag)))
  1626. tags.addIfNotAlreadyThere (tag);
  1627. #endif
  1628. // add discrete layout tags
  1629. int n = bus->getMaxSupportedChannels(maxChannelsToProbeFor());
  1630. for (int ch = 0; ch < n; ++ch)
  1631. {
  1632. #ifdef JucePlugin_PreferredChannelConfigurations
  1633. const short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
  1634. if (AudioUnitHelpers::isLayoutSupported (*juceFilter, isInput, busNum, ch, configs))
  1635. tags.addIfNotAlreadyThere (static_cast<AudioChannelLayoutTag> ((int) kAudioChannelLayoutTag_DiscreteInOrder | ch));
  1636. #else
  1637. if (bus->isLayoutSupported (AudioChannelSet::discreteChannels (ch)))
  1638. tags.addIfNotAlreadyThere (static_cast<AudioChannelLayoutTag> ((int) kAudioChannelLayoutTag_DiscreteInOrder | ch));
  1639. #endif
  1640. }
  1641. }
  1642. }
  1643. void addSupportedLayoutTagsForDirection (bool isInput)
  1644. {
  1645. auto& layouts = isInput ? supportedInputLayouts : supportedOutputLayouts;
  1646. layouts.clear();
  1647. auto numBuses = AudioUnitHelpers::getBusCount (juceFilter.get(), isInput);
  1648. for (int busNr = 0; busNr < numBuses; ++busNr)
  1649. {
  1650. Array<AudioChannelLayoutTag> busLayouts;
  1651. addSupportedLayoutTagsForBus (isInput, busNr, busLayouts);
  1652. layouts.add (busLayouts);
  1653. }
  1654. }
  1655. void addSupportedLayoutTags()
  1656. {
  1657. currentInputLayout.clear(); currentOutputLayout.clear();
  1658. currentInputLayout. resize (AudioUnitHelpers::getBusCount (juceFilter.get(), true));
  1659. currentOutputLayout.resize (AudioUnitHelpers::getBusCount (juceFilter.get(), false));
  1660. addSupportedLayoutTagsForDirection (true);
  1661. addSupportedLayoutTagsForDirection (false);
  1662. }
  1663. static int maxChannelsToProbeFor()
  1664. {
  1665. return (getHostType().isLogic() ? 8 : 64);
  1666. }
  1667. //==============================================================================
  1668. void auPropertyListener (AudioUnitPropertyID propId, AudioUnitScope scope, AudioUnitElement)
  1669. {
  1670. if (scope == kAudioUnitScope_Global && propId == kAudioUnitProperty_ContextName
  1671. && juceFilter != nullptr && mContextName != nullptr)
  1672. {
  1673. AudioProcessor::TrackProperties props;
  1674. props.name = String::fromCFString (mContextName);
  1675. juceFilter->updateTrackProperties (props);
  1676. }
  1677. }
  1678. static void auPropertyListenerDispatcher (void* inRefCon, AudioUnit, AudioUnitPropertyID propId,
  1679. AudioUnitScope scope, AudioUnitElement element)
  1680. {
  1681. static_cast<JuceAU*> (inRefCon)->auPropertyListener (propId, scope, element);
  1682. }
  1683. JUCE_DECLARE_NON_COPYABLE (JuceAU)
  1684. };
  1685. //==============================================================================
  1686. #if BUILD_AU_CARBON_UI
  1687. class JuceAUView : public AUCarbonViewBase
  1688. {
  1689. public:
  1690. JuceAUView (AudioUnitCarbonView auview)
  1691. : AUCarbonViewBase (auview),
  1692. juceFilter (nullptr)
  1693. {
  1694. }
  1695. ~JuceAUView()
  1696. {
  1697. deleteUI();
  1698. }
  1699. ComponentResult CreateUI (Float32 /*inXOffset*/, Float32 /*inYOffset*/) override
  1700. {
  1701. JUCE_AUTORELEASEPOOL
  1702. {
  1703. if (juceFilter == nullptr)
  1704. {
  1705. void* pointers[2];
  1706. UInt32 propertySize = sizeof (pointers);
  1707. AudioUnitGetProperty (GetEditAudioUnit(),
  1708. juceFilterObjectPropertyID,
  1709. kAudioUnitScope_Global,
  1710. 0,
  1711. pointers,
  1712. &propertySize);
  1713. juceFilter = (AudioProcessor*) pointers[0];
  1714. }
  1715. if (juceFilter != nullptr)
  1716. {
  1717. deleteUI();
  1718. if (AudioProcessorEditor* editorComp = juceFilter->createEditorIfNeeded())
  1719. {
  1720. editorComp->setOpaque (true);
  1721. windowComp = new ComponentInHIView (editorComp, mCarbonPane);
  1722. }
  1723. }
  1724. else
  1725. {
  1726. jassertfalse; // can't get a pointer to our effect
  1727. }
  1728. }
  1729. return noErr;
  1730. }
  1731. AudioUnitCarbonViewEventListener getEventListener() const { return mEventListener; }
  1732. void* getEventListenerUserData() const { return mEventListenerUserData; }
  1733. private:
  1734. //==============================================================================
  1735. AudioProcessor* juceFilter;
  1736. ScopedPointer<Component> windowComp;
  1737. FakeMouseMoveGenerator fakeMouseGenerator;
  1738. void deleteUI()
  1739. {
  1740. if (windowComp != nullptr)
  1741. {
  1742. PopupMenu::dismissAllActiveMenus();
  1743. /* This assertion is triggered when there's some kind of modal component active, and the
  1744. host is trying to delete our plugin.
  1745. If you must use modal components, always use them in a non-blocking way, by never
  1746. calling runModalLoop(), but instead using enterModalState() with a callback that
  1747. will be performed on completion. (Note that this assertion could actually trigger
  1748. a false alarm even if you're doing it correctly, but is here to catch people who
  1749. aren't so careful) */
  1750. jassert (Component::getCurrentlyModalComponent() == nullptr);
  1751. if (JuceAU::EditorCompHolder* editorCompHolder = dynamic_cast<JuceAU::EditorCompHolder*> (windowComp->getChildComponent(0)))
  1752. if (AudioProcessorEditor* audioProcessEditor = dynamic_cast<AudioProcessorEditor*> (editorCompHolder->getChildComponent(0)))
  1753. juceFilter->editorBeingDeleted (audioProcessEditor);
  1754. windowComp = nullptr;
  1755. }
  1756. }
  1757. //==============================================================================
  1758. // Uses a child NSWindow to sit in front of a HIView and display our component
  1759. class ComponentInHIView : public Component
  1760. {
  1761. public:
  1762. ComponentInHIView (AudioProcessorEditor* ed, HIViewRef parentHIView)
  1763. : parentView (parentHIView),
  1764. editor (ed),
  1765. recursive (false)
  1766. {
  1767. JUCE_AUTORELEASEPOOL
  1768. {
  1769. jassert (ed != nullptr);
  1770. addAndMakeVisible (editor);
  1771. setOpaque (true);
  1772. setVisible (true);
  1773. setBroughtToFrontOnMouseClick (true);
  1774. setSize (editor.getWidth(), editor.getHeight());
  1775. SizeControl (parentHIView, (SInt16) editor.getWidth(), (SInt16) editor.getHeight());
  1776. WindowRef windowRef = HIViewGetWindow (parentHIView);
  1777. hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];
  1778. // not really sure why this is needed in older OS X versions
  1779. // but JUCE plug-ins crash without it
  1780. if ((SystemStats::getOperatingSystemType() & 0xff) < 12)
  1781. [hostWindow retain];
  1782. [hostWindow setCanHide: YES];
  1783. [hostWindow setReleasedWhenClosed: YES];
  1784. updateWindowPos();
  1785. #if ! JucePlugin_EditorRequiresKeyboardFocus
  1786. addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  1787. setWantsKeyboardFocus (false);
  1788. #else
  1789. addToDesktop (ComponentPeer::windowIsTemporary);
  1790. setWantsKeyboardFocus (true);
  1791. #endif
  1792. setVisible (true);
  1793. toFront (false);
  1794. addSubWindow();
  1795. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1796. [pluginWindow setNextResponder: hostWindow];
  1797. attachWindowHidingHooks (this, (WindowRef) windowRef, hostWindow);
  1798. }
  1799. }
  1800. ~ComponentInHIView()
  1801. {
  1802. JUCE_AUTORELEASEPOOL
  1803. {
  1804. removeWindowHidingHooks (this);
  1805. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1806. [hostWindow removeChildWindow: pluginWindow];
  1807. removeFromDesktop();
  1808. [hostWindow release];
  1809. hostWindow = nil;
  1810. }
  1811. }
  1812. void updateWindowPos()
  1813. {
  1814. HIPoint f;
  1815. f.x = f.y = 0;
  1816. HIPointConvert (&f, kHICoordSpaceView, parentView, kHICoordSpaceScreenPixel, 0);
  1817. setTopLeftPosition ((int) f.x, (int) f.y);
  1818. }
  1819. void addSubWindow()
  1820. {
  1821. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1822. [pluginWindow setExcludedFromWindowsMenu: YES];
  1823. [pluginWindow setCanHide: YES];
  1824. [hostWindow addChildWindow: pluginWindow
  1825. ordered: NSWindowAbove];
  1826. [hostWindow orderFront: nil];
  1827. [pluginWindow orderFront: nil];
  1828. }
  1829. void resized() override
  1830. {
  1831. if (Component* const child = getChildComponent (0))
  1832. child->setBounds (getLocalBounds());
  1833. }
  1834. void paint (Graphics&) override {}
  1835. void childBoundsChanged (Component*) override
  1836. {
  1837. if (! recursive)
  1838. {
  1839. recursive = true;
  1840. const int w = jmax (32, editor.getWidth());
  1841. const int h = jmax (32, editor.getHeight());
  1842. SizeControl (parentView, (SInt16) w, (SInt16) h);
  1843. if (getWidth() != w || getHeight() != h)
  1844. setSize (w, h);
  1845. editor.repaint();
  1846. updateWindowPos();
  1847. addSubWindow(); // (need this for AULab)
  1848. recursive = false;
  1849. }
  1850. }
  1851. bool keyPressed (const KeyPress& kp) override
  1852. {
  1853. if (! kp.getModifiers().isCommandDown())
  1854. {
  1855. // If we have an unused keypress, move the key-focus to a host window
  1856. // and re-inject the event..
  1857. static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
  1858. NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
  1859. if (lastEventTime != eventTime)
  1860. {
  1861. lastEventTime = eventTime;
  1862. [[hostWindow parentWindow] makeKeyWindow];
  1863. repostCurrentNSEvent();
  1864. }
  1865. }
  1866. return false;
  1867. }
  1868. private:
  1869. HIViewRef parentView;
  1870. NSWindow* hostWindow;
  1871. JuceAU::EditorCompHolder editor;
  1872. bool recursive;
  1873. };
  1874. };
  1875. #endif
  1876. //==============================================================================
  1877. #define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \
  1878. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \
  1879. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \
  1880. { \
  1881. PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_AudioUnit; \
  1882. return ComponentEntryPoint<Class>::Dispatch (params, obj); \
  1883. }
  1884. #if JucePlugin_ProducesMidiOutput || JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  1885. #define FACTORY_BASE_CLASS AUMIDIEffectFactory
  1886. #else
  1887. #define FACTORY_BASE_CLASS AUBaseFactory
  1888. #endif
  1889. #define JUCE_FACTORY_ENTRYX(Class, Name) \
  1890. extern "C" __attribute__((visibility("default"))) void* Name ## Factory (const AudioComponentDescription* desc); \
  1891. extern "C" __attribute__((visibility("default"))) void* Name ## Factory (const AudioComponentDescription* desc) \
  1892. { \
  1893. PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_AudioUnit; \
  1894. return FACTORY_BASE_CLASS<Class>::Factory (desc); \
  1895. }
  1896. #define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)
  1897. #define JUCE_FACTORY_ENTRY(Class, Name) JUCE_FACTORY_ENTRYX(Class, Name)
  1898. //==============================================================================
  1899. JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)
  1900. #ifndef AUDIOCOMPONENT_ENTRY
  1901. #define JUCE_DISABLE_AU_FACTORY_ENTRY 1
  1902. #endif
  1903. #if ! JUCE_DISABLE_AU_FACTORY_ENTRY // (You might need to disable this for old Xcode 3 builds)
  1904. JUCE_FACTORY_ENTRY (JuceAU, JucePlugin_AUExportPrefix)
  1905. #endif
  1906. #if BUILD_AU_CARBON_UI
  1907. JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)
  1908. #endif
  1909. #if ! JUCE_DISABLE_AU_FACTORY_ENTRY
  1910. #include "CoreAudioUtilityClasses/AUPlugInDispatch.cpp"
  1911. #endif
  1912. #endif