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.

2199 lines
83KB

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