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.

1410 lines
49KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. // Your project must contain an AppConfig.h file with your project-specific settings in it,
  19. // and your header search path must make it accessible to the module's files.
  20. #include "AppConfig.h"
  21. #include "../utility/juce_CheckSettingMacros.h"
  22. #if JucePlugin_Build_AU
  23. #if __LP64__
  24. #undef JUCE_SUPPORT_CARBON
  25. #define JUCE_SUPPORT_CARBON 0
  26. #endif
  27. #include "../utility/juce_IncludeSystemHeaders.h"
  28. #include <AudioUnit/AUCocoaUIView.h>
  29. #include <AudioUnit/AudioUnit.h>
  30. #include <AudioToolbox/AudioUnitUtilities.h>
  31. #if JUCE_SUPPORT_CARBON
  32. #define Point CarbonDummyPointName
  33. #define Component CarbonDummyCompName
  34. #endif
  35. #include "AUMIDIEffectBase.h"
  36. #include "MusicDeviceBase.h"
  37. #undef Point
  38. #undef Component
  39. /** The BUILD_AU_CARBON_UI flag lets you specify whether old-school carbon hosts are supported as
  40. well as ones that can open a cocoa view. If this is enabled, you'll need to also add the AUCarbonBase
  41. files to your project.
  42. */
  43. #if ! (defined (BUILD_AU_CARBON_UI) || JUCE_64BIT)
  44. #define BUILD_AU_CARBON_UI 1
  45. #endif
  46. #ifdef __LP64__
  47. #undef BUILD_AU_CARBON_UI // (not possible in a 64-bit build)
  48. #endif
  49. #if BUILD_AU_CARBON_UI
  50. #undef Button
  51. #define Point CarbonDummyPointName
  52. #include "AUCarbonViewBase.h"
  53. #undef Point
  54. class JuceAUView;
  55. #endif
  56. #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1
  57. #include "../utility/juce_IncludeModuleHeaders.h"
  58. #include "../utility/juce_FakeMouseMoveGenerator.h"
  59. #include "../utility/juce_CarbonVisibility.h"
  60. #include "../utility/juce_PluginHostType.h"
  61. #include "../../juce_core/native/juce_osx_ObjCHelpers.h"
  62. //==============================================================================
  63. #define JuceUICreationClass JucePlugin_AUCocoaViewClassName
  64. #define juceFilterObjectPropertyID 0x1a45ffe9
  65. static Array<void*> activePlugins, activeUIs;
  66. static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  67. static const int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs);
  68. #if JucePlugin_IsSynth
  69. #define JuceAUBaseClass MusicDeviceBase
  70. #else
  71. #define JuceAUBaseClass AUMIDIEffectBase
  72. #endif
  73. // This macro can be set if you need to override this internal name for some reason..
  74. #ifndef JUCE_STATE_DICTIONARY_KEY
  75. #define JUCE_STATE_DICTIONARY_KEY CFSTR("jucePluginState")
  76. #endif
  77. //==============================================================================
  78. /** Somewhere in the codebase of your plugin, you need to implement this function
  79. and make it create an instance of the filter subclass that you're building.
  80. */
  81. extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
  82. //==============================================================================
  83. class JuceAU : public JuceAUBaseClass,
  84. public AudioProcessorListener,
  85. public AudioPlayHead,
  86. public ComponentListener
  87. {
  88. public:
  89. //==============================================================================
  90. JuceAU (AudioUnit component)
  91. #if JucePlugin_IsSynth
  92. : MusicDeviceBase (component, 0, 1),
  93. #else
  94. : AUMIDIEffectBase (component),
  95. #endif
  96. bufferSpace (2, 16),
  97. prepared (false)
  98. {
  99. if (activePlugins.size() + activeUIs.size() == 0)
  100. {
  101. #if BUILD_AU_CARBON_UI
  102. NSApplicationLoad();
  103. #endif
  104. initialiseJuce_GUI();
  105. }
  106. juceFilter = createPluginFilter();
  107. jassert (juceFilter != nullptr);
  108. juceFilter->setPlayHead (this);
  109. juceFilter->addListener (this);
  110. Globals()->UseIndexedParameters (juceFilter->getNumParameters());
  111. activePlugins.add (this);
  112. zerostruct (auEvent);
  113. auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance();
  114. auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  115. auEvent.mArgument.mParameter.mElement = 0;
  116. CreateElements();
  117. CAStreamBasicDescription streamDescription;
  118. streamDescription.mSampleRate = GetSampleRate();
  119. streamDescription.SetCanonical (channelConfigs[0][1], false);
  120. Outputs().GetIOElement(0)->SetStreamFormat (streamDescription);
  121. #if ! JucePlugin_IsSynth
  122. streamDescription.SetCanonical (channelConfigs[0][0], false);
  123. Inputs().GetIOElement(0)->SetStreamFormat (streamDescription);
  124. #endif
  125. }
  126. ~JuceAU()
  127. {
  128. deleteActiveEditors();
  129. juceFilter = nullptr;
  130. jassert (activePlugins.contains (this));
  131. activePlugins.removeValue (this);
  132. if (activePlugins.size() + activeUIs.size() == 0)
  133. shutdownJuce_GUI();
  134. }
  135. void deleteActiveEditors();
  136. //==============================================================================
  137. ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,
  138. AudioUnitScope inScope,
  139. AudioUnitElement inElement,
  140. UInt32& outDataSize,
  141. Boolean& outWritable)
  142. {
  143. if (inScope == kAudioUnitScope_Global)
  144. {
  145. if (inID == juceFilterObjectPropertyID)
  146. {
  147. outWritable = false;
  148. outDataSize = sizeof (void*) * 2;
  149. return noErr;
  150. }
  151. else if (inID == kAudioUnitProperty_OfflineRender)
  152. {
  153. outWritable = true;
  154. outDataSize = sizeof (UInt32);
  155. return noErr;
  156. }
  157. else if (inID == kMusicDeviceProperty_InstrumentCount)
  158. {
  159. outDataSize = sizeof (UInt32);
  160. outWritable = false;
  161. return noErr;
  162. }
  163. else if (inID == kAudioUnitProperty_CocoaUI)
  164. {
  165. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  166. // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)
  167. if (SystemStats::getOSXMinorVersionNumber() > 4)
  168. #endif
  169. {
  170. outDataSize = sizeof (AudioUnitCocoaViewInfo);
  171. outWritable = true;
  172. return noErr;
  173. }
  174. }
  175. }
  176. return JuceAUBaseClass::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
  177. }
  178. ComponentResult GetProperty (AudioUnitPropertyID inID,
  179. AudioUnitScope inScope,
  180. AudioUnitElement inElement,
  181. void* outData)
  182. {
  183. if (inScope == kAudioUnitScope_Global)
  184. {
  185. if (inID == juceFilterObjectPropertyID)
  186. {
  187. ((void**) outData)[0] = (void*) static_cast <AudioProcessor*> (juceFilter);
  188. ((void**) outData)[1] = (void*) this;
  189. return noErr;
  190. }
  191. else if (inID == kAudioUnitProperty_OfflineRender)
  192. {
  193. *(UInt32*) outData = (juceFilter != nullptr && juceFilter->isNonRealtime()) ? 1 : 0;
  194. return noErr;
  195. }
  196. else if (inID == kMusicDeviceProperty_InstrumentCount)
  197. {
  198. *(UInt32*) outData = 1;
  199. return noErr;
  200. }
  201. else if (inID == kAudioUnitProperty_CocoaUI)
  202. {
  203. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  204. // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)
  205. if (SystemStats::getOSXMinorVersionNumber() > 4)
  206. #endif
  207. {
  208. JUCE_AUTORELEASEPOOL
  209. AudioUnitCocoaViewInfo* info = (AudioUnitCocoaViewInfo*) outData;
  210. const File bundleFile (File::getSpecialLocation (File::currentApplicationFile));
  211. NSString* bundlePath = [NSString stringWithUTF8String: (const char*) bundleFile.getFullPathName().toUTF8()];
  212. NSBundle* b = [NSBundle bundleWithPath: bundlePath];
  213. info->mCocoaAUViewClass[0] = (CFStringRef) [nsStringLiteral (JUCE_STRINGIFY (JuceUICreationClass)) retain];
  214. info->mCocoaAUViewBundleLocation = (CFURLRef) [[NSURL fileURLWithPath: [b bundlePath]] retain];
  215. return noErr;
  216. }
  217. }
  218. }
  219. return JuceAUBaseClass::GetProperty (inID, inScope, inElement, outData);
  220. }
  221. ComponentResult SetProperty (AudioUnitPropertyID inID,
  222. AudioUnitScope inScope,
  223. AudioUnitElement inElement,
  224. const void* inData,
  225. UInt32 inDataSize)
  226. {
  227. if (inScope == kAudioUnitScope_Global && inID == kAudioUnitProperty_OfflineRender)
  228. {
  229. if (juceFilter != nullptr)
  230. juceFilter->setNonRealtime ((*(UInt32*) inData) != 0);
  231. return noErr;
  232. }
  233. return JuceAUBaseClass::SetProperty (inID, inScope, inElement, inData, inDataSize);
  234. }
  235. ComponentResult SaveState (CFPropertyListRef* outData)
  236. {
  237. ComponentResult err = JuceAUBaseClass::SaveState (outData);
  238. if (err != noErr)
  239. return err;
  240. jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID());
  241. CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData;
  242. if (juceFilter != nullptr)
  243. {
  244. juce::MemoryBlock state;
  245. juceFilter->getCurrentProgramStateInformation (state);
  246. if (state.getSize() > 0)
  247. {
  248. CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const UInt8*) state.getData(), state.getSize());
  249. CFDictionarySetValue (dict, JUCE_STATE_DICTIONARY_KEY, ourState);
  250. CFRelease (ourState);
  251. }
  252. }
  253. return noErr;
  254. }
  255. ComponentResult RestoreState (CFPropertyListRef inData)
  256. {
  257. ComponentResult err = JuceAUBaseClass::RestoreState (inData);
  258. if (err != noErr)
  259. return err;
  260. if (juceFilter != nullptr)
  261. {
  262. CFDictionaryRef dict = (CFDictionaryRef) inData;
  263. CFDataRef data = 0;
  264. if (CFDictionaryGetValueIfPresent (dict, JUCE_STATE_DICTIONARY_KEY, (const void**) &data))
  265. {
  266. if (data != 0)
  267. {
  268. const int numBytes = (int) CFDataGetLength (data);
  269. const juce::uint8* const rawBytes = CFDataGetBytePtr (data);
  270. if (numBytes > 0)
  271. juceFilter->setCurrentProgramStateInformation (rawBytes, numBytes);
  272. }
  273. }
  274. }
  275. return noErr;
  276. }
  277. UInt32 SupportedNumChannels (const AUChannelInfo** outInfo)
  278. {
  279. // If you hit this, then you need to add some configurations to your
  280. // JucePlugin_PreferredChannelConfigurations setting..
  281. jassert (numChannelConfigs > 0);
  282. if (outInfo != nullptr)
  283. {
  284. *outInfo = channelInfo;
  285. for (int i = 0; i < numChannelConfigs; ++i)
  286. {
  287. #if JucePlugin_IsSynth
  288. channelInfo[i].inChannels = 0;
  289. #else
  290. channelInfo[i].inChannels = channelConfigs[i][0];
  291. #endif
  292. channelInfo[i].outChannels = channelConfigs[i][1];
  293. }
  294. }
  295. return numChannelConfigs;
  296. }
  297. //==============================================================================
  298. ComponentResult GetParameterInfo (AudioUnitScope inScope,
  299. AudioUnitParameterID inParameterID,
  300. AudioUnitParameterInfo& outParameterInfo)
  301. {
  302. const int index = (int) inParameterID;
  303. if (inScope == kAudioUnitScope_Global
  304. && juceFilter != nullptr
  305. && index < juceFilter->getNumParameters())
  306. {
  307. outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable
  308. | kAudioUnitParameterFlag_IsReadable
  309. | kAudioUnitParameterFlag_HasCFNameString;
  310. const String name (juceFilter->getParameterName (index));
  311. // set whether the param is automatable (unnamed parameters aren't allowed to be automated)
  312. if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))
  313. outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
  314. if (juceFilter->isMetaParameter (index))
  315. outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;
  316. AUBase::FillInParameterName (outParameterInfo, name.toCFString(), false);
  317. outParameterInfo.minValue = 0.0f;
  318. outParameterInfo.maxValue = 1.0f;
  319. outParameterInfo.defaultValue = 0.0f;
  320. outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
  321. return noErr;
  322. }
  323. else
  324. {
  325. return kAudioUnitErr_InvalidParameter;
  326. }
  327. }
  328. ComponentResult GetParameter (AudioUnitParameterID inID,
  329. AudioUnitScope inScope,
  330. AudioUnitElement inElement,
  331. Float32& outValue)
  332. {
  333. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  334. {
  335. outValue = juceFilter->getParameter ((int) inID);
  336. return noErr;
  337. }
  338. return AUBase::GetParameter (inID, inScope, inElement, outValue);
  339. }
  340. ComponentResult SetParameter (AudioUnitParameterID inID,
  341. AudioUnitScope inScope,
  342. AudioUnitElement inElement,
  343. Float32 inValue,
  344. UInt32 inBufferOffsetInFrames)
  345. {
  346. if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)
  347. {
  348. juceFilter->setParameter ((int) inID, inValue);
  349. return noErr;
  350. }
  351. return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames);
  352. }
  353. //==============================================================================
  354. ComponentResult Version() { return JucePlugin_VersionCode; }
  355. bool SupportsTail() { return true; }
  356. Float64 GetTailTime() { return (JucePlugin_TailLengthSeconds); }
  357. Float64 GetSampleRate() { return GetOutput(0)->GetStreamFormat().mSampleRate; }
  358. Float64 GetLatency()
  359. {
  360. jassert (GetSampleRate() > 0);
  361. if (GetSampleRate() <= 0)
  362. return 0.0;
  363. return juceFilter->getLatencySamples() / GetSampleRate();
  364. }
  365. //==============================================================================
  366. #if BUILD_AU_CARBON_UI
  367. int GetNumCustomUIComponents()
  368. {
  369. return PluginHostType().isDigitalPerformer() ? 0 : 1;
  370. }
  371. void GetUIComponentDescs (ComponentDescription* inDescArray)
  372. {
  373. inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;
  374. inDescArray[0].componentSubType = JucePlugin_AUSubType;
  375. inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode;
  376. inDescArray[0].componentFlags = 0;
  377. inDescArray[0].componentFlagsMask = 0;
  378. }
  379. #endif
  380. //==============================================================================
  381. bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)
  382. {
  383. info.timeSigNumerator = 0;
  384. info.timeSigDenominator = 0;
  385. info.timeInSeconds = 0;
  386. info.editOriginTime = 0;
  387. info.ppqPositionOfLastBarStart = 0;
  388. info.isPlaying = false;
  389. info.isRecording = false;
  390. info.isLooping = false;
  391. info.ppqLoopStart = 0;
  392. info.ppqLoopEnd = 0;
  393. switch (lastSMPTETime.mType)
  394. {
  395. case kSMPTETimeType24: info.frameRate = AudioPlayHead::fps24; break;
  396. case kSMPTETimeType25: info.frameRate = AudioPlayHead::fps25; break;
  397. case kSMPTETimeType30Drop: info.frameRate = AudioPlayHead::fps30drop; break;
  398. case kSMPTETimeType30: info.frameRate = AudioPlayHead::fps30; break;
  399. case kSMPTETimeType2997: info.frameRate = AudioPlayHead::fps2997; break;
  400. case kSMPTETimeType2997Drop: info.frameRate = AudioPlayHead::fps2997drop; break;
  401. //case kSMPTETimeType60:
  402. //case kSMPTETimeType5994:
  403. default: info.frameRate = AudioPlayHead::fpsUnknown; break;
  404. }
  405. if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)
  406. {
  407. info.ppqPosition = 0;
  408. info.bpm = 0;
  409. }
  410. UInt32 outDeltaSampleOffsetToNextBeat;
  411. double outCurrentMeasureDownBeat;
  412. float num;
  413. UInt32 den;
  414. if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,
  415. &outCurrentMeasureDownBeat) == noErr)
  416. {
  417. info.timeSigNumerator = (int) num;
  418. info.timeSigDenominator = den;
  419. info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
  420. }
  421. double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat;
  422. Boolean playing, playchanged, looping;
  423. if (CallHostTransportState (&playing,
  424. &playchanged,
  425. &outCurrentSampleInTimeLine,
  426. &looping,
  427. &outCycleStartBeat,
  428. &outCycleEndBeat) == noErr)
  429. {
  430. info.isPlaying = playing;
  431. info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate();
  432. }
  433. return true;
  434. }
  435. void sendAUEvent (const AudioUnitEventType type, const int index)
  436. {
  437. if (AUEventListenerNotify != 0)
  438. {
  439. auEvent.mEventType = type;
  440. auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index;
  441. AUEventListenerNotify (0, 0, &auEvent);
  442. }
  443. }
  444. void audioProcessorParameterChanged (AudioProcessor*, int index, float /*newValue*/)
  445. {
  446. sendAUEvent (kAudioUnitEvent_ParameterValueChange, index);
  447. }
  448. void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index)
  449. {
  450. sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index);
  451. }
  452. void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index)
  453. {
  454. sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
  455. }
  456. void audioProcessorChanged (AudioProcessor*)
  457. {
  458. // xxx is there an AU equivalent?
  459. }
  460. bool StreamFormatWritable (AudioUnitScope, AudioUnitElement)
  461. {
  462. return ! IsInitialized();
  463. }
  464. // (these two slightly different versions are because the definition changed between 10.4 and 10.5)
  465. ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID&, UInt32, const MusicDeviceNoteParams&) { return noErr; }
  466. ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID*, UInt32, const MusicDeviceNoteParams&) { return noErr; }
  467. ComponentResult StopNote (MusicDeviceGroupID, NoteInstanceID, UInt32) { return noErr; }
  468. //==============================================================================
  469. ComponentResult Initialize()
  470. {
  471. #if ! JucePlugin_IsSynth
  472. const int numIns = GetInput(0) != 0 ? GetInput(0)->GetStreamFormat().mChannelsPerFrame : 0;
  473. #endif
  474. const int numOuts = GetOutput(0) != 0 ? GetOutput(0)->GetStreamFormat().mChannelsPerFrame : 0;
  475. bool isValidChannelConfig = false;
  476. for (int i = 0; i < numChannelConfigs; ++i)
  477. #if JucePlugin_IsSynth
  478. if (numOuts == channelConfigs[i][1])
  479. #else
  480. if (numIns == channelConfigs[i][0] && numOuts == channelConfigs[i][1])
  481. #endif
  482. isValidChannelConfig = true;
  483. if (! isValidChannelConfig)
  484. return kAudioUnitErr_FormatNotSupported;
  485. JuceAUBaseClass::Initialize();
  486. prepareToPlay();
  487. return noErr;
  488. }
  489. void Cleanup()
  490. {
  491. JuceAUBaseClass::Cleanup();
  492. if (juceFilter != nullptr)
  493. juceFilter->releaseResources();
  494. bufferSpace.setSize (2, 16);
  495. midiEvents.clear();
  496. incomingEvents.clear();
  497. prepared = false;
  498. }
  499. ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement)
  500. {
  501. if (! prepared)
  502. prepareToPlay();
  503. if (juceFilter != nullptr)
  504. juceFilter->reset();
  505. return JuceAUBaseClass::Reset (inScope, inElement);
  506. }
  507. void prepareToPlay()
  508. {
  509. if (juceFilter != nullptr)
  510. {
  511. juceFilter->setPlayConfigDetails (
  512. #if ! JucePlugin_IsSynth
  513. GetInput(0)->GetStreamFormat().mChannelsPerFrame,
  514. #else
  515. 0,
  516. #endif
  517. GetOutput(0)->GetStreamFormat().mChannelsPerFrame,
  518. GetSampleRate(),
  519. GetMaxFramesPerSlice());
  520. bufferSpace.setSize (juceFilter->getNumInputChannels() + juceFilter->getNumOutputChannels(),
  521. GetMaxFramesPerSlice() + 32);
  522. juceFilter->prepareToPlay (GetSampleRate(), GetMaxFramesPerSlice());
  523. midiEvents.ensureSize (2048);
  524. midiEvents.clear();
  525. incomingEvents.ensureSize (2048);
  526. incomingEvents.clear();
  527. channels.calloc (jmax (juceFilter->getNumInputChannels(),
  528. juceFilter->getNumOutputChannels()) + 4);
  529. prepared = true;
  530. }
  531. }
  532. ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags,
  533. const AudioTimeStamp& inTimeStamp,
  534. UInt32 nFrames)
  535. {
  536. lastSMPTETime = inTimeStamp.mSMPTETime;
  537. #if ! JucePlugin_IsSynth
  538. return JuceAUBaseClass::Render (ioActionFlags, inTimeStamp, nFrames);
  539. #else
  540. // synths can't have any inputs..
  541. AudioBufferList inBuffer;
  542. inBuffer.mNumberBuffers = 0;
  543. return ProcessBufferLists (ioActionFlags, inBuffer, GetOutput(0)->GetBufferList(), nFrames);
  544. #endif
  545. }
  546. OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags,
  547. const AudioBufferList& inBuffer,
  548. AudioBufferList& outBuffer,
  549. UInt32 numSamples)
  550. {
  551. if (juceFilter != nullptr)
  552. {
  553. jassert (prepared);
  554. int numOutChans = 0;
  555. int nextSpareBufferChan = 0;
  556. bool needToReinterleave = false;
  557. const int numIn = juceFilter->getNumInputChannels();
  558. const int numOut = juceFilter->getNumOutputChannels();
  559. unsigned int i;
  560. for (i = 0; i < outBuffer.mNumberBuffers; ++i)
  561. {
  562. AudioBuffer& buf = outBuffer.mBuffers[i];
  563. if (buf.mNumberChannels == 1)
  564. {
  565. channels [numOutChans++] = (float*) buf.mData;
  566. }
  567. else
  568. {
  569. needToReinterleave = true;
  570. for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numOutChans < numOut; ++subChan)
  571. channels [numOutChans++] = bufferSpace.getSampleData (nextSpareBufferChan++);
  572. }
  573. if (numOutChans >= numOut)
  574. break;
  575. }
  576. int numInChans = 0;
  577. for (i = 0; i < inBuffer.mNumberBuffers; ++i)
  578. {
  579. const AudioBuffer& buf = inBuffer.mBuffers[i];
  580. if (buf.mNumberChannels == 1)
  581. {
  582. if (numInChans < numOutChans)
  583. memcpy (channels [numInChans], (const float*) buf.mData, sizeof (float) * numSamples);
  584. else
  585. channels [numInChans] = (float*) buf.mData;
  586. ++numInChans;
  587. }
  588. else
  589. {
  590. // need to de-interleave..
  591. for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numInChans < numIn; ++subChan)
  592. {
  593. float* dest;
  594. if (numInChans < numOutChans)
  595. {
  596. dest = channels [numInChans++];
  597. }
  598. else
  599. {
  600. dest = bufferSpace.getSampleData (nextSpareBufferChan++);
  601. channels [numInChans++] = dest;
  602. }
  603. const float* src = ((const float*) buf.mData) + subChan;
  604. for (int j = numSamples; --j >= 0;)
  605. {
  606. *dest++ = *src;
  607. src += buf.mNumberChannels;
  608. }
  609. }
  610. }
  611. if (numInChans >= numIn)
  612. break;
  613. }
  614. {
  615. const ScopedLock sl (incomingMidiLock);
  616. midiEvents.clear();
  617. incomingEvents.swapWith (midiEvents);
  618. }
  619. {
  620. AudioSampleBuffer buffer (channels, jmax (numIn, numOut), numSamples);
  621. const ScopedLock sl (juceFilter->getCallbackLock());
  622. if (juceFilter->isSuspended())
  623. {
  624. for (int i = 0; i < numOut; ++i)
  625. zeromem (channels [i], sizeof (float) * numSamples);
  626. }
  627. else
  628. {
  629. juceFilter->processBlock (buffer, midiEvents);
  630. }
  631. }
  632. if (! midiEvents.isEmpty())
  633. {
  634. #if JucePlugin_ProducesMidiOutput
  635. const juce::uint8* midiEventData;
  636. int midiEventSize, midiEventPosition;
  637. MidiBuffer::Iterator i (midiEvents);
  638. while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
  639. {
  640. jassert (isPositiveAndBelow (midiEventPosition, (int) numSamples));
  641. //xxx
  642. }
  643. #else
  644. // if your plugin creates midi messages, you'll need to set
  645. // the JucePlugin_ProducesMidiOutput macro to 1 in your
  646. // JucePluginCharacteristics.h file
  647. //jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
  648. #endif
  649. midiEvents.clear();
  650. }
  651. if (needToReinterleave)
  652. {
  653. nextSpareBufferChan = 0;
  654. for (i = 0; i < outBuffer.mNumberBuffers; ++i)
  655. {
  656. AudioBuffer& buf = outBuffer.mBuffers[i];
  657. if (buf.mNumberChannels > 1)
  658. {
  659. for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan)
  660. {
  661. const float* src = bufferSpace.getSampleData (nextSpareBufferChan++);
  662. float* dest = ((float*) buf.mData) + subChan;
  663. for (int j = numSamples; --j >= 0;)
  664. {
  665. *dest = *src++;
  666. dest += buf.mNumberChannels;
  667. }
  668. }
  669. }
  670. }
  671. }
  672. #if ! JucePlugin_SilenceInProducesSilenceOut
  673. ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
  674. #endif
  675. }
  676. return noErr;
  677. }
  678. protected:
  679. OSStatus HandleMidiEvent (UInt8 nStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2,
  680. #if defined (MAC_OS_X_VERSION_10_5)
  681. UInt32 inStartFrame)
  682. #else
  683. long inStartFrame)
  684. #endif
  685. {
  686. #if JucePlugin_WantsMidiInput
  687. const ScopedLock sl (incomingMidiLock);
  688. const juce::uint8 data[] = { (juce::uint8) (nStatus | inChannel),
  689. (juce::uint8) inData1,
  690. (juce::uint8) inData2 };
  691. incomingEvents.addEvent (data, 3, inStartFrame);
  692. #endif
  693. return noErr;
  694. }
  695. OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength)
  696. {
  697. #if JucePlugin_WantsMidiInput
  698. const ScopedLock sl (incomingMidiLock);
  699. incomingEvents.addEvent (inData, inLength, 0);
  700. #endif
  701. return noErr;
  702. }
  703. //==============================================================================
  704. ComponentResult GetPresets (CFArrayRef* outData) const
  705. {
  706. if (outData != nullptr)
  707. {
  708. const int numPrograms = juceFilter->getNumPrograms();
  709. presetsArray.ensureSize (sizeof (AUPreset) * numPrograms, true);
  710. AUPreset* const presets = (AUPreset*) presetsArray.getData();
  711. CFMutableArrayRef presetsArray = CFArrayCreateMutable (0, numPrograms, 0);
  712. for (int i = 0; i < numPrograms; ++i)
  713. {
  714. presets[i].presetNumber = i;
  715. presets[i].presetName = juceFilter->getProgramName(i).toCFString();
  716. CFArrayAppendValue (presetsArray, presets + i);
  717. }
  718. *outData = (CFArrayRef) presetsArray;
  719. }
  720. return noErr;
  721. }
  722. OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset)
  723. {
  724. const int numPrograms = juceFilter->getNumPrograms();
  725. const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;
  726. if (chosenPresetNumber >= numPrograms)
  727. return kAudioUnitErr_InvalidProperty;
  728. AUPreset chosenPreset;
  729. chosenPreset.presetNumber = chosenPresetNumber;
  730. chosenPreset.presetName = juceFilter->getProgramName (chosenPresetNumber).toCFString();
  731. juceFilter->setCurrentProgram (chosenPresetNumber);
  732. SetAFactoryPresetAsCurrent (chosenPreset);
  733. return noErr;
  734. }
  735. void componentMovedOrResized (Component& component, bool /*wasMoved*/, bool /*wasResized*/)
  736. {
  737. NSView* view = (NSView*) component.getWindowHandle();
  738. NSRect r = [[view superview] frame];
  739. r.origin.y = r.origin.y + r.size.height - component.getHeight();
  740. r.size.width = component.getWidth();
  741. r.size.height = component.getHeight();
  742. [[view superview] setFrame: r];
  743. [view setFrame: NSMakeRect (0, 0, component.getWidth(), component.getHeight())];
  744. [view setNeedsDisplay: YES];
  745. }
  746. private:
  747. //==============================================================================
  748. ScopedPointer<AudioProcessor> juceFilter;
  749. AudioSampleBuffer bufferSpace;
  750. HeapBlock <float*> channels;
  751. MidiBuffer midiEvents, incomingEvents;
  752. bool prepared;
  753. SMPTETime lastSMPTETime;
  754. AUChannelInfo channelInfo [numChannelConfigs];
  755. AudioUnitEvent auEvent;
  756. mutable juce::MemoryBlock presetsArray;
  757. CriticalSection incomingMidiLock;
  758. JUCE_DECLARE_NON_COPYABLE (JuceAU);
  759. };
  760. //==============================================================================
  761. class EditorCompHolder : public Component
  762. {
  763. public:
  764. EditorCompHolder (AudioProcessorEditor* const editor)
  765. {
  766. setSize (editor->getWidth(), editor->getHeight());
  767. addAndMakeVisible (editor);
  768. #if ! JucePlugin_EditorRequiresKeyboardFocus
  769. setWantsKeyboardFocus (false);
  770. #else
  771. setWantsKeyboardFocus (true);
  772. #endif
  773. }
  774. ~EditorCompHolder()
  775. {
  776. deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may
  777. // have been transferred to another parent which takes over ownership.
  778. }
  779. static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
  780. {
  781. EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
  782. NSRect r = NSMakeRect (0, 0, editorCompHolder->getWidth(), editorCompHolder->getHeight());
  783. static JuceUIViewClass cls;
  784. NSView* view = [[cls.createInstance() initWithFrame: r] autorelease];
  785. JuceUIViewClass::setFilter (view, filter);
  786. JuceUIViewClass::setAU (view, au);
  787. JuceUIViewClass::setEditor (view, editorCompHolder);
  788. [view setHidden: NO];
  789. [view setPostsFrameChangedNotifications: YES];
  790. [[NSNotificationCenter defaultCenter] addObserver: view
  791. selector: @selector (applicationWillTerminate:)
  792. name: NSApplicationWillTerminateNotification
  793. object: nil];
  794. activeUIs.add (view);
  795. editorCompHolder->addToDesktop (0, (void*) view);
  796. editorCompHolder->setVisible (view);
  797. return view;
  798. }
  799. void childBoundsChanged (Component*)
  800. {
  801. Component* editor = getChildComponent(0);
  802. if (editor != nullptr)
  803. {
  804. const int w = jmax (32, editor->getWidth());
  805. const int h = jmax (32, editor->getHeight());
  806. if (getWidth() != w || getHeight() != h)
  807. setSize (w, h);
  808. NSView* view = (NSView*) getWindowHandle();
  809. NSRect r = [[view superview] frame];
  810. r.size.width = editor->getWidth();
  811. r.size.height = editor->getHeight();
  812. [[view superview] setFrame: r];
  813. [view setFrame: NSMakeRect (0, 0, editor->getWidth(), editor->getHeight())];
  814. [view setNeedsDisplay: YES];
  815. }
  816. }
  817. //==============================================================================
  818. struct JuceUIViewClass : public ObjCClass <NSView>
  819. {
  820. JuceUIViewClass() : ObjCClass <NSView> ("JUCEAUView_")
  821. {
  822. addIvar<AudioProcessor*> ("filter");
  823. addIvar<JuceAU*> ("au");
  824. addIvar<EditorCompHolder*> ("editor");
  825. addMethod (@selector (dealloc), dealloc, "v@:");
  826. addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@");
  827. addMethod (@selector (viewDidMoveToWindow), viewDidMoveToWindow, "v@:");
  828. addMethod (@selector (mouseDownCanMoveWindow), mouseDownCanMoveWindow, "c@:");
  829. registerClass();
  830. }
  831. static void deleteEditor (id self)
  832. {
  833. ScopedPointer<EditorCompHolder> editorComp (getEditor (self));
  834. if (editorComp != nullptr)
  835. {
  836. JuceAU* const au = getAU (self);
  837. if (editorComp->getChildComponent(0) != nullptr)
  838. {
  839. if (activePlugins.contains (au)) // plugin may have been deleted before the UI
  840. {
  841. AudioProcessor* const filter = getIvar<AudioProcessor*> (self, "filter");
  842. filter->editorBeingDeleted ((AudioProcessorEditor*) editorComp->getChildComponent(0));
  843. }
  844. }
  845. editorComp = nullptr;
  846. setEditor (self, nullptr);
  847. }
  848. }
  849. static JuceAU* getAU (id self) { return getIvar<JuceAU*> (self, "au"); }
  850. static EditorCompHolder* getEditor (id self) { return getIvar<EditorCompHolder*> (self, "editor"); }
  851. static void setFilter (id self, AudioProcessor* filter) { object_setInstanceVariable (self, "filter", filter); }
  852. static void setAU (id self, JuceAU* au) { object_setInstanceVariable (self, "au", au); }
  853. static void setEditor (id self, EditorCompHolder* e) { object_setInstanceVariable (self, "editor", e); }
  854. private:
  855. static void dealloc (id self, SEL)
  856. {
  857. if (activeUIs.contains (self))
  858. shutdown (self);
  859. sendSuperclassMessage (self, @selector (dealloc));
  860. }
  861. static void applicationWillTerminate (id self, SEL, NSNotification*)
  862. {
  863. shutdown (self);
  864. }
  865. static void shutdown (id self)
  866. {
  867. // there's some kind of component currently modal, but the host
  868. // is trying to delete our plugin..
  869. jassert (Component::getCurrentlyModalComponent() == nullptr);
  870. [[NSNotificationCenter defaultCenter] removeObserver: self];
  871. deleteEditor (self);
  872. jassert (activeUIs.contains (self));
  873. activeUIs.removeValue (self);
  874. if (activePlugins.size() + activeUIs.size() == 0)
  875. shutdownJuce_GUI();
  876. }
  877. static void viewDidMoveToWindow (id self, SEL)
  878. {
  879. NSWindow* w = [(NSView*) self window];
  880. if (w != nil)
  881. {
  882. [w setAcceptsMouseMovedEvents: YES];
  883. EditorCompHolder* const editorComp = getEditor (self);
  884. if (editorComp != nullptr)
  885. [w makeFirstResponder: (NSView*) editorComp->getWindowHandle()];
  886. }
  887. }
  888. static BOOL mouseDownCanMoveWindow (id self, SEL)
  889. {
  890. return NO;
  891. }
  892. };
  893. private:
  894. JUCE_DECLARE_NON_COPYABLE (EditorCompHolder);
  895. };
  896. void JuceAU::deleteActiveEditors()
  897. {
  898. for (int i = activeUIs.size(); --i >= 0;)
  899. {
  900. id ui = (id) activeUIs.getUnchecked(i);
  901. if (EditorCompHolder::JuceUIViewClass::getAU (ui) == this)
  902. EditorCompHolder::JuceUIViewClass::deleteEditor (ui);
  903. }
  904. }
  905. //==============================================================================
  906. @interface JuceUICreationClass : NSObject <AUCocoaUIBase>
  907. {
  908. }
  909. - (unsigned) interfaceVersion;
  910. - (NSString*) description;
  911. - (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit
  912. withSize: (NSSize) inPreferredSize;
  913. @end
  914. @implementation JuceUICreationClass
  915. - (unsigned) interfaceVersion
  916. {
  917. return 0;
  918. }
  919. - (NSString*) description
  920. {
  921. return [NSString stringWithString: nsStringLiteral (JucePlugin_Name)];
  922. }
  923. - (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit
  924. withSize: (NSSize) inPreferredSize
  925. {
  926. void* pointers[2];
  927. UInt32 propertySize = sizeof (pointers);
  928. if (AudioUnitGetProperty (inAudioUnit,
  929. juceFilterObjectPropertyID,
  930. kAudioUnitScope_Global,
  931. 0,
  932. pointers,
  933. &propertySize) != noErr)
  934. return nil;
  935. AudioProcessor* filter = (AudioProcessor*) pointers[0];
  936. JuceAU* au = (JuceAU*) pointers[1];
  937. if (filter == nullptr)
  938. return nil;
  939. AudioProcessorEditor* editorComp = filter->createEditorIfNeeded();
  940. if (editorComp == nullptr)
  941. return nil;
  942. return EditorCompHolder::createViewFor (filter, au, editorComp);
  943. }
  944. @end
  945. #if BUILD_AU_CARBON_UI
  946. //==============================================================================
  947. class JuceAUView : public AUCarbonViewBase
  948. {
  949. public:
  950. JuceAUView (AudioUnitCarbonView auview)
  951. : AUCarbonViewBase (auview),
  952. juceFilter (nullptr)
  953. {
  954. }
  955. ~JuceAUView()
  956. {
  957. deleteUI();
  958. }
  959. ComponentResult CreateUI (Float32 /*inXOffset*/, Float32 /*inYOffset*/)
  960. {
  961. JUCE_AUTORELEASEPOOL
  962. if (juceFilter == nullptr)
  963. {
  964. void* pointers[2];
  965. UInt32 propertySize = sizeof (pointers);
  966. AudioUnitGetProperty (GetEditAudioUnit(),
  967. juceFilterObjectPropertyID,
  968. kAudioUnitScope_Global,
  969. 0,
  970. pointers,
  971. &propertySize);
  972. juceFilter = (AudioProcessor*) pointers[0];
  973. }
  974. if (juceFilter != nullptr)
  975. {
  976. deleteUI();
  977. AudioProcessorEditor* editorComp = juceFilter->createEditorIfNeeded();
  978. editorComp->setOpaque (true);
  979. windowComp = new ComponentInHIView (editorComp, mCarbonPane);
  980. }
  981. else
  982. {
  983. jassertfalse // can't get a pointer to our effect
  984. }
  985. return noErr;
  986. }
  987. AudioUnitCarbonViewEventListener getEventListener() const { return mEventListener; }
  988. void* getEventListenerUserData() const { return mEventListenerUserData; }
  989. private:
  990. //==============================================================================
  991. AudioProcessor* juceFilter;
  992. ScopedPointer<Component> windowComp;
  993. FakeMouseMoveGenerator fakeMouseGenerator;
  994. void deleteUI()
  995. {
  996. if (windowComp != nullptr)
  997. {
  998. PopupMenu::dismissAllActiveMenus();
  999. /* This assertion is triggered when there's some kind of modal component active, and the
  1000. host is trying to delete our plugin.
  1001. If you must use modal components, always use them in a non-blocking way, by never
  1002. calling runModalLoop(), but instead using enterModalState() with a callback that
  1003. will be performed on completion. (Note that this assertion could actually trigger
  1004. a false alarm even if you're doing it correctly, but is here to catch people who
  1005. aren't so careful) */
  1006. jassert (Component::getCurrentlyModalComponent() == nullptr);
  1007. EditorCompHolder* editorCompHolder = dynamic_cast <EditorCompHolder*> (windowComp->getChildComponent(0));
  1008. if (editorCompHolder != nullptr)
  1009. {
  1010. AudioProcessorEditor* audioProcessEditor = dynamic_cast <AudioProcessorEditor*> (editorCompHolder->getChildComponent(0));
  1011. if (audioProcessEditor != nullptr)
  1012. juceFilter->editorBeingDeleted (audioProcessEditor);
  1013. }
  1014. windowComp = nullptr;
  1015. }
  1016. }
  1017. //==============================================================================
  1018. // Uses a child NSWindow to sit in front of a HIView and display our component
  1019. class ComponentInHIView : public Component
  1020. {
  1021. public:
  1022. ComponentInHIView (AudioProcessorEditor* const editor_, HIViewRef parentHIView)
  1023. : parentView (parentHIView),
  1024. editor (editor_),
  1025. recursive (false)
  1026. {
  1027. JUCE_AUTORELEASEPOOL
  1028. jassert (editor_ != nullptr);
  1029. addAndMakeVisible (&editor);
  1030. setOpaque (true);
  1031. setVisible (true);
  1032. setBroughtToFrontOnMouseClick (true);
  1033. setSize (editor.getWidth(), editor.getHeight());
  1034. SizeControl (parentHIView, editor.getWidth(), editor.getHeight());
  1035. WindowRef windowRef = HIViewGetWindow (parentHIView);
  1036. hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];
  1037. [hostWindow retain];
  1038. [hostWindow setCanHide: YES];
  1039. [hostWindow setReleasedWhenClosed: YES];
  1040. updateWindowPos();
  1041. #if ! JucePlugin_EditorRequiresKeyboardFocus
  1042. addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);
  1043. setWantsKeyboardFocus (false);
  1044. #else
  1045. addToDesktop (ComponentPeer::windowIsTemporary);
  1046. setWantsKeyboardFocus (true);
  1047. #endif
  1048. setVisible (true);
  1049. toFront (false);
  1050. addSubWindow();
  1051. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1052. [pluginWindow setNextResponder: hostWindow];
  1053. attachWindowHidingHooks (this, (WindowRef) windowRef, hostWindow);
  1054. }
  1055. ~ComponentInHIView()
  1056. {
  1057. JUCE_AUTORELEASEPOOL
  1058. removeWindowHidingHooks (this);
  1059. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1060. [hostWindow removeChildWindow: pluginWindow];
  1061. removeFromDesktop();
  1062. [hostWindow release];
  1063. hostWindow = nil;
  1064. }
  1065. void updateWindowPos()
  1066. {
  1067. HIPoint f;
  1068. f.x = f.y = 0;
  1069. HIPointConvert (&f, kHICoordSpaceView, parentView, kHICoordSpaceScreenPixel, 0);
  1070. setTopLeftPosition ((int) f.x, (int) f.y);
  1071. }
  1072. void addSubWindow()
  1073. {
  1074. NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];
  1075. [pluginWindow setExcludedFromWindowsMenu: YES];
  1076. [pluginWindow setCanHide: YES];
  1077. [hostWindow addChildWindow: pluginWindow
  1078. ordered: NSWindowAbove];
  1079. [hostWindow orderFront: nil];
  1080. [pluginWindow orderFront: nil];
  1081. }
  1082. void resized()
  1083. {
  1084. Component* const child = getChildComponent (0);
  1085. if (child != nullptr)
  1086. child->setBounds (getLocalBounds());
  1087. }
  1088. void paint (Graphics&) {}
  1089. void childBoundsChanged (Component*)
  1090. {
  1091. if (! recursive)
  1092. {
  1093. recursive = true;
  1094. const int w = jmax (32, editor.getWidth());
  1095. const int h = jmax (32, editor.getHeight());
  1096. SizeControl (parentView, w, h);
  1097. if (getWidth() != w || getHeight() != h)
  1098. setSize (w, h);
  1099. editor.repaint();
  1100. updateWindowPos();
  1101. addSubWindow(); // (need this for AULab)
  1102. recursive = false;
  1103. }
  1104. }
  1105. bool keyPressed (const KeyPress& kp)
  1106. {
  1107. if (! kp.getModifiers().isCommandDown())
  1108. {
  1109. // If we have an unused keypress, move the key-focus to a host window
  1110. // and re-inject the event..
  1111. static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event
  1112. if (lastEventTime != [[NSApp currentEvent] timestamp])
  1113. {
  1114. lastEventTime = [[NSApp currentEvent] timestamp];
  1115. [[hostWindow parentWindow] makeKeyWindow];
  1116. [NSApp postEvent: [NSApp currentEvent] atStart: YES];
  1117. }
  1118. }
  1119. return false;
  1120. }
  1121. private:
  1122. HIViewRef parentView;
  1123. NSWindow* hostWindow;
  1124. EditorCompHolder editor;
  1125. bool recursive;
  1126. };
  1127. };
  1128. #endif
  1129. //==============================================================================
  1130. #define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \
  1131. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \
  1132. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \
  1133. { \
  1134. return ComponentEntryPoint<Class>::Dispatch(params, obj); \
  1135. }
  1136. #define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)
  1137. JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)
  1138. #if BUILD_AU_CARBON_UI
  1139. JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)
  1140. #endif
  1141. #endif