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.

1708 lines
63KB

  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. // Your project must contain an AppConfig.h file with your project-specific settings in it,
  18. // and your header search path must make it accessible to the module's files.
  19. #include "AppConfig.h"
  20. #include "../utility/juce_CheckSettingMacros.h"
  21. #if JucePlugin_Build_AU
  22. #if __LP64__
  23. #undef JUCE_SUPPORT_CARBON
  24. #define JUCE_SUPPORT_CARBON 0
  25. #endif
  26. #ifdef __clang__
  27. #pragma clang diagnostic push
  28. #pragma clang diagnostic ignored "-Wshorten-64-to-32"
  29. #pragma clang diagnostic ignored "-Wunused-parameter"
  30. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  31. #pragma clang diagnostic ignored "-Wsign-conversion"
  32. #pragma clang diagnostic ignored "-Wconversion"
  33. #pragma clang diagnostic ignored "-Woverloaded-virtual"
  34. #endif
  35. #include "../utility/juce_IncludeSystemHeaders.h"
  36. #include <AudioUnit/AUCocoaUIView.h>
  37. #include <AudioUnit/AudioUnit.h>
  38. #include <AudioToolbox/AudioUnitUtilities.h>
  39. #include <CoreMIDI/MIDIServices.h>
  40. #if JUCE_SUPPORT_CARBON
  41. #define Point CarbonDummyPointName
  42. #define Component CarbonDummyCompName
  43. #endif
  44. /*
  45. Got an include error here?
  46. You probably need to install Apple's AU classes - see the
  47. juce website for more info on how to get them:
  48. http://www.juce.com/forum/topic/aus-xcode
  49. */
  50. #include "CoreAudioUtilityClasses/AUMIDIEffectBase.h"
  51. #include "CoreAudioUtilityClasses/MusicDeviceBase.h"
  52. #undef Point
  53. #undef Component
  54. /** The BUILD_AU_CARBON_UI flag lets you specify whether old-school carbon hosts are supported as
  55. well as ones that can open a cocoa view. If this is enabled, you'll need to also add the AUCarbonBase
  56. files to your project.
  57. */
  58. #if ! (defined (BUILD_AU_CARBON_UI) || JUCE_64BIT)
  59. #define BUILD_AU_CARBON_UI 1
  60. #endif
  61. #ifdef __LP64__
  62. #undef BUILD_AU_CARBON_UI // (not possible in a 64-bit build)
  63. #endif
  64. #if BUILD_AU_CARBON_UI
  65. #undef Button
  66. #define Point CarbonDummyPointName
  67. #include "CoreAudioUtilityClasses/AUCarbonViewBase.h"
  68. #undef Point
  69. #endif
  70. #ifdef __clang__
  71. #pragma clang diagnostic pop
  72. #endif
  73. #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1
  74. #include "../utility/juce_IncludeModuleHeaders.h"
  75. #include "../utility/juce_FakeMouseMoveGenerator.h"
  76. #include "../utility/juce_CarbonVisibility.h"
  77. #include "../../juce_core/native/juce_osx_ObjCHelpers.h"
  78. //==============================================================================
  79. static Array<void*> activePlugins, activeUIs;
  80. static const AudioUnitPropertyID juceFilterObjectPropertyID = 0x1a45ffe9;
  81. static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  82. static const int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs);
  83. #if JucePlugin_IsSynth
  84. class JuceAUBaseClass : public MusicDeviceBase
  85. {
  86. public:
  87. JuceAUBaseClass (AudioComponentInstance comp) : MusicDeviceBase (comp, 0, 1) {}
  88. };
  89. #else
  90. class JuceAUBaseClass : public AUMIDIEffectBase
  91. {
  92. public:
  93. JuceAUBaseClass (AudioComponentInstance comp) : AUMIDIEffectBase (comp, false) {}
  94. OSStatus MIDIEvent (UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame)
  95. {
  96. return AUMIDIBase::MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame);
  97. }
  98. OSStatus SysEx (const UInt8* inData, UInt32 inLength)
  99. {
  100. return AUMIDIBase::SysEx (inData, inLength);
  101. }
  102. };
  103. #endif
  104. // This macro can be set if you need to override this internal name for some reason..
  105. #ifndef JUCE_STATE_DICTIONARY_KEY
  106. #define JUCE_STATE_DICTIONARY_KEY CFSTR("jucePluginState")
  107. #endif
  108. //==============================================================================
  109. class JuceAU : public JuceAUBaseClass,
  110. public AudioProcessorListener,
  111. public AudioPlayHead,
  112. public ComponentListener
  113. {
  114. public:
  115. //==============================================================================
  116. JuceAU (AudioUnit component)
  117. : JuceAUBaseClass (component),
  118. bufferSpace (2, 16),
  119. prepared (false)
  120. {
  121. if (activePlugins.size() + activeUIs.size() == 0)
  122. {
  123. #if BUILD_AU_CARBON_UI
  124. NSApplicationLoad();
  125. #endif
  126. initialiseJuce_GUI();
  127. }
  128. juceFilter = createPluginFilterOfType (AudioProcessor::wrapperType_AudioUnit);
  129. juceFilter->setPlayHead (this);
  130. juceFilter->addListener (this);
  131. Globals()->UseIndexedParameters (juceFilter->getNumParameters());
  132. activePlugins.add (this);
  133. zerostruct (auEvent);
  134. auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance();
  135. auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  136. auEvent.mArgument.mParameter.mElement = 0;
  137. zerostruct (midiCallback);
  138. CreateElements();
  139. CAStreamBasicDescription streamDescription;
  140. streamDescription.mSampleRate = getSampleRate();
  141. streamDescription.SetCanonical ((UInt32) channelConfigs[0][1], false);
  142. Outputs().GetIOElement(0)->SetStreamFormat (streamDescription);
  143. #if ! JucePlugin_IsSynth
  144. streamDescription.SetCanonical ((UInt32) channelConfigs[0][0], false);
  145. Inputs().GetIOElement(0)->SetStreamFormat (streamDescription);
  146. #endif
  147. }
  148. ~JuceAU()
  149. {
  150. deleteActiveEditors();
  151. juceFilter = nullptr;
  152. clearPresetsArray();
  153. jassert (activePlugins.contains (this));
  154. activePlugins.removeFirstMatchingValue (this);
  155. if (activePlugins.size() + activeUIs.size() == 0)
  156. shutdownJuce_GUI();
  157. }
  158. void deleteActiveEditors()
  159. {
  160. for (int i = activeUIs.size(); --i >= 0;)
  161. {
  162. id ui = (id) activeUIs.getUnchecked(i);
  163. if (JuceUIViewClass::getAU (ui) == this)
  164. JuceUIViewClass::deleteEditor (ui);
  165. }
  166. }
  167. //==============================================================================
  168. ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,
  169. AudioUnitScope inScope,
  170. AudioUnitElement inElement,
  171. UInt32& outDataSize,
  172. Boolean& outWritable) override
  173. {
  174. if (inScope == kAudioUnitScope_Global)
  175. {
  176. switch (inID)
  177. {
  178. case juceFilterObjectPropertyID:
  179. outWritable = false;
  180. outDataSize = sizeof (void*) * 2;
  181. return noErr;
  182. case kAudioUnitProperty_OfflineRender:
  183. outWritable = true;
  184. outDataSize = sizeof (UInt32);
  185. return noErr;
  186. case kMusicDeviceProperty_InstrumentCount:
  187. outDataSize = sizeof (UInt32);
  188. outWritable = false;
  189. return noErr;
  190. case kAudioUnitProperty_CocoaUI:
  191. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  192. // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)
  193. if (SystemStats::getOperatingSystemType() >= SystemStats::MacOSX_10_5)
  194. #endif
  195. {
  196. outDataSize = sizeof (AudioUnitCocoaViewInfo);
  197. outWritable = true;
  198. return noErr;
  199. }
  200. break;
  201. #if JucePlugin_ProducesMidiOutput
  202. case kAudioUnitProperty_MIDIOutputCallbackInfo:
  203. outDataSize = sizeof (CFArrayRef);
  204. outWritable = false;
  205. return noErr;
  206. case kAudioUnitProperty_MIDIOutputCallback:
  207. outDataSize = sizeof (AUMIDIOutputCallbackStruct);
  208. outWritable = true;
  209. return noErr;
  210. #endif
  211. case kAudioUnitProperty_ParameterStringFromValue:
  212. outDataSize = sizeof (AudioUnitParameterStringFromValue);
  213. outWritable = false;
  214. return noErr;
  215. case kAudioUnitProperty_ParameterValueFromString:
  216. outDataSize = sizeof (AudioUnitParameterValueFromString);
  217. outWritable = false;
  218. return noErr;
  219. default: break;
  220. }
  221. }
  222. return JuceAUBaseClass::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
  223. }
  224. ComponentResult GetProperty (AudioUnitPropertyID inID,
  225. AudioUnitScope inScope,
  226. AudioUnitElement inElement,
  227. void* outData) override
  228. {
  229. if (inScope == kAudioUnitScope_Global)
  230. {
  231. switch (inID)
  232. {
  233. case juceFilterObjectPropertyID:
  234. ((void**) outData)[0] = (void*) static_cast<AudioProcessor*> (juceFilter);
  235. ((void**) outData)[1] = (void*) this;
  236. return noErr;
  237. case kAudioUnitProperty_OfflineRender:
  238. *(UInt32*) outData = (juceFilter != nullptr && juceFilter->isNonRealtime()) ? 1 : 0;
  239. return noErr;
  240. case kMusicDeviceProperty_InstrumentCount:
  241. *(UInt32*) outData = 1;
  242. return noErr;
  243. case kAudioUnitProperty_CocoaUI:
  244. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  245. // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)
  246. if (SystemStats::getOperatingSystemType() >= SystemStats::MacOSX_10_5)
  247. #endif
  248. {
  249. JUCE_AUTORELEASEPOOL
  250. {
  251. static JuceUICreationClass cls;
  252. // (NB: this may be the host's bundle, not necessarily the component's)
  253. NSBundle* bundle = [NSBundle bundleForClass: cls.cls];
  254. AudioUnitCocoaViewInfo* info = static_cast<AudioUnitCocoaViewInfo*> (outData);
  255. info->mCocoaAUViewClass[0] = (CFStringRef) [juceStringToNS (class_getName (cls.cls)) retain];
  256. info->mCocoaAUViewBundleLocation = (CFURLRef) [[NSURL fileURLWithPath: [bundle bundlePath]] retain];
  257. }
  258. return noErr;
  259. }
  260. break;
  261. #if JucePlugin_ProducesMidiOutput
  262. case kAudioUnitProperty_MIDIOutputCallbackInfo:
  263. {
  264. CFStringRef strs[1];
  265. strs[0] = CFSTR ("MIDI Callback");
  266. CFArrayRef callbackArray = CFArrayCreate (nullptr, (const void**) strs, 1, &kCFTypeArrayCallBacks);
  267. *(CFArrayRef*) outData = callbackArray;
  268. return noErr;
  269. }
  270. #endif
  271. case kAudioUnitProperty_ParameterValueFromString:
  272. {
  273. if (AudioUnitParameterValueFromString* pv = (AudioUnitParameterValueFromString*) outData)
  274. {
  275. if (juceFilter != nullptr)
  276. {
  277. const String text (String::fromCFString (pv->inString));
  278. if (AudioProcessorParameter* param = juceFilter->getParameters() [(int) pv->inParamID])
  279. pv->outValue = param->getValueForText (text);
  280. else
  281. pv->outValue = text.getFloatValue();
  282. return noErr;
  283. }
  284. }
  285. }
  286. break;
  287. case kAudioUnitProperty_ParameterStringFromValue:
  288. {
  289. if (AudioUnitParameterStringFromValue* pv = (AudioUnitParameterStringFromValue*) outData)
  290. {
  291. if (juceFilter != nullptr)
  292. {
  293. const float value = (float) *(pv->inValue);
  294. String text;
  295. if (AudioProcessorParameter* param = juceFilter->getParameters() [(int) pv->inParamID])
  296. text = param->getText ((float) *(pv->inValue), 0);
  297. else
  298. text = String (value);
  299. pv->outString = text.toCFString();
  300. return noErr;
  301. }
  302. }
  303. }
  304. break;
  305. default:
  306. break;
  307. }
  308. }
  309. return JuceAUBaseClass::GetProperty (inID, inScope, inElement, outData);
  310. }
  311. ComponentResult SetProperty (AudioUnitPropertyID inID,
  312. AudioUnitScope inScope,
  313. AudioUnitElement inElement,
  314. const void* inData,
  315. UInt32 inDataSize) override
  316. {
  317. if (inScope == kAudioUnitScope_Global)
  318. {
  319. switch (inID)
  320. {
  321. #if JucePlugin_ProducesMidiOutput
  322. case kAudioUnitProperty_MIDIOutputCallback:
  323. if (inDataSize < sizeof (AUMIDIOutputCallbackStruct))
  324. return kAudioUnitErr_InvalidPropertyValue;
  325. if (AUMIDIOutputCallbackStruct* callbackStruct = (AUMIDIOutputCallbackStruct*) inData)
  326. midiCallback = *callbackStruct;
  327. return noErr;
  328. #endif
  329. case kAudioUnitProperty_OfflineRender:
  330. if (juceFilter != nullptr)
  331. juceFilter->setNonRealtime ((*(UInt32*) inData) != 0);
  332. return noErr;
  333. default: break;
  334. }
  335. }
  336. return JuceAUBaseClass::SetProperty (inID, inScope, inElement, inData, inDataSize);
  337. }
  338. ComponentResult SaveState (CFPropertyListRef* outData) override
  339. {
  340. ComponentResult err = JuceAUBaseClass::SaveState (outData);
  341. if (err != noErr)
  342. return err;
  343. jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID());
  344. CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData;
  345. if (juceFilter != nullptr)
  346. {
  347. juce::MemoryBlock state;
  348. juceFilter->getCurrentProgramStateInformation (state);
  349. if (state.getSize() > 0)
  350. {
  351. CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const UInt8*) state.getData(), (CFIndex) state.getSize());
  352. CFDictionarySetValue (dict, JUCE_STATE_DICTIONARY_KEY, ourState);
  353. CFRelease (ourState);
  354. }
  355. }
  356. return noErr;
  357. }
  358. ComponentResult RestoreState (CFPropertyListRef inData) override
  359. {
  360. {
  361. // Remove the data entry from the state to prevent the superclass loading the parameters
  362. CFMutableDictionaryRef copyWithoutData = CFDictionaryCreateMutableCopy (nullptr, 0, (CFDictionaryRef) inData);
  363. CFDictionaryRemoveValue (copyWithoutData, CFSTR (kAUPresetDataKey));
  364. ComponentResult err = JuceAUBaseClass::RestoreState (copyWithoutData);
  365. CFRelease (copyWithoutData);
  366. if (err != noErr)
  367. return err;
  368. }
  369. if (juceFilter != nullptr)
  370. {
  371. CFDictionaryRef dict = (CFDictionaryRef) inData;
  372. CFDataRef data = 0;
  373. if (CFDictionaryGetValueIfPresent (dict, JUCE_STATE_DICTIONARY_KEY, (const void**) &data))
  374. {
  375. if (data != 0)
  376. {
  377. const int numBytes = (int) CFDataGetLength (data);
  378. const juce::uint8* const rawBytes = CFDataGetBytePtr (data);
  379. if (numBytes > 0)
  380. juceFilter->setCurrentProgramStateInformation (rawBytes, numBytes);
  381. }
  382. }
  383. }
  384. return noErr;
  385. }
  386. UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) override
  387. {
  388. // If you hit this, then you need to add some configurations to your
  389. // JucePlugin_PreferredChannelConfigurations setting..
  390. jassert (numChannelConfigs > 0);
  391. if (outInfo != nullptr)
  392. {
  393. *outInfo = channelInfo;
  394. for (int i = 0; i < numChannelConfigs; ++i)
  395. {
  396. #if JucePlugin_IsSynth
  397. channelInfo[i].inChannels = 0;
  398. #else
  399. channelInfo[i].inChannels = channelConfigs[i][0];
  400. #endif
  401. channelInfo[i].outChannels = channelConfigs[i][1];
  402. }
  403. }
  404. return numChannelConfigs;
  405. }
  406. UInt32 GetAudioChannelLayout (AudioUnitScope scope,
  407. AudioUnitElement element,
  408. AudioChannelLayout *outLayoutPtr,
  409. Boolean &outWritable) override
  410. {
  411. // fallback to old code if this plug-in does not have multi channel IO
  412. if (! hasMultiChannelConfiguration())
  413. return 0;
  414. if (element == 0 && (scope == kAudioUnitScope_Output || scope == kAudioUnitScope_Input))
  415. {
  416. outWritable = false;
  417. const int numChannels = (scope == kAudioUnitScope_Output) ? findNumOutputChannels()
  418. : findNumInputChannels();
  419. const size_t sizeInBytes = (sizeof (AudioChannelLayout) - sizeof (AudioChannelDescription)) +
  420. (static_cast<size_t> (numChannels) * sizeof (AudioChannelDescription));
  421. if (outLayoutPtr != nullptr)
  422. {
  423. zeromem (outLayoutPtr, sizeInBytes);
  424. outLayoutPtr->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
  425. outLayoutPtr->mNumberChannelDescriptions = static_cast<UInt32> (numChannels);
  426. for (int i = 0; i < numChannels; ++i)
  427. {
  428. AudioChannelDescription& layoutDescr = outLayoutPtr->mChannelDescriptions [i];
  429. layoutDescr.mChannelLabel = kAudioChannelLabel_Unused;
  430. layoutDescr.mChannelFlags = kAudioChannelFlags_AllOff;
  431. }
  432. }
  433. return static_cast<UInt32> (sizeInBytes);
  434. }
  435. return JuceAUBaseClass::GetAudioChannelLayout(scope, element, outLayoutPtr, outWritable);
  436. }
  437. //==============================================================================
  438. ComponentResult GetParameterInfo (AudioUnitScope inScope,
  439. AudioUnitParameterID inParameterID,
  440. AudioUnitParameterInfo& outParameterInfo) override
  441. {
  442. const int index = (int) inParameterID;
  443. if (inScope == kAudioUnitScope_Global
  444. && juceFilter != nullptr
  445. && index < juceFilter->getNumParameters())
  446. {
  447. outParameterInfo.flags = (UInt32) (kAudioUnitParameterFlag_IsWritable
  448. | kAudioUnitParameterFlag_IsReadable
  449. | kAudioUnitParameterFlag_HasCFNameString
  450. | kAudioUnitParameterFlag_ValuesHaveStrings);
  451. #if JucePlugin_AUHighResolutionParameters
  452. outParameterInfo.flags |= (UInt32) kAudioUnitParameterFlag_IsHighResolution;
  453. #endif
  454. const String name (juceFilter->getParameterName (index));
  455. // set whether the param is automatable (unnamed parameters aren't allowed to be automated)
  456. if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))
  457. outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
  458. if (juceFilter->isMetaParameter (index))
  459. outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
  460. AUBase::FillInParameterName (outParameterInfo, name.toCFString(), true);
  461. outParameterInfo.minValue = 0.0f;
  462. outParameterInfo.maxValue = 1.0f;
  463. outParameterInfo.defaultValue = juceFilter->getParameterDefaultValue (index);
  464. jassert (outParameterInfo.defaultValue >= outParameterInfo.minValue
  465. && outParameterInfo.defaultValue <= outParameterInfo.maxValue);
  466. outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
  467. return noErr;
  468. }
  469. return kAudioUnitErr_InvalidParameter;
  470. }
  471. ComponentResult GetParameter (AudioUnitParameterID inID,
  472. AudioUnitScope inScope,
  473. AudioUnitElement inElement,
  474. Float32& outValue) override
  475. {
  476. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  477. {
  478. outValue = juceFilter->getParameter ((int) inID);
  479. return noErr;
  480. }
  481. return AUBase::GetParameter (inID, inScope, inElement, outValue);
  482. }
  483. ComponentResult SetParameter (AudioUnitParameterID inID,
  484. AudioUnitScope inScope,
  485. AudioUnitElement inElement,
  486. Float32 inValue,
  487. UInt32 inBufferOffsetInFrames) override
  488. {
  489. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  490. {
  491. juceFilter->setParameter ((int) inID, inValue);
  492. return noErr;
  493. }
  494. return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames);
  495. }
  496. // No idea what this method actually does or what it should return. Current Apple docs say nothing about it.
  497. // (Note that this isn't marked 'override' in case older versions of the SDK don't include it)
  498. bool CanScheduleParameters() const override { return false; }
  499. //==============================================================================
  500. ComponentResult Version() override { return JucePlugin_VersionCode; }
  501. bool SupportsTail() override { return true; }
  502. Float64 GetTailTime() override { return juceFilter->getTailLengthSeconds(); }
  503. double getSampleRate() { return GetOutput(0)->GetStreamFormat().mSampleRate; }
  504. Float64 GetLatency() override
  505. {
  506. const double rate = getSampleRate();
  507. jassert (rate > 0);
  508. return rate > 0 ? juceFilter->getLatencySamples() / rate : 0;
  509. }
  510. //==============================================================================
  511. #if BUILD_AU_CARBON_UI
  512. int GetNumCustomUIComponents() override
  513. {
  514. return getHostType().isDigitalPerformer() ? 0 : 1;
  515. }
  516. void GetUIComponentDescs (ComponentDescription* inDescArray) override
  517. {
  518. inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;
  519. inDescArray[0].componentSubType = JucePlugin_AUSubType;
  520. inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode;
  521. inDescArray[0].componentFlags = 0;
  522. inDescArray[0].componentFlagsMask = 0;
  523. }
  524. #endif
  525. //==============================================================================
  526. bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) override
  527. {
  528. info.timeSigNumerator = 0;
  529. info.timeSigDenominator = 0;
  530. info.editOriginTime = 0;
  531. info.ppqPositionOfLastBarStart = 0;
  532. info.isRecording = false;
  533. info.ppqLoopStart = 0;
  534. info.ppqLoopEnd = 0;
  535. switch (lastTimeStamp.mSMPTETime.mType)
  536. {
  537. case kSMPTETimeType24: info.frameRate = AudioPlayHead::fps24; break;
  538. case kSMPTETimeType25: info.frameRate = AudioPlayHead::fps25; break;
  539. case kSMPTETimeType30Drop: info.frameRate = AudioPlayHead::fps30drop; break;
  540. case kSMPTETimeType30: info.frameRate = AudioPlayHead::fps30; break;
  541. case kSMPTETimeType2997: info.frameRate = AudioPlayHead::fps2997; break;
  542. case kSMPTETimeType2997Drop: info.frameRate = AudioPlayHead::fps2997drop; break;
  543. //case kSMPTETimeType60:
  544. //case kSMPTETimeType5994:
  545. default: info.frameRate = AudioPlayHead::fpsUnknown; break;
  546. }
  547. if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)
  548. {
  549. info.ppqPosition = 0;
  550. info.bpm = 0;
  551. }
  552. UInt32 outDeltaSampleOffsetToNextBeat;
  553. double outCurrentMeasureDownBeat;
  554. float num;
  555. UInt32 den;
  556. if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,
  557. &outCurrentMeasureDownBeat) == noErr)
  558. {
  559. info.timeSigNumerator = (int) num;
  560. info.timeSigDenominator = (int) den;
  561. info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
  562. }
  563. double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat;
  564. Boolean playing = false, looping = false, playchanged;
  565. if (CallHostTransportState (&playing,
  566. &playchanged,
  567. &outCurrentSampleInTimeLine,
  568. &looping,
  569. &outCycleStartBeat,
  570. &outCycleEndBeat) != noErr)
  571. {
  572. // If the host doesn't support this callback, then use the sample time from lastTimeStamp:
  573. outCurrentSampleInTimeLine = lastTimeStamp.mSampleTime;
  574. }
  575. info.isPlaying = playing;
  576. info.timeInSamples = (int64) (outCurrentSampleInTimeLine + 0.5);
  577. info.timeInSeconds = info.timeInSamples / getSampleRate();
  578. info.isLooping = looping;
  579. return true;
  580. }
  581. void sendAUEvent (const AudioUnitEventType type, const int index)
  582. {
  583. auEvent.mEventType = type;
  584. auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index;
  585. AUEventListenerNotify (0, 0, &auEvent);
  586. }
  587. void audioProcessorParameterChanged (AudioProcessor*, int index, float /*newValue*/) override
  588. {
  589. sendAUEvent (kAudioUnitEvent_ParameterValueChange, index);
  590. }
  591. void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index) override
  592. {
  593. sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index);
  594. }
  595. void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index) override
  596. {
  597. sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
  598. }
  599. void audioProcessorChanged (AudioProcessor*) override
  600. {
  601. PropertyChanged (kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0);
  602. PropertyChanged (kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
  603. PropertyChanged (kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, 0);
  604. refreshCurrentPreset();
  605. PropertyChanged (kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0);
  606. }
  607. bool StreamFormatWritable (AudioUnitScope, AudioUnitElement) override
  608. {
  609. return ! IsInitialized();
  610. }
  611. ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID*, UInt32, const MusicDeviceNoteParams&) override { return noErr; }
  612. ComponentResult StopNote (MusicDeviceGroupID, NoteInstanceID, UInt32) override { return noErr; }
  613. //==============================================================================
  614. ComponentResult Initialize() override
  615. {
  616. #if ! JucePlugin_IsSynth
  617. const int numIns = findNumInputChannels();
  618. #endif
  619. const int numOuts = findNumOutputChannels();
  620. bool isValidChannelConfig = false;
  621. for (int i = 0; i < numChannelConfigs; ++i)
  622. #if JucePlugin_IsSynth
  623. if (numOuts == channelConfigs[i][1])
  624. #else
  625. if (numIns == channelConfigs[i][0] && numOuts == channelConfigs[i][1])
  626. #endif
  627. isValidChannelConfig = true;
  628. if (! isValidChannelConfig)
  629. return kAudioUnitErr_FormatNotSupported;
  630. JuceAUBaseClass::Initialize();
  631. prepareToPlay();
  632. return noErr;
  633. }
  634. void Cleanup() override
  635. {
  636. JuceAUBaseClass::Cleanup();
  637. if (juceFilter != nullptr)
  638. juceFilter->releaseResources();
  639. bufferSpace.setSize (2, 16);
  640. midiEvents.clear();
  641. incomingEvents.clear();
  642. prepared = false;
  643. }
  644. ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement) override
  645. {
  646. if (! prepared)
  647. prepareToPlay();
  648. if (juceFilter != nullptr)
  649. juceFilter->reset();
  650. return JuceAUBaseClass::Reset (inScope, inElement);
  651. }
  652. int findNumInputChannels()
  653. {
  654. #if ! JucePlugin_IsSynth
  655. if (AUInputElement* e = GetInput(0))
  656. return (int) e->GetStreamFormat().mChannelsPerFrame;
  657. #endif
  658. return 0;
  659. }
  660. int findNumOutputChannels()
  661. {
  662. if (AUOutputElement* e = GetOutput(0))
  663. return (int) e->GetStreamFormat().mChannelsPerFrame;
  664. return 0;
  665. }
  666. void prepareToPlay()
  667. {
  668. if (juceFilter != nullptr)
  669. {
  670. juceFilter->setPlayConfigDetails (findNumInputChannels(),
  671. findNumOutputChannels(),
  672. getSampleRate(),
  673. (int) GetMaxFramesPerSlice());
  674. bufferSpace.setSize (juceFilter->getNumInputChannels() + juceFilter->getNumOutputChannels(),
  675. (int) GetMaxFramesPerSlice() + 32);
  676. juceFilter->prepareToPlay (getSampleRate(), (int) GetMaxFramesPerSlice());
  677. midiEvents.ensureSize (2048);
  678. midiEvents.clear();
  679. incomingEvents.ensureSize (2048);
  680. incomingEvents.clear();
  681. channels.calloc ((size_t) jmax (juceFilter->getNumInputChannels(),
  682. juceFilter->getNumOutputChannels()) + 4);
  683. prepared = true;
  684. }
  685. }
  686. ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags,
  687. const AudioTimeStamp& inTimeStamp,
  688. UInt32 nFrames) override
  689. {
  690. lastTimeStamp = inTimeStamp;
  691. #if ! JucePlugin_IsSynth
  692. return JuceAUBaseClass::Render (ioActionFlags, inTimeStamp, nFrames);
  693. #else
  694. // synths can't have any inputs..
  695. AudioBufferList inBuffer;
  696. inBuffer.mNumberBuffers = 0;
  697. return ProcessBufferLists (ioActionFlags, inBuffer, GetOutput(0)->GetBufferList(), nFrames);
  698. #endif
  699. }
  700. OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags,
  701. const AudioBufferList& inBuffer,
  702. AudioBufferList& outBuffer,
  703. UInt32 numSamples) override
  704. {
  705. if (juceFilter != nullptr)
  706. {
  707. jassert (prepared);
  708. int numOutChans = 0;
  709. int nextSpareBufferChan = 0;
  710. bool needToReinterleave = false;
  711. const int numIn = juceFilter->getNumInputChannels();
  712. const int numOut = juceFilter->getNumOutputChannels();
  713. for (unsigned int i = 0; i < outBuffer.mNumberBuffers; ++i)
  714. {
  715. ::AudioBuffer& buf = outBuffer.mBuffers[i];
  716. if (buf.mNumberChannels == 1)
  717. {
  718. channels [numOutChans++] = (float*) buf.mData;
  719. }
  720. else
  721. {
  722. needToReinterleave = true;
  723. for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numOutChans < numOut; ++subChan)
  724. channels [numOutChans++] = bufferSpace.getWritePointer (nextSpareBufferChan++);
  725. }
  726. if (numOutChans >= numOut)
  727. break;
  728. }
  729. int numInChans = 0;
  730. for (unsigned int i = 0; i < inBuffer.mNumberBuffers; ++i)
  731. {
  732. const ::AudioBuffer& buf = inBuffer.mBuffers[i];
  733. if (buf.mNumberChannels == 1)
  734. {
  735. if (numInChans < numOutChans)
  736. memcpy (channels [numInChans], (const float*) buf.mData, sizeof (float) * numSamples);
  737. else
  738. channels [numInChans] = (float*) buf.mData;
  739. ++numInChans;
  740. }
  741. else
  742. {
  743. // need to de-interleave..
  744. for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numInChans < numIn; ++subChan)
  745. {
  746. float* dest;
  747. if (numInChans < numOutChans)
  748. {
  749. dest = channels [numInChans++];
  750. }
  751. else
  752. {
  753. dest = bufferSpace.getWritePointer (nextSpareBufferChan++);
  754. channels [numInChans++] = dest;
  755. }
  756. const float* src = ((const float*) buf.mData) + subChan;
  757. for (int j = (int) numSamples; --j >= 0;)
  758. {
  759. *dest++ = *src;
  760. src += buf.mNumberChannels;
  761. }
  762. }
  763. }
  764. if (numInChans >= numIn)
  765. break;
  766. }
  767. {
  768. const ScopedLock sl (incomingMidiLock);
  769. midiEvents.clear();
  770. incomingEvents.swapWith (midiEvents);
  771. }
  772. {
  773. AudioSampleBuffer buffer (channels, jmax (numIn, numOut), (int) numSamples);
  774. const ScopedLock sl (juceFilter->getCallbackLock());
  775. if (juceFilter->isSuspended())
  776. {
  777. for (int j = 0; j < numOut; ++j)
  778. zeromem (channels [j], sizeof (float) * numSamples);
  779. }
  780. #if ! JucePlugin_IsSynth
  781. else if (ShouldBypassEffect())
  782. {
  783. juceFilter->processBlockBypassed (buffer, midiEvents);
  784. }
  785. #endif
  786. else
  787. {
  788. juceFilter->processBlock (buffer, midiEvents);
  789. }
  790. }
  791. if (! midiEvents.isEmpty())
  792. {
  793. #if JucePlugin_ProducesMidiOutput
  794. if (midiCallback.midiOutputCallback != nullptr)
  795. {
  796. UInt32 numPackets = 0;
  797. size_t dataSize = 0;
  798. const juce::uint8* midiEventData;
  799. int midiEventSize, midiEventPosition;
  800. for (MidiBuffer::Iterator i (midiEvents); i.getNextEvent (midiEventData, midiEventSize, midiEventPosition);)
  801. {
  802. jassert (isPositiveAndBelow (midiEventPosition, (int) numSamples));
  803. dataSize += (size_t) midiEventSize;
  804. ++numPackets;
  805. }
  806. MIDIPacket* p;
  807. const size_t packetMembersSize = sizeof (MIDIPacket) - sizeof (p->data); // NB: GCC chokes on "sizeof (MidiMessage::data)"
  808. const size_t packetListMembersSize = sizeof (MIDIPacketList) - sizeof (p->data);
  809. HeapBlock<MIDIPacketList> packetList;
  810. packetList.malloc (packetListMembersSize + packetMembersSize * numPackets + dataSize, 1);
  811. packetList->numPackets = numPackets;
  812. p = packetList->packet;
  813. for (MidiBuffer::Iterator i (midiEvents); i.getNextEvent (midiEventData, midiEventSize, midiEventPosition);)
  814. {
  815. p->timeStamp = (MIDITimeStamp) midiEventPosition;
  816. p->length = (UInt16) midiEventSize;
  817. memcpy (p->data, midiEventData, (size_t) midiEventSize);
  818. p = MIDIPacketNext (p);
  819. }
  820. midiCallback.midiOutputCallback (midiCallback.userData, &lastTimeStamp, 0, packetList);
  821. }
  822. #endif
  823. midiEvents.clear();
  824. }
  825. if (needToReinterleave)
  826. {
  827. nextSpareBufferChan = 0;
  828. for (unsigned int i = 0; i < outBuffer.mNumberBuffers; ++i)
  829. {
  830. ::AudioBuffer& buf = outBuffer.mBuffers[i];
  831. if (buf.mNumberChannels > 1)
  832. {
  833. for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan)
  834. {
  835. const float* src = bufferSpace.getReadPointer (nextSpareBufferChan++);
  836. float* dest = ((float*) buf.mData) + subChan;
  837. for (int j = (int) numSamples; --j >= 0;)
  838. {
  839. *dest = *src++;
  840. dest += buf.mNumberChannels;
  841. }
  842. }
  843. }
  844. }
  845. }
  846. #if ! JucePlugin_SilenceInProducesSilenceOut
  847. ioActionFlags &= (AudioUnitRenderActionFlags) ~kAudioUnitRenderAction_OutputIsSilence;
  848. #else
  849. ignoreUnused (ioActionFlags);
  850. #endif
  851. }
  852. return noErr;
  853. }
  854. OSStatus HandleMidiEvent (UInt8 nStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2, UInt32 inStartFrame) override
  855. {
  856. #if JucePlugin_WantsMidiInput
  857. const ScopedLock sl (incomingMidiLock);
  858. const juce::uint8 data[] = { (juce::uint8) (nStatus | inChannel),
  859. (juce::uint8) inData1,
  860. (juce::uint8) inData2 };
  861. incomingEvents.addEvent (data, 3, (int) inStartFrame);
  862. return noErr;
  863. #else
  864. (void) nStatus; (void) inChannel; (void) inData1; (void) inData2; (void) inStartFrame;
  865. return kAudioUnitErr_PropertyNotInUse;
  866. #endif
  867. }
  868. OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength) override
  869. {
  870. #if JucePlugin_WantsMidiInput
  871. const ScopedLock sl (incomingMidiLock);
  872. incomingEvents.addEvent (inData, (int) inLength, 0);
  873. return noErr;
  874. #else
  875. (void) inData; (void) inLength;
  876. return kAudioUnitErr_PropertyNotInUse;
  877. #endif
  878. }
  879. //==============================================================================
  880. ComponentResult GetPresets (CFArrayRef* outData) const override
  881. {
  882. if (outData != nullptr)
  883. {
  884. const int numPrograms = juceFilter->getNumPrograms();
  885. clearPresetsArray();
  886. presetsArray.insertMultiple (0, AUPreset(), numPrograms);
  887. CFMutableArrayRef presetsArrayRef = CFArrayCreateMutable (0, numPrograms, 0);
  888. for (int i = 0; i < numPrograms; ++i)
  889. {
  890. String name (juceFilter->getProgramName(i));
  891. if (name.isEmpty())
  892. name = "Untitled";
  893. AUPreset& p = presetsArray.getReference(i);
  894. p.presetNumber = i;
  895. p.presetName = name.toCFString();
  896. CFArrayAppendValue (presetsArrayRef, &p);
  897. }
  898. *outData = (CFArrayRef) presetsArrayRef;
  899. }
  900. return noErr;
  901. }
  902. OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) override
  903. {
  904. const int numPrograms = juceFilter->getNumPrograms();
  905. const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;
  906. if (chosenPresetNumber >= numPrograms)
  907. return kAudioUnitErr_InvalidProperty;
  908. AUPreset chosenPreset;
  909. chosenPreset.presetNumber = chosenPresetNumber;
  910. chosenPreset.presetName = juceFilter->getProgramName (chosenPresetNumber).toCFString();
  911. juceFilter->setCurrentProgram (chosenPresetNumber);
  912. SetAFactoryPresetAsCurrent (chosenPreset);
  913. return noErr;
  914. }
  915. void componentMovedOrResized (Component& component, bool /*wasMoved*/, bool /*wasResized*/) override
  916. {
  917. NSView* view = (NSView*) component.getWindowHandle();
  918. NSRect r = [[view superview] frame];
  919. r.origin.y = r.origin.y + r.size.height - component.getHeight();
  920. r.size.width = component.getWidth();
  921. r.size.height = component.getHeight();
  922. [[view superview] setFrame: r];
  923. [view setFrame: makeNSRect (component.getLocalBounds())];
  924. [view setNeedsDisplay: YES];
  925. }
  926. //==============================================================================
  927. class EditorCompHolder : public Component
  928. {
  929. public:
  930. EditorCompHolder (AudioProcessorEditor* const editor)
  931. {
  932. setSize (editor->getWidth(), editor->getHeight());
  933. addAndMakeVisible (editor);
  934. #if ! JucePlugin_EditorRequiresKeyboardFocus
  935. setWantsKeyboardFocus (false);
  936. #else
  937. setWantsKeyboardFocus (true);
  938. #endif
  939. }
  940. ~EditorCompHolder()
  941. {
  942. deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
  943. // have been transferred to another parent which takes over ownership.
  944. }
  945. static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
  946. {
  947. EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
  948. NSRect r = makeNSRect (editorCompHolder->getLocalBounds());
  949. static JuceUIViewClass cls;
  950. NSView* view = [[cls.createInstance() initWithFrame: r] autorelease];
  951. JuceUIViewClass::setFilter (view, filter);
  952. JuceUIViewClass::setAU (view, au);
  953. JuceUIViewClass::setEditor (view, editorCompHolder);
  954. [view setHidden: NO];
  955. [view setPostsFrameChangedNotifications: YES];
  956. [[NSNotificationCenter defaultCenter] addObserver: view
  957. selector: @selector (applicationWillTerminate:)
  958. name: NSApplicationWillTerminateNotification
  959. object: nil];
  960. activeUIs.add (view);
  961. editorCompHolder->addToDesktop (0, (void*) view);
  962. editorCompHolder->setVisible (view);
  963. return view;
  964. }
  965. void childBoundsChanged (Component*) override
  966. {
  967. if (Component* editor = getChildComponent(0))
  968. {
  969. const int w = jmax (32, editor->getWidth());
  970. const int h = jmax (32, editor->getHeight());
  971. if (getWidth() != w || getHeight() != h)
  972. setSize (w, h);
  973. NSView* view = (NSView*) getWindowHandle();
  974. NSRect r = [[view superview] frame];
  975. r.size.width = editor->getWidth();
  976. r.size.height = editor->getHeight();
  977. [[view superview] setFrame: r];
  978. [view setFrame: makeNSRect (editor->getLocalBounds())];
  979. [view setNeedsDisplay: YES];
  980. }
  981. }
  982. bool keyPressed (const KeyPress&) override
  983. {
  984. if (getHostType().isAbletonLive())
  985. {
  986. static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
  987. NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
  988. if (lastEventTime != eventTime)
  989. {
  990. lastEventTime = eventTime;
  991. NSView* view = (NSView*) getWindowHandle();
  992. NSView* hostView = [view superview];
  993. NSWindow* hostWindow = [hostView window];
  994. [hostWindow makeFirstResponder: hostView];
  995. [hostView keyDown: [NSApp currentEvent]];
  996. [hostWindow makeFirstResponder: view];
  997. }
  998. }
  999. return false;
  1000. }
  1001. private:
  1002. JUCE_DECLARE_NON_COPYABLE (EditorCompHolder)
  1003. };
  1004. //==============================================================================
  1005. struct JuceUIViewClass : public ObjCClass<NSView>
  1006. {
  1007. JuceUIViewClass() : ObjCClass<NSView> ("JUCEAUView_")
  1008. {
  1009. addIvar<AudioProcessor*> ("filter");
  1010. addIvar<JuceAU*> ("au");
  1011. addIvar<EditorCompHolder*> ("editor");
  1012. addMethod (@selector (dealloc), dealloc, "v@:");
  1013. addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
  1014. addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:");
  1015. addMethod (@selector (mouseDownCanMoveWindow), mouseDownCanMoveWindow, "c@:");
  1016. registerClass();
  1017. }
  1018. static void deleteEditor (id self)
  1019. {
  1020. ScopedPointer<EditorCompHolder> editorComp (getEditor (self));
  1021. if (editorComp != nullptr)
  1022. {
  1023. if (editorComp->getChildComponent(0) != nullptr
  1024. && activePlugins.contains (getAU (self))) // plugin may have been deleted before the UI
  1025. {
  1026. AudioProcessor* const filter = getIvar<AudioProcessor*> (self, "filter");
  1027. filter->editorBeingDeleted ((AudioProcessorEditor*) editorComp->getChildComponent(0));
  1028. }
  1029. editorComp = nullptr;
  1030. setEditor (self, nullptr);
  1031. }
  1032. }
  1033. static JuceAU* getAU (id self) { return getIvar<JuceAU*> (self, "au"); }
  1034. static EditorCompHolder* getEditor (id self) { return getIvar<EditorCompHolder*> (self, "editor"); }
  1035. static void setFilter (id self, AudioProcessor* filter) { object_setInstanceVariable (self, "filter", filter); }
  1036. static void setAU (id self, JuceAU* au) { object_setInstanceVariable (self, "au", au); }
  1037. static void setEditor (id self, EditorCompHolder* e) { object_setInstanceVariable (self, "editor", e); }
  1038. private:
  1039. static void dealloc (id self, SEL)
  1040. {
  1041. if (activeUIs.contains (self))
  1042. shutdown (self);
  1043. sendSuperclassMessage (self, @selector (dealloc));
  1044. }
  1045. static void applicationWillTerminate (id self, SEL, NSNotification*)
  1046. {
  1047. shutdown (self);
  1048. }
  1049. static void shutdown (id self)
  1050. {
  1051. [[NSNotificationCenter defaultCenter] removeObserver: self];
  1052. deleteEditor (self);
  1053. jassert (activeUIs.contains (self));
  1054. activeUIs.removeFirstMatchingValue (self);
  1055. if (activePlugins.size() + activeUIs.size() == 0)
  1056. {
  1057. // there's some kind of component currently modal, but the host
  1058. // is trying to delete our plugin..
  1059. jassert (Component::getCurrentlyModalComponent() == nullptr);
  1060. shutdownJuce_GUI();
  1061. }
  1062. }
  1063. static void viewDidMoveToWindow (id self, SEL)
  1064. {
  1065. if (NSWindow* w = [(NSView*) self window])
  1066. {
  1067. [w setAcceptsMouseMovedEvents: YES];
  1068. if (EditorCompHolder* const editorComp = getEditor (self))
  1069. [w makeFirstResponder: (NSView*) editorComp->getWindowHandle()];
  1070. }
  1071. }
  1072. static BOOL mouseDownCanMoveWindow (id, SEL)
  1073. {
  1074. return NO;
  1075. }
  1076. };
  1077. //==============================================================================
  1078. struct JuceUICreationClass : public ObjCClass<NSObject>
  1079. {
  1080. JuceUICreationClass() : ObjCClass<NSObject> ("JUCE_AUCocoaViewClass_")
  1081. {
  1082. addMethod (@selector (interfaceVersion), interfaceVersion, @encode (unsigned int), "@:");
  1083. addMethod (@selector (description), description, @encode (NSString*), "@:");
  1084. addMethod (@selector (uiViewForAudioUnit:withSize:), uiViewForAudioUnit, @encode (NSView*), "@:", @encode (AudioUnit), @encode (NSSize));
  1085. addProtocol (@protocol (AUCocoaUIBase));
  1086. registerClass();
  1087. }
  1088. private:
  1089. static unsigned int interfaceVersion (id, SEL) { return 0; }
  1090. static NSString* description (id, SEL)
  1091. {
  1092. return [NSString stringWithString: nsStringLiteral (JucePlugin_Name)];
  1093. }
  1094. static NSView* uiViewForAudioUnit (id, SEL, AudioUnit inAudioUnit, NSSize)
  1095. {
  1096. void* pointers[2];
  1097. UInt32 propertySize = sizeof (pointers);
  1098. if (AudioUnitGetProperty (inAudioUnit, juceFilterObjectPropertyID,
  1099. kAudioUnitScope_Global, 0, pointers, &propertySize) == noErr)
  1100. {
  1101. if (AudioProcessor* filter = static_cast<AudioProcessor*> (pointers[0]))
  1102. if (AudioProcessorEditor* editorComp = filter->createEditorIfNeeded())
  1103. return EditorCompHolder::createViewFor (filter, static_cast<JuceAU*> (pointers[1]), editorComp);
  1104. }
  1105. return nil;
  1106. }
  1107. };
  1108. private:
  1109. //==============================================================================
  1110. ScopedPointer<AudioProcessor> juceFilter;
  1111. AudioSampleBuffer bufferSpace;
  1112. HeapBlock<float*> channels;
  1113. MidiBuffer midiEvents, incomingEvents;
  1114. bool prepared;
  1115. AUChannelInfo channelInfo [numChannelConfigs];
  1116. AudioUnitEvent auEvent;
  1117. mutable Array<AUPreset> presetsArray;
  1118. CriticalSection incomingMidiLock;
  1119. AUMIDIOutputCallbackStruct midiCallback;
  1120. AudioTimeStamp lastTimeStamp;
  1121. void clearPresetsArray() const
  1122. {
  1123. for (int i = presetsArray.size(); --i >= 0;)
  1124. CFRelease (presetsArray.getReference(i).presetName);
  1125. presetsArray.clear();
  1126. }
  1127. void refreshCurrentPreset()
  1128. {
  1129. // this will make the AU host re-read and update the current preset name
  1130. // in case it was changed here in the plug-in:
  1131. const int currentProgramNumber = juceFilter->getCurrentProgram();
  1132. const String currentProgramName = juceFilter->getProgramName (currentProgramNumber);
  1133. AUPreset currentPreset;
  1134. currentPreset.presetNumber = currentProgramNumber;
  1135. currentPreset.presetName = currentProgramName.toCFString();
  1136. SetAFactoryPresetAsCurrent (currentPreset);
  1137. }
  1138. //==============================================================================
  1139. bool hasMultiChannelConfiguration () noexcept
  1140. {
  1141. for (int i = 0; i < numChannelConfigs; ++i)
  1142. {
  1143. #if !JucePlugin_IsSynth
  1144. if (channelConfigs[i][0] > 2)
  1145. return true;
  1146. #endif
  1147. if (channelConfigs[i][1] > 2)
  1148. return true;
  1149. }
  1150. return false;
  1151. }
  1152. JUCE_DECLARE_NON_COPYABLE (JuceAU)
  1153. };
  1154. //==============================================================================
  1155. #if BUILD_AU_CARBON_UI
  1156. class JuceAUView : public AUCarbonViewBase
  1157. {
  1158. public:
  1159. JuceAUView (AudioUnitCarbonView auview)
  1160. : AUCarbonViewBase (auview),
  1161. juceFilter (nullptr)
  1162. {
  1163. }
  1164. ~JuceAUView()
  1165. {
  1166. deleteUI();
  1167. }
  1168. ComponentResult CreateUI (Float32 /*inXOffset*/, Float32 /*inYOffset*/) override
  1169. {
  1170. JUCE_AUTORELEASEPOOL
  1171. {
  1172. if (juceFilter == nullptr)
  1173. {
  1174. void* pointers[2];
  1175. UInt32 propertySize = sizeof (pointers);
  1176. AudioUnitGetProperty (GetEditAudioUnit(),
  1177. juceFilterObjectPropertyID,
  1178. kAudioUnitScope_Global,
  1179. 0,
  1180. pointers,
  1181. &propertySize);
  1182. juceFilter = (AudioProcessor*) pointers[0];
  1183. }
  1184. if (juceFilter != nullptr)
  1185. {
  1186. deleteUI();
  1187. if (AudioProcessorEditor* editorComp = juceFilter->createEditorIfNeeded())
  1188. {
  1189. editorComp->setOpaque (true);
  1190. windowComp = new ComponentInHIView (editorComp, mCarbonPane);
  1191. }
  1192. }
  1193. else
  1194. {
  1195. jassertfalse; // can't get a pointer to our effect
  1196. }
  1197. }
  1198. return noErr;
  1199. }
  1200. AudioUnitCarbonViewEventListener getEventListener() const { return mEventListener; }
  1201. void* getEventListenerUserData() const { return mEventListenerUserData; }
  1202. private:
  1203. //==============================================================================
  1204. AudioProcessor* juceFilter;
  1205. ScopedPointer<Component> windowComp;
  1206. FakeMouseMoveGenerator fakeMouseGenerator;
  1207. void deleteUI()
  1208. {
  1209. if (windowComp != nullptr)
  1210. {
  1211. PopupMenu::dismissAllActiveMenus();
  1212. /* This assertion is triggered when there's some kind of modal component active, and the
  1213. host is trying to delete our plugin.
  1214. If you must use modal components, always use them in a non-blocking way, by never
  1215. calling runModalLoop(), but instead using enterModalState() with a callback that
  1216. will be performed on completion. (Note that this assertion could actually trigger
  1217. a false alarm even if you're doing it correctly, but is here to catch people who
  1218. aren't so careful) */
  1219. jassert (Component::getCurrentlyModalComponent() == nullptr);
  1220. if (JuceAU::EditorCompHolder* editorCompHolder = dynamic_cast<JuceAU::EditorCompHolder*> (windowComp->getChildComponent(0)))
  1221. if (AudioProcessorEditor* audioProcessEditor = dynamic_cast<AudioProcessorEditor*> (editorCompHolder->getChildComponent(0)))
  1222. juceFilter->editorBeingDeleted (audioProcessEditor);
  1223. windowComp = nullptr;
  1224. }
  1225. }
  1226. //==============================================================================
  1227. // Uses a child NSWindow to sit in front of a HIView and display our component
  1228. class ComponentInHIView : public Component
  1229. {
  1230. public:
  1231. ComponentInHIView (AudioProcessorEditor* ed, HIViewRef parentHIView)
  1232. : parentView (parentHIView),
  1233. editor (ed),
  1234. recursive (false)
  1235. {
  1236. JUCE_AUTORELEASEPOOL
  1237. {
  1238. jassert (ed != nullptr);
  1239. addAndMakeVisible (editor);
  1240. setOpaque (true);
  1241. setVisible (true);
  1242. setBroughtToFrontOnMouseClick (true);
  1243. setSize (editor.getWidth(), editor.getHeight());
  1244. SizeControl (parentHIView, (SInt16) editor.getWidth(), (SInt16) editor.getHeight());
  1245. WindowRef windowRef = HIViewGetWindow (parentHIView);
  1246. hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];
  1247. [hostWindow retain];
  1248. [hostWindow setCanHide: YES];
  1249. [hostWindow setReleasedWhenClosed: YES];
  1250. updateWindowPos();
  1251. #if ! JucePlugin_EditorRequiresKeyboardFocus
  1252. addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  1253. setWantsKeyboardFocus (false);
  1254. #else
  1255. addToDesktop (ComponentPeer::windowIsTemporary);
  1256. setWantsKeyboardFocus (true);
  1257. #endif
  1258. setVisible (true);
  1259. toFront (false);
  1260. addSubWindow();
  1261. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1262. [pluginWindow setNextResponder: hostWindow];
  1263. attachWindowHidingHooks (this, (WindowRef) windowRef, hostWindow);
  1264. }
  1265. }
  1266. ~ComponentInHIView()
  1267. {
  1268. JUCE_AUTORELEASEPOOL
  1269. {
  1270. removeWindowHidingHooks (this);
  1271. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1272. [hostWindow removeChildWindow: pluginWindow];
  1273. removeFromDesktop();
  1274. [hostWindow release];
  1275. hostWindow = nil;
  1276. }
  1277. }
  1278. void updateWindowPos()
  1279. {
  1280. HIPoint f;
  1281. f.x = f.y = 0;
  1282. HIPointConvert (&f, kHICoordSpaceView, parentView, kHICoordSpaceScreenPixel, 0);
  1283. setTopLeftPosition ((int) f.x, (int) f.y);
  1284. }
  1285. void addSubWindow()
  1286. {
  1287. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1288. [pluginWindow setExcludedFromWindowsMenu: YES];
  1289. [pluginWindow setCanHide: YES];
  1290. [hostWindow addChildWindow: pluginWindow
  1291. ordered: NSWindowAbove];
  1292. [hostWindow orderFront: nil];
  1293. [pluginWindow orderFront: nil];
  1294. }
  1295. void resized() override
  1296. {
  1297. if (Component* const child = getChildComponent (0))
  1298. child->setBounds (getLocalBounds());
  1299. }
  1300. void paint (Graphics&) override {}
  1301. void childBoundsChanged (Component*) override
  1302. {
  1303. if (! recursive)
  1304. {
  1305. recursive = true;
  1306. const int w = jmax (32, editor.getWidth());
  1307. const int h = jmax (32, editor.getHeight());
  1308. SizeControl (parentView, (SInt16) w, (SInt16) h);
  1309. if (getWidth() != w || getHeight() != h)
  1310. setSize (w, h);
  1311. editor.repaint();
  1312. updateWindowPos();
  1313. addSubWindow(); // (need this for AULab)
  1314. recursive = false;
  1315. }
  1316. }
  1317. bool keyPressed (const KeyPress& kp) override
  1318. {
  1319. if (! kp.getModifiers().isCommandDown())
  1320. {
  1321. // If we have an unused keypress, move the key-focus to a host window
  1322. // and re-inject the event..
  1323. static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
  1324. NSTimeInterval eventTime = [[NSApp currentEvent] timestamp];
  1325. if (lastEventTime != eventTime)
  1326. {
  1327. lastEventTime = eventTime;
  1328. [[hostWindow parentWindow] makeKeyWindow];
  1329. repostCurrentNSEvent();
  1330. }
  1331. }
  1332. return false;
  1333. }
  1334. private:
  1335. HIViewRef parentView;
  1336. NSWindow* hostWindow;
  1337. JuceAU::EditorCompHolder editor;
  1338. bool recursive;
  1339. };
  1340. };
  1341. #endif
  1342. //==============================================================================
  1343. #define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \
  1344. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \
  1345. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \
  1346. { \
  1347. return ComponentEntryPoint<Class>::Dispatch (params, obj); \
  1348. }
  1349. #if JucePlugin_ProducesMidiOutput || JucePlugin_WantsMidiInput
  1350. #define FACTORY_BASE_CLASS AUMIDIEffectFactory
  1351. #else
  1352. #define FACTORY_BASE_CLASS AUBaseFactory
  1353. #endif
  1354. #define JUCE_FACTORY_ENTRYX(Class, Name) \
  1355. extern "C" __attribute__((visibility("default"))) void* Name ## Factory (const AudioComponentDescription* desc); \
  1356. extern "C" __attribute__((visibility("default"))) void* Name ## Factory (const AudioComponentDescription* desc) \
  1357. { \
  1358. return FACTORY_BASE_CLASS<Class>::Factory (desc); \
  1359. }
  1360. #define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)
  1361. #define JUCE_FACTORY_ENTRY(Class, Name) JUCE_FACTORY_ENTRYX(Class, Name)
  1362. //==============================================================================
  1363. JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)
  1364. #ifndef AUDIOCOMPONENT_ENTRY
  1365. #define JUCE_DISABLE_AU_FACTORY_ENTRY 1
  1366. #endif
  1367. #if ! JUCE_DISABLE_AU_FACTORY_ENTRY // (You might need to disable this for old Xcode 3 builds)
  1368. JUCE_FACTORY_ENTRY (JuceAU, JucePlugin_AUExportPrefix)
  1369. #endif
  1370. #if BUILD_AU_CARBON_UI
  1371. JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)
  1372. #endif
  1373. #if ! JUCE_DISABLE_AU_FACTORY_ENTRY
  1374. #include "CoreAudioUtilityClasses/AUPlugInDispatch.cpp"
  1375. #endif
  1376. #endif