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.

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