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.

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