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.

2660 lines
101KB

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