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.

1378 lines
47KB

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