Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / src / audio / plugin_client / AU / juce_AU_Wrapper.mm
blobf1467d94e8d6b3599b5d39c781138a7d5fa4d42c
1 /*\r
2   ==============================================================================\r
3 \r
4    This file is part of the JUCE library - "Jules' Utility Class Extensions"\r
5    Copyright 2004-11 by Raw Material Software Ltd.\r
6 \r
7   ------------------------------------------------------------------------------\r
8 \r
9    JUCE can be redistributed and/or modified under the terms of the GNU General\r
10    Public License (Version 2), as published by the Free Software Foundation.\r
11    A copy of the license is included in the JUCE distribution, or can be found\r
12    online at www.gnu.org/licenses.\r
14    JUCE is distributed in the hope that it will be useful, but WITHOUT ANY\r
15    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r
16    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\r
18   ------------------------------------------------------------------------------\r
20    To release a closed-source product which uses JUCE, commercial licenses are\r
21    available: visit www.rawmaterialsoftware.com/juce for more information.\r
23   ==============================================================================\r
24 */\r
26 #include <Cocoa/Cocoa.h>\r
27 #include <AudioUnit/AUCocoaUIView.h>\r
28 #include <AudioUnit/AudioUnit.h>\r
29 #include <AudioToolbox/AudioUnitUtilities.h>\r
30 #include "AUMIDIEffectBase.h"\r
31 #include "MusicDeviceBase.h"\r
32 #include "../juce_IncludeCharacteristics.h"\r
34 #if JucePlugin_Build_AU\r
36 /** The BUILD_AU_CARBON_UI flag lets you specify whether old-school carbon hosts are supported as\r
37     well as ones that can open a cocoa view. If this is enabled, you'll need to also add the AUCarbonBase\r
38     files to your project.\r
39 */\r
40 #ifndef BUILD_AU_CARBON_UI\r
41  #define BUILD_AU_CARBON_UI 1\r
42 #endif\r
44 #ifdef __LP64__\r
45  #undef BUILD_AU_CARBON_UI  // (not possible in a 64-bit build)\r
46 #endif\r
48 #if BUILD_AU_CARBON_UI\r
49  #undef Button\r
50  #include "AUCarbonViewBase.h"\r
51  class JuceAUView;\r
52 #endif\r
54 #define JUCE_MAC_WINDOW_VISIBITY_BODGE 1\r
56 #include "../juce_PluginHeaders.h"\r
57 #include "../juce_PluginHostType.h"\r
59 //==============================================================================\r
60 #define juceFilterObjectPropertyID 0x1a45ffe9\r
61 static Array<void*> activePlugins, activeUIs;\r
63 static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };\r
64 static const int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs);\r
66 #if JucePlugin_IsSynth\r
67  #define JuceAUBaseClass MusicDeviceBase\r
68 #else\r
69  #define JuceAUBaseClass AUMIDIEffectBase\r
70 #endif\r
72 //==============================================================================\r
73 /** Somewhere in the codebase of your plugin, you need to implement this function\r
74     and make it create an instance of the filter subclass that you're building.\r
75 */\r
76 extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();\r
78 //==============================================================================\r
79 #define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d\r
80 #define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d)\r
81 #define MakeObjCClassName(rootName)  appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JucePlugin_AUExportPrefix)\r
83 #define JuceUICreationClass         JucePlugin_AUCocoaViewClassName\r
84 #define JuceUIViewClass             MakeObjCClassName(JuceUIViewClass)\r
86 class JuceAU;\r
87 class EditorCompHolder;\r
89 //==============================================================================\r
90 @interface JuceUICreationClass   : NSObject <AUCocoaUIBase>\r
91 {\r
92 }\r
94 - (JuceUICreationClass*) init;\r
95 - (void) dealloc;\r
96 - (unsigned) interfaceVersion;\r
97 - (NSString *) description;\r
98 - (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit\r
99                       withSize: (NSSize) inPreferredSize;\r
100 @end\r
102 //==============================================================================\r
103 @interface JuceUIViewClass : NSView\r
105     AudioProcessor* filter;\r
106     JuceAU* au;\r
107     EditorCompHolder* editorComp;\r
110 - (JuceUIViewClass*) initWithFilter: (AudioProcessor*) filter\r
111                              withAU: (JuceAU*) au\r
112                       withComponent: (AudioProcessorEditor*) editorComp;\r
113 - (void) dealloc;\r
114 - (void) shutdown;\r
115 - (void) applicationWillTerminate: (NSNotification*) aNotification;\r
116 - (void) viewDidMoveToWindow;\r
117 - (BOOL) mouseDownCanMoveWindow;\r
118 - (void) filterBeingDeleted: (JuceAU*) au_;\r
119 - (void) deleteEditor;\r
121 @end\r
124 //==============================================================================\r
125 class JuceAU   : public JuceAUBaseClass,\r
126                  public AudioProcessorListener,\r
127                  public AudioPlayHead,\r
128                  public ComponentListener\r
130 public:\r
131     //==============================================================================\r
132     JuceAU (AudioUnit component)\r
133       #if JucePlugin_IsSynth\r
134         : MusicDeviceBase (component, 0, 1),\r
135       #else\r
136         : AUMIDIEffectBase (component),\r
137       #endif\r
138           bufferSpace (2, 16),\r
139           prepared (false)\r
140     {\r
141         if (activePlugins.size() + activeUIs.size() == 0)\r
142         {\r
143           #if BUILD_AU_CARBON_UI\r
144             NSApplicationLoad();\r
145           #endif\r
147             initialiseJuce_GUI();\r
148         }\r
150         juceFilter = createPluginFilter();\r
151         jassert (juceFilter != nullptr);\r
153         juceFilter->setPlayHead (this);\r
154         juceFilter->addListener (this);\r
156         Globals()->UseIndexedParameters (juceFilter->getNumParameters());\r
158         activePlugins.add (this);\r
160         zerostruct (auEvent);\r
161         auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance();\r
162         auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;\r
163         auEvent.mArgument.mParameter.mElement = 0;\r
164     }\r
166     ~JuceAU()\r
167     {\r
168         for (int i = activeUIs.size(); --i >= 0;)\r
169             [((JuceUIViewClass*) activeUIs.getUnchecked(i)) filterBeingDeleted: this];\r
171         juceFilter = nullptr;\r
173         jassert (activePlugins.contains (this));\r
174         activePlugins.removeValue (this);\r
176         if (activePlugins.size() + activeUIs.size() == 0)\r
177             shutdownJuce_GUI();\r
178     }\r
180     //==============================================================================\r
181     ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,\r
182                                      AudioUnitScope inScope,\r
183                                      AudioUnitElement inElement,\r
184                                      UInt32& outDataSize,\r
185                                      Boolean& outWritable)\r
186     {\r
187         if (inScope == kAudioUnitScope_Global)\r
188         {\r
189             if (inID == juceFilterObjectPropertyID)\r
190             {\r
191                 outWritable = false;\r
192                 outDataSize = sizeof (void*) * 2;\r
193                 return noErr;\r
194             }\r
195             else if (inID == kAudioUnitProperty_OfflineRender)\r
196             {\r
197                 outWritable = true;\r
198                 outDataSize = sizeof (UInt32);\r
199                 return noErr;\r
200             }\r
201             else if (inID == kMusicDeviceProperty_InstrumentCount)\r
202             {\r
203                 outDataSize = sizeof (UInt32);\r
204                 outWritable = false;\r
205                 return noErr;\r
206             }\r
207             else if (inID == kAudioUnitProperty_CocoaUI)\r
208             {\r
209               #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
210                 // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)\r
211                 if (PlatformUtilities::getOSXMinorVersionNumber() > 4)\r
212               #endif\r
213                 {\r
214                     outDataSize = sizeof (AudioUnitCocoaViewInfo);\r
215                     outWritable = true;\r
216                     return noErr;\r
217                 }\r
218             }\r
219         }\r
221         return JuceAUBaseClass::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);\r
222     }\r
224     ComponentResult GetProperty (AudioUnitPropertyID inID,\r
225                                  AudioUnitScope inScope,\r
226                                  AudioUnitElement inElement,\r
227                                  void* outData)\r
228     {\r
229         if (inScope == kAudioUnitScope_Global)\r
230         {\r
231             if (inID == juceFilterObjectPropertyID)\r
232             {\r
233                 ((void**) outData)[0] = (void*) static_cast <AudioProcessor*> (juceFilter);\r
234                 ((void**) outData)[1] = (void*) this;\r
235                 return noErr;\r
236             }\r
237             else if (inID == kAudioUnitProperty_OfflineRender)\r
238             {\r
239                 *(UInt32*) outData = (juceFilter != nullptr && juceFilter->isNonRealtime()) ? 1 : 0;\r
240                 return noErr;\r
241             }\r
242             else if (inID == kMusicDeviceProperty_InstrumentCount)\r
243             {\r
244                 *(UInt32*) outData = 1;\r
245                 return noErr;\r
246             }\r
247             else if (inID == kAudioUnitProperty_CocoaUI)\r
248             {\r
249               #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
250                 // (On 10.4, there's a random obj-c dispatching crash when trying to load a cocoa UI)\r
251                 if (PlatformUtilities::getOSXMinorVersionNumber() > 4)\r
252               #endif\r
253                 {\r
254                     JUCE_AUTORELEASEPOOL\r
256                     AudioUnitCocoaViewInfo* info = (AudioUnitCocoaViewInfo*) outData;\r
258                     const File bundleFile (File::getSpecialLocation (File::currentApplicationFile));\r
259                     NSString* bundlePath = [NSString stringWithUTF8String: (const char*) bundleFile.getFullPathName().toUTF8()];\r
260                     NSBundle* b = [NSBundle bundleWithPath: bundlePath];\r
262                     info->mCocoaAUViewClass[0] = (CFStringRef) [[[JuceUICreationClass class] className] retain];\r
263                     info->mCocoaAUViewBundleLocation = (CFURLRef) [[NSURL fileURLWithPath: [b bundlePath]] retain];\r
265                     return noErr;\r
266                 }\r
267             }\r
268         }\r
270         return JuceAUBaseClass::GetProperty (inID, inScope, inElement, outData);\r
271     }\r
273     ComponentResult SetProperty (AudioUnitPropertyID inID,\r
274                                  AudioUnitScope inScope,\r
275                                  AudioUnitElement inElement,\r
276                                  const void* inData,\r
277                                  UInt32 inDataSize)\r
278     {\r
279         if (inScope == kAudioUnitScope_Global && inID == kAudioUnitProperty_OfflineRender)\r
280         {\r
281             if (juceFilter != nullptr)\r
282                 juceFilter->setNonRealtime ((*(UInt32*) inData) != 0);\r
284             return noErr;\r
285         }\r
287         return JuceAUBaseClass::SetProperty (inID, inScope, inElement, inData, inDataSize);\r
288     }\r
290     ComponentResult SaveState (CFPropertyListRef* outData)\r
291     {\r
292         ComponentResult err = JuceAUBaseClass::SaveState (outData);\r
294         if (err != noErr)\r
295             return err;\r
297         jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID());\r
299         CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData;\r
301         if (juceFilter != nullptr)\r
302         {\r
303             MemoryBlock state;\r
304             juceFilter->getCurrentProgramStateInformation (state);\r
306             if (state.getSize() > 0)\r
307             {\r
308                 CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const UInt8*) state.getData(), state.getSize());\r
309                 CFDictionarySetValue (dict, CFSTR("jucePluginState"), ourState);\r
310                 CFRelease (ourState);\r
311             }\r
312         }\r
314         return noErr;\r
315     }\r
317     ComponentResult RestoreState (CFPropertyListRef inData)\r
318     {\r
319         ComponentResult err = JuceAUBaseClass::RestoreState (inData);\r
321         if (err != noErr)\r
322             return err;\r
324         if (juceFilter != nullptr)\r
325         {\r
326             CFDictionaryRef dict = (CFDictionaryRef) inData;\r
327             CFDataRef data = 0;\r
329             if (CFDictionaryGetValueIfPresent (dict, CFSTR("jucePluginState"), (const void**) &data))\r
330             {\r
331                 if (data != 0)\r
332                 {\r
333                     const int numBytes = (int) CFDataGetLength (data);\r
334                     const JUCE_NAMESPACE::uint8* const rawBytes = CFDataGetBytePtr (data);\r
336                     if (numBytes > 0)\r
337                         juceFilter->setCurrentProgramStateInformation (rawBytes, numBytes);\r
338                 }\r
339             }\r
340         }\r
342         return noErr;\r
343     }\r
345     UInt32 SupportedNumChannels (const AUChannelInfo** outInfo)\r
346     {\r
347         // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations\r
348         // value in your JucePluginCharacteristics.h file..\r
349         jassert (numChannelConfigs > 0);\r
351         if (outInfo != nullptr)\r
352         {\r
353             *outInfo = channelInfo;\r
355             for (int i = 0; i < numChannelConfigs; ++i)\r
356             {\r
357               #if JucePlugin_IsSynth\r
358                 channelInfo[i].inChannels = 0;\r
359               #else\r
360                 channelInfo[i].inChannels = channelConfigs[i][0];\r
361               #endif\r
362                 channelInfo[i].outChannels = channelConfigs[i][1];\r
363             }\r
364         }\r
366         return numChannelConfigs;\r
367     }\r
369     //==============================================================================\r
370     ComponentResult GetParameterInfo (AudioUnitScope inScope,\r
371                                       AudioUnitParameterID inParameterID,\r
372                                       AudioUnitParameterInfo& outParameterInfo)\r
373     {\r
374         const int index = (int) inParameterID;\r
376         if (inScope == kAudioUnitScope_Global\r
377              && juceFilter != nullptr\r
378              && index < juceFilter->getNumParameters())\r
379         {\r
380             outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable\r
381                                       | kAudioUnitParameterFlag_IsReadable\r
382                                       | kAudioUnitParameterFlag_HasCFNameString;\r
384             const String name (juceFilter->getParameterName (index));\r
386             // set whether the param is automatable (unnamed parameters aren't allowed to be automated)\r
387             if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))\r
388                 outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;\r
390             if (juceFilter->isMetaParameter (index))\r
391                 outParameterInfo.flags |= kAudioUnitParameterFlag_IsGlobalMeta;\r
393             AUBase::FillInParameterName (outParameterInfo,\r
394                                          PlatformUtilities::juceStringToCFString (name),\r
395                                          false);\r
397             outParameterInfo.minValue = 0.0f;\r
398             outParameterInfo.maxValue = 1.0f;\r
399             outParameterInfo.defaultValue = 0.0f;\r
400             outParameterInfo.unit = kAudioUnitParameterUnit_Generic;\r
402             return noErr;\r
403         }\r
404         else\r
405         {\r
406             return kAudioUnitErr_InvalidParameter;\r
407         }\r
408     }\r
410     ComponentResult GetParameter (AudioUnitParameterID inID,\r
411                                   AudioUnitScope inScope,\r
412                                   AudioUnitElement inElement,\r
413                                   Float32& outValue)\r
414     {\r
415         if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)\r
416         {\r
417             outValue = juceFilter->getParameter ((int) inID);\r
418             return noErr;\r
419         }\r
421         return AUBase::GetParameter (inID, inScope, inElement, outValue);\r
422     }\r
424     ComponentResult SetParameter (AudioUnitParameterID inID,\r
425                                   AudioUnitScope inScope,\r
426                                   AudioUnitElement inElement,\r
427                                   Float32 inValue,\r
428                                   UInt32 inBufferOffsetInFrames)\r
429     {\r
430         if (inScope == kAudioUnitScope_Global && juceFilter != nullptr)\r
431         {\r
432             juceFilter->setParameter ((int) inID, inValue);\r
433             return noErr;\r
434         }\r
436         return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames);\r
437     }\r
439     //==============================================================================\r
440     ComponentResult Version()                   { return JucePlugin_VersionCode; }\r
441     bool SupportsTail()                         { return true; }\r
442     Float64 GetTailTime()                       { return (JucePlugin_TailLengthSeconds); }\r
443     Float64 GetSampleRate()                     { return GetOutput(0)->GetStreamFormat().mSampleRate; }\r
445     Float64 GetLatency()\r
446     {\r
447         jassert (GetSampleRate() > 0);\r
449         if (GetSampleRate() <= 0)\r
450             return 0.0;\r
452         return juceFilter->getLatencySamples() / GetSampleRate();\r
453     }\r
455     //==============================================================================\r
456    #if BUILD_AU_CARBON_UI\r
457     int GetNumCustomUIComponents()              { return 1; }\r
459     void GetUIComponentDescs (ComponentDescription* inDescArray)\r
460     {\r
461         inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;\r
462         inDescArray[0].componentSubType = JucePlugin_AUSubType;\r
463         inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode;\r
464         inDescArray[0].componentFlags = 0;\r
465         inDescArray[0].componentFlagsMask = 0;\r
466     }\r
467    #endif\r
469     //==============================================================================\r
470     bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info)\r
471     {\r
472         info.timeSigNumerator = 0;\r
473         info.timeSigDenominator = 0;\r
474         info.timeInSeconds = 0;\r
475         info.editOriginTime = 0;\r
476         info.ppqPositionOfLastBarStart = 0;\r
477         info.isPlaying = false;\r
478         info.isRecording = false;\r
480         switch (lastSMPTETime.mType)\r
481         {\r
482             case kSMPTETimeType24:          info.frameRate = AudioPlayHead::fps24; break;\r
483             case kSMPTETimeType25:          info.frameRate = AudioPlayHead::fps25; break;\r
484             case kSMPTETimeType30Drop:      info.frameRate = AudioPlayHead::fps30drop; break;\r
485             case kSMPTETimeType30:          info.frameRate = AudioPlayHead::fps30; break;\r
486             case kSMPTETimeType2997:        info.frameRate = AudioPlayHead::fps2997; break;\r
487             case kSMPTETimeType2997Drop:    info.frameRate = AudioPlayHead::fps2997drop; break;\r
488             //case kSMPTETimeType60:\r
489             //case kSMPTETimeType5994:\r
490             default:                        info.frameRate = AudioPlayHead::fpsUnknown; break;\r
491         }\r
493         if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)\r
494         {\r
495             info.ppqPosition = 0;\r
496             info.bpm = 0;\r
497         }\r
499         UInt32 outDeltaSampleOffsetToNextBeat;\r
500         double outCurrentMeasureDownBeat;\r
501         float num;\r
502         UInt32 den;\r
504         if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,\r
505                                          &outCurrentMeasureDownBeat) == noErr)\r
506         {\r
507             info.timeSigNumerator = (int) num;\r
508             info.timeSigDenominator = den;\r
509             info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;\r
510         }\r
512         double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat;\r
513         Boolean playing, playchanged, looping;\r
515         if (CallHostTransportState (&playing,\r
516                                     &playchanged,\r
517                                     &outCurrentSampleInTimeLine,\r
518                                     &looping,\r
519                                     &outCycleStartBeat,\r
520                                     &outCycleEndBeat) == noErr)\r
521         {\r
522             info.isPlaying = playing;\r
523             info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate();\r
524         }\r
526         return true;\r
527     }\r
529     void sendAUEvent (const AudioUnitEventType type, const int index)\r
530     {\r
531         if (AUEventListenerNotify != 0)\r
532         {\r
533             auEvent.mEventType = type;\r
534             auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index;\r
535             AUEventListenerNotify (0, 0, &auEvent);\r
536         }\r
537     }\r
539     void audioProcessorParameterChanged (AudioProcessor*, int index, float /*newValue*/)\r
540     {\r
541         sendAUEvent (kAudioUnitEvent_ParameterValueChange, index);\r
542     }\r
544     void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int index)\r
545     {\r
546         sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index);\r
547     }\r
549     void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int index)\r
550     {\r
551         sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);\r
552     }\r
554     void audioProcessorChanged (AudioProcessor*)\r
555     {\r
556         // xxx is there an AU equivalent?\r
557     }\r
559     bool StreamFormatWritable (AudioUnitScope, AudioUnitElement)\r
560     {\r
561         return ! IsInitialized();\r
562     }\r
564     // (these two slightly different versions are because the definition changed between 10.4 and 10.5)\r
565     ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID&, UInt32, const MusicDeviceNoteParams&) { return noErr; }\r
566     ComponentResult StartNote (MusicDeviceInstrumentID, MusicDeviceGroupID, NoteInstanceID*, UInt32, const MusicDeviceNoteParams&) { return noErr; }\r
567     ComponentResult StopNote (MusicDeviceGroupID, NoteInstanceID, UInt32)   { return noErr; }\r
569     //==============================================================================\r
570     ComponentResult Initialize()\r
571     {\r
572        #if ! JucePlugin_IsSynth\r
573         const int numIns = GetInput(0) != 0 ? GetInput(0)->GetStreamFormat().mChannelsPerFrame : 0;\r
574        #endif\r
575         const int numOuts = GetOutput(0) != 0 ? GetOutput(0)->GetStreamFormat().mChannelsPerFrame : 0;\r
577         bool isValidChannelConfig = false;\r
579         for (int i = 0; i < numChannelConfigs; ++i)\r
580           #if JucePlugin_IsSynth\r
581             if (numOuts == channelConfigs[i][1])\r
582           #else\r
583             if (numIns == channelConfigs[i][0] && numOuts == channelConfigs[i][1])\r
584           #endif\r
585                 isValidChannelConfig = true;\r
587         if (! isValidChannelConfig)\r
588             return kAudioUnitErr_FormatNotSupported;\r
590         JuceAUBaseClass::Initialize();\r
591         prepareToPlay();\r
592         return noErr;\r
593     }\r
595     void Cleanup()\r
596     {\r
597         JuceAUBaseClass::Cleanup();\r
599         if (juceFilter != nullptr)\r
600             juceFilter->releaseResources();\r
602         bufferSpace.setSize (2, 16);\r
603         midiEvents.clear();\r
604         incomingEvents.clear();\r
605         prepared = false;\r
606     }\r
608     ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement)\r
609     {\r
610         if (! prepared)\r
611             prepareToPlay();\r
613         if (juceFilter != nullptr)\r
614             juceFilter->reset();\r
616         return JuceAUBaseClass::Reset (inScope, inElement);\r
617     }\r
619     void prepareToPlay()\r
620     {\r
621         if (juceFilter != nullptr)\r
622         {\r
623             juceFilter->setPlayConfigDetails (\r
624                  #if ! JucePlugin_IsSynth\r
625                   GetInput(0)->GetStreamFormat().mChannelsPerFrame,\r
626                  #else\r
627                   0,\r
628                  #endif\r
629                   GetOutput(0)->GetStreamFormat().mChannelsPerFrame,\r
630                   GetSampleRate(),\r
631                   GetMaxFramesPerSlice());\r
633             bufferSpace.setSize (juceFilter->getNumInputChannels() + juceFilter->getNumOutputChannels(),\r
634                                  GetMaxFramesPerSlice() + 32);\r
636             juceFilter->prepareToPlay (GetSampleRate(), GetMaxFramesPerSlice());\r
638             midiEvents.ensureSize (2048);\r
639             midiEvents.clear();\r
640             incomingEvents.ensureSize (2048);\r
641             incomingEvents.clear();\r
643             channels.calloc (jmax (juceFilter->getNumInputChannels(),\r
644                                    juceFilter->getNumOutputChannels()) + 4);\r
646             prepared = true;\r
647         }\r
648     }\r
650     ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags,\r
651                             const AudioTimeStamp& inTimeStamp,\r
652                             UInt32 nFrames)\r
653     {\r
654         lastSMPTETime = inTimeStamp.mSMPTETime;\r
656        #if ! JucePlugin_IsSynth\r
657         return JuceAUBaseClass::Render (ioActionFlags, inTimeStamp, nFrames);\r
658        #else\r
659         // synths can't have any inputs..\r
660         AudioBufferList inBuffer;\r
661         inBuffer.mNumberBuffers = 0;\r
663         return ProcessBufferLists (ioActionFlags, inBuffer, GetOutput(0)->GetBufferList(), nFrames);\r
664        #endif\r
665     }\r
667     OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags,\r
668                                  const AudioBufferList& inBuffer,\r
669                                  AudioBufferList& outBuffer,\r
670                                  UInt32 numSamples)\r
671     {\r
672         if (juceFilter != nullptr)\r
673         {\r
674             jassert (prepared);\r
676             int numOutChans = 0;\r
677             int nextSpareBufferChan = 0;\r
678             bool needToReinterleave = false;\r
679             const int numIn = juceFilter->getNumInputChannels();\r
680             const int numOut = juceFilter->getNumOutputChannels();\r
682             unsigned int i;\r
683             for (i = 0; i < outBuffer.mNumberBuffers; ++i)\r
684             {\r
685                 AudioBuffer& buf = outBuffer.mBuffers[i];\r
687                 if (buf.mNumberChannels == 1)\r
688                 {\r
689                     channels [numOutChans++] = (float*) buf.mData;\r
690                 }\r
691                 else\r
692                 {\r
693                     needToReinterleave = true;\r
695                     for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numOutChans < numOut; ++subChan)\r
696                         channels [numOutChans++] = bufferSpace.getSampleData (nextSpareBufferChan++);\r
697                 }\r
699                 if (numOutChans >= numOut)\r
700                     break;\r
701             }\r
703             int numInChans = 0;\r
705             for (i = 0; i < inBuffer.mNumberBuffers; ++i)\r
706             {\r
707                 const AudioBuffer& buf = inBuffer.mBuffers[i];\r
709                 if (buf.mNumberChannels == 1)\r
710                 {\r
711                     if (numInChans < numOutChans)\r
712                         memcpy (channels [numInChans], (const float*) buf.mData, sizeof (float) * numSamples);\r
713                     else\r
714                         channels [numInChans] = (float*) buf.mData;\r
716                     ++numInChans;\r
717                 }\r
718                 else\r
719                 {\r
720                     // need to de-interleave..\r
721                     for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numInChans < numIn; ++subChan)\r
722                     {\r
723                         float* dest;\r
725                         if (numInChans < numOutChans)\r
726                         {\r
727                             dest = channels [numInChans++];\r
728                         }\r
729                         else\r
730                         {\r
731                             dest = bufferSpace.getSampleData (nextSpareBufferChan++);\r
732                             channels [numInChans++] = dest;\r
733                         }\r
735                         const float* src = ((const float*) buf.mData) + subChan;\r
737                         for (int j = numSamples; --j >= 0;)\r
738                         {\r
739                             *dest++ = *src;\r
740                             src += buf.mNumberChannels;\r
741                         }\r
742                     }\r
743                 }\r
745                 if (numInChans >= numIn)\r
746                     break;\r
747             }\r
749             {\r
750                 const ScopedLock sl (incomingMidiLock);\r
751                 midiEvents.clear();\r
752                 incomingEvents.swapWith (midiEvents);\r
753             }\r
755             {\r
756                 AudioSampleBuffer buffer (channels, jmax (numIn, numOut), numSamples);\r
758                 const ScopedLock sl (juceFilter->getCallbackLock());\r
760                 if (juceFilter->isSuspended())\r
761                 {\r
762                     for (int i = 0; i < numOut; ++i)\r
763                         zeromem (channels [i], sizeof (float) * numSamples);\r
764                 }\r
765                 else\r
766                 {\r
767                     juceFilter->processBlock (buffer, midiEvents);\r
768                 }\r
769             }\r
771             if (! midiEvents.isEmpty())\r
772             {\r
773                #if JucePlugin_ProducesMidiOutput\r
774                 const JUCE_NAMESPACE::uint8* midiEventData;\r
775                 int midiEventSize, midiEventPosition;\r
776                 MidiBuffer::Iterator i (midiEvents);\r
778                 while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))\r
779                 {\r
780                     jassert (isPositiveAndBelow (midiEventPosition, (int) numSamples));\r
784                     //xxx\r
785                 }\r
786                #else\r
787                 // if your plugin creates midi messages, you'll need to set\r
788                 // the JucePlugin_ProducesMidiOutput macro to 1 in your\r
789                 // JucePluginCharacteristics.h file\r
790                 //jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);\r
791                #endif\r
793                 midiEvents.clear();\r
794             }\r
796             if (needToReinterleave)\r
797             {\r
798                 nextSpareBufferChan = 0;\r
800                 for (i = 0; i < outBuffer.mNumberBuffers; ++i)\r
801                 {\r
802                     AudioBuffer& buf = outBuffer.mBuffers[i];\r
804                     if (buf.mNumberChannels > 1)\r
805                     {\r
806                         for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan)\r
807                         {\r
808                             const float* src = bufferSpace.getSampleData (nextSpareBufferChan++);\r
809                             float* dest = ((float*) buf.mData) + subChan;\r
811                             for (int j = numSamples; --j >= 0;)\r
812                             {\r
813                                 *dest = *src++;\r
814                                 dest += buf.mNumberChannels;\r
815                             }\r
816                         }\r
817                     }\r
818                 }\r
819             }\r
821            #if ! JucePlugin_SilenceInProducesSilenceOut\r
822             ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;\r
823            #endif\r
824         }\r
826         return noErr;\r
827     }\r
829 protected:\r
830     OSStatus HandleMidiEvent (UInt8 nStatus, UInt8 inChannel, UInt8 inData1, UInt8 inData2,\r
831                              #if defined (MAC_OS_X_VERSION_10_5)\r
832                               UInt32 inStartFrame)\r
833                              #else\r
834                               long inStartFrame)\r
835                              #endif\r
836     {\r
837        #if JucePlugin_WantsMidiInput\r
838         const ScopedLock sl (incomingMidiLock);\r
839         const JUCE_NAMESPACE::uint8 data[] = { (JUCE_NAMESPACE::uint8) (nStatus | inChannel),\r
840                                                (JUCE_NAMESPACE::uint8) inData1,\r
841                                                (JUCE_NAMESPACE::uint8) inData2 };\r
843         incomingEvents.addEvent (data, 3, inStartFrame);\r
844        #endif\r
846         return noErr;\r
847     }\r
849     OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength)\r
850     {\r
851        #if JucePlugin_WantsMidiInput\r
852         const ScopedLock sl (incomingMidiLock);\r
853         incomingEvents.addEvent (inData, inLength, 0);\r
854        #endif\r
855         return noErr;\r
856     }\r
858     //==============================================================================\r
859     ComponentResult GetPresets (CFArrayRef* outData) const\r
860     {\r
861         if (outData != nullptr)\r
862         {\r
863             const int numPrograms = juceFilter->getNumPrograms();\r
864             presetsArray.ensureSize (sizeof (AUPreset) * numPrograms, true);\r
865             AUPreset* const presets = (AUPreset*) presetsArray.getData();\r
867             CFMutableArrayRef presetsArray = CFArrayCreateMutable (0, numPrograms, 0);\r
869             for (int i = 0; i < numPrograms; ++i)\r
870             {\r
871                 presets[i].presetNumber = i;\r
872                 presets[i].presetName = PlatformUtilities::juceStringToCFString (juceFilter->getProgramName (i));\r
874                 CFArrayAppendValue (presetsArray, presets + i);\r
875             }\r
877             *outData = (CFArrayRef) presetsArray;\r
878         }\r
880         return noErr;\r
881     }\r
883     OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset)\r
884     {\r
885         const int numPrograms = juceFilter->getNumPrograms();\r
886         const SInt32 chosenPresetNumber = (int) inNewFactoryPreset.presetNumber;\r
888         if (chosenPresetNumber >= numPrograms)\r
889             return kAudioUnitErr_InvalidProperty;\r
891         AUPreset chosenPreset;\r
892         chosenPreset.presetNumber = chosenPresetNumber;\r
893         chosenPreset.presetName = PlatformUtilities::juceStringToCFString (juceFilter->getProgramName (chosenPresetNumber));\r
895         juceFilter->setCurrentProgram (chosenPresetNumber);\r
896         SetAFactoryPresetAsCurrent (chosenPreset);\r
898         return noErr;\r
899     }\r
901     void componentMovedOrResized (Component& component, bool /*wasMoved*/, bool /*wasResized*/)\r
902     {\r
903         NSView* view = (NSView*) component.getWindowHandle();\r
904         NSRect r = [[view superview] frame];\r
905         r.origin.y = r.origin.y + r.size.height - component.getHeight();\r
906         r.size.width = component.getWidth();\r
907         r.size.height = component.getHeight();\r
908         [[view superview] setFrame: r];\r
909         [view setFrame: NSMakeRect (0, 0, component.getWidth(), component.getHeight())];\r
910         [view setNeedsDisplay: YES];\r
911     }\r
913 private:\r
914     //==============================================================================\r
915     ScopedPointer<AudioProcessor> juceFilter;\r
916     AudioSampleBuffer bufferSpace;\r
917     HeapBlock <float*> channels;\r
918     MidiBuffer midiEvents, incomingEvents;\r
919     bool prepared;\r
920     SMPTETime lastSMPTETime;\r
921     AUChannelInfo channelInfo [numChannelConfigs];\r
922     AudioUnitEvent auEvent;\r
923     mutable MemoryBlock presetsArray;\r
924     CriticalSection incomingMidiLock;\r
926     JUCE_DECLARE_NON_COPYABLE (JuceAU);\r
927 };\r
929 //==============================================================================\r
930 class EditorCompHolder  : public Component\r
932 public:\r
933     EditorCompHolder (AudioProcessorEditor* const editor)\r
934     {\r
935         setSize (editor->getWidth(), editor->getHeight());\r
936         addAndMakeVisible (editor);\r
938        #if ! JucePlugin_EditorRequiresKeyboardFocus\r
939         setWantsKeyboardFocus (false);\r
940        #else\r
941         setWantsKeyboardFocus (true);\r
942        #endif\r
943     }\r
945     ~EditorCompHolder()\r
946     {\r
947         deleteAllChildren(); // note that we can't use a ScopedPointer because the editor may\r
948                              // have been transferred to another parent which takes over ownership.\r
949     }\r
951     void childBoundsChanged (Component*)\r
952     {\r
953         Component* editor = getChildComponent(0);\r
955         if (editor != nullptr)\r
956         {\r
957             const int w = jmax (32, editor->getWidth());\r
958             const int h = jmax (32, editor->getHeight());\r
960             if (getWidth() != w || getHeight() != h)\r
961                 setSize (w, h);\r
963             NSView* view = (NSView*) getWindowHandle();\r
964             NSRect r = [[view superview] frame];\r
965             r.size.width = editor->getWidth();\r
966             r.size.height = editor->getHeight();\r
967             [[view superview] setFrame: r];\r
968             [view setFrame: NSMakeRect (0, 0, editor->getWidth(), editor->getHeight())];\r
969             [view setNeedsDisplay: YES];\r
970         }\r
971     }\r
973 private:\r
974     JUCE_DECLARE_NON_COPYABLE (EditorCompHolder);\r
975 };\r
977 //==============================================================================\r
978 @implementation JuceUIViewClass\r
980 - (JuceUIViewClass*) initWithFilter: (AudioProcessor*) filter_\r
981                              withAU: (JuceAU*) au_\r
982                       withComponent: (AudioProcessorEditor*) editorComp_\r
984     filter = filter_;\r
985     au = au_;\r
986     editorComp = new EditorCompHolder (editorComp_);\r
988     [super initWithFrame: NSMakeRect (0, 0, editorComp_->getWidth(), editorComp_->getHeight())];\r
989     [self setHidden: NO];\r
990     [self setPostsFrameChangedNotifications: YES];\r
992     [[NSNotificationCenter defaultCenter] addObserver: self\r
993                                              selector: @selector (applicationWillTerminate:)\r
994                                                  name: NSApplicationWillTerminateNotification\r
995                                                object: nil];\r
996     activeUIs.add (self);\r
998     editorComp->addToDesktop (0, (void*) self);\r
999     editorComp->setVisible (true);\r
1001     return self;\r
1004 - (void) dealloc\r
1006     if (activeUIs.contains (self))\r
1007         [self shutdown];\r
1009     [super dealloc];\r
1012 - (void) applicationWillTerminate: (NSNotification*) aNotification\r
1014     (void) aNotification;\r
1015     [self shutdown];\r
1018 - (void) shutdown\r
1020     // there's some kind of component currently modal, but the host\r
1021     // is trying to delete our plugin..\r
1022     jassert (Component::getCurrentlyModalComponent() == nullptr);\r
1024     [[NSNotificationCenter defaultCenter] removeObserver: self];\r
1025     [self deleteEditor];\r
1027     jassert (activeUIs.contains (self));\r
1028     activeUIs.removeValue (self);\r
1029     if (activePlugins.size() + activeUIs.size() == 0)\r
1030         shutdownJuce_GUI();\r
1033 - (void) viewDidMoveToWindow\r
1035     if ([self window] != nil)\r
1036     {\r
1037         [[self window] setAcceptsMouseMovedEvents: YES];\r
1039         if (editorComp != nullptr)\r
1040             [[self window] makeFirstResponder: (NSView*) editorComp->getWindowHandle()];\r
1041     }\r
1044 - (BOOL) mouseDownCanMoveWindow\r
1046     return NO;\r
1049 - (void) deleteEditor\r
1051     if (editorComp != nullptr)\r
1052     {\r
1053         if (editorComp->getChildComponent(0) != nullptr)\r
1054             if (activePlugins.contains ((void*) au)) // plugin may have been deleted before the UI\r
1055                 filter->editorBeingDeleted ((AudioProcessorEditor*) editorComp->getChildComponent(0));\r
1057         deleteAndZero (editorComp);\r
1058     }\r
1060     editorComp = nullptr;\r
1063 - (void) filterBeingDeleted: (JuceAU*) au_\r
1065     if (au_ == au)\r
1066         [self deleteEditor];\r
1069 @end\r
1071 //==============================================================================\r
1072 @implementation JuceUICreationClass\r
1074 - (JuceUICreationClass*) init\r
1076     return [super init];\r
1079 - (void) dealloc\r
1081     [super dealloc];\r
1084 - (unsigned) interfaceVersion\r
1086     return 0;\r
1089 - (NSString*) description\r
1091     return [NSString stringWithString: @JucePlugin_Name];\r
1094 - (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit\r
1095                       withSize: (NSSize) inPreferredSize\r
1097     void* pointers[2];\r
1098     UInt32 propertySize = sizeof (pointers);\r
1100     if (AudioUnitGetProperty (inAudioUnit,\r
1101                               juceFilterObjectPropertyID,\r
1102                               kAudioUnitScope_Global,\r
1103                               0,\r
1104                               pointers,\r
1105                               &propertySize) != noErr)\r
1106         return nil;\r
1108     AudioProcessor* filter = (AudioProcessor*) pointers[0];\r
1109     JuceAU* au = (JuceAU*) pointers[1];\r
1111     if (filter == nullptr)\r
1112         return nil;\r
1114     AudioProcessorEditor* editorComp = filter->createEditorIfNeeded();\r
1116     if (editorComp == nullptr)\r
1117         return nil;\r
1119     return [[[JuceUIViewClass alloc] initWithFilter: filter\r
1120                                              withAU: au\r
1121                                       withComponent: editorComp] autorelease];\r
1123 @end\r
1126 #if BUILD_AU_CARBON_UI\r
1128 //==============================================================================\r
1129 class JuceAUView  : public AUCarbonViewBase\r
1131 public:\r
1132     JuceAUView (AudioUnitCarbonView auview)\r
1133       : AUCarbonViewBase (auview),\r
1134         juceFilter (nullptr)\r
1135     {\r
1136     }\r
1138     ~JuceAUView()\r
1139     {\r
1140         deleteUI();\r
1141     }\r
1143     ComponentResult CreateUI (Float32 /*inXOffset*/, Float32 /*inYOffset*/)\r
1144     {\r
1145         JUCE_AUTORELEASEPOOL\r
1147         if (juceFilter == nullptr)\r
1148         {\r
1149             void* pointers[2];\r
1150             UInt32 propertySize = sizeof (pointers);\r
1152             AudioUnitGetProperty (GetEditAudioUnit(),\r
1153                                   juceFilterObjectPropertyID,\r
1154                                   kAudioUnitScope_Global,\r
1155                                   0,\r
1156                                   pointers,\r
1157                                   &propertySize);\r
1159             juceFilter = (AudioProcessor*) pointers[0];\r
1160         }\r
1162         if (juceFilter != nullptr)\r
1163         {\r
1164             deleteUI();\r
1166             AudioProcessorEditor* editorComp = juceFilter->createEditorIfNeeded();\r
1167             editorComp->setOpaque (true);\r
1168             windowComp = new ComponentInHIView (editorComp, mCarbonPane);\r
1169         }\r
1170         else\r
1171         {\r
1172             jassertfalse // can't get a pointer to our effect\r
1173         }\r
1175         return noErr;\r
1176     }\r
1178     AudioUnitCarbonViewEventListener getEventListener() const   { return mEventListener; }\r
1179     void* getEventListenerUserData() const                      { return mEventListenerUserData; }\r
1181 private:\r
1182     //==============================================================================\r
1183     AudioProcessor* juceFilter;\r
1184     ScopedPointer<Component> windowComp;\r
1185     FakeMouseMoveGenerator fakeMouseGenerator;\r
1187     void deleteUI()\r
1188     {\r
1189         if (windowComp != nullptr)\r
1190         {\r
1191             PopupMenu::dismissAllActiveMenus();\r
1193             /* This assertion is triggered when there's some kind of modal component active, and the\r
1194                host is trying to delete our plugin.\r
1195                If you must use modal components, always use them in a non-blocking way, by never\r
1196                calling runModalLoop(), but instead using enterModalState() with a callback that\r
1197                will be performed on completion. (Note that this assertion could actually trigger\r
1198                a false alarm even if you're doing it correctly, but is here to catch people who\r
1199                aren't so careful) */\r
1200             jassert (Component::getCurrentlyModalComponent() == nullptr);\r
1202             if (windowComp != nullptr && windowComp->getChildComponent(0) != nullptr)\r
1203                 juceFilter->editorBeingDeleted ((AudioProcessorEditor*) windowComp->getChildComponent(0));\r
1205             windowComp = nullptr;\r
1206         }\r
1207     }\r
1209     //==============================================================================\r
1210     // Uses a child NSWindow to sit in front of a HIView and display our component\r
1211     class ComponentInHIView  : public Component\r
1212     {\r
1213     public:\r
1214         //==============================================================================\r
1215         ComponentInHIView (AudioProcessorEditor* const editor_, HIViewRef parentHIView)\r
1216             : parentView (parentHIView),\r
1217               editor (editor_),\r
1218               recursive (false)\r
1219         {\r
1220             JUCE_AUTORELEASEPOOL\r
1222             jassert (editor_ != nullptr);\r
1223             addAndMakeVisible (&editor);\r
1224             setOpaque (true);\r
1225             setVisible (true);\r
1226             setBroughtToFrontOnMouseClick (true);\r
1228             setSize (editor.getWidth(), editor.getHeight());\r
1229             SizeControl (parentHIView, editor.getWidth(), editor.getHeight());\r
1231             WindowRef windowRef = HIViewGetWindow (parentHIView);\r
1232             hostWindow = [[NSWindow alloc] initWithWindowRef: windowRef];\r
1234             [hostWindow retain];\r
1235             [hostWindow setCanHide: YES];\r
1236             [hostWindow setReleasedWhenClosed: YES];\r
1238             updateWindowPos();\r
1240            #if ! JucePlugin_EditorRequiresKeyboardFocus\r
1241             addToDesktop (ComponentPeer::windowIsTemporary | ComponentPeer::windowIgnoresKeyPresses);\r
1242             setWantsKeyboardFocus (false);\r
1243            #else\r
1244             addToDesktop (ComponentPeer::windowIsTemporary);\r
1245             setWantsKeyboardFocus (true);\r
1246           #endif\r
1248             setVisible (true);\r
1249             toFront (false);\r
1251             addSubWindow();\r
1253             NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];\r
1254             [pluginWindow setNextResponder: hostWindow];\r
1256             attachWindowHidingHooks (this, (WindowRef) windowRef, hostWindow);\r
1257         }\r
1259         ~ComponentInHIView()\r
1260         {\r
1261             JUCE_AUTORELEASEPOOL\r
1263             removeWindowHidingHooks (this);\r
1265             NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];\r
1266             [hostWindow removeChildWindow: pluginWindow];\r
1267             removeFromDesktop();\r
1269             [hostWindow release];\r
1270             hostWindow = 0;\r
1271         }\r
1273         void updateWindowPos()\r
1274         {\r
1275             HIPoint f;\r
1276             f.x = f.y = 0;\r
1277             HIPointConvert (&f, kHICoordSpaceView, parentView, kHICoordSpaceScreenPixel, 0);\r
1278             setTopLeftPosition ((int) f.x, (int) f.y);\r
1279         }\r
1281         void addSubWindow()\r
1282         {\r
1283             NSWindow* pluginWindow = [((NSView*) getWindowHandle()) window];\r
1284             [pluginWindow setExcludedFromWindowsMenu: YES];\r
1285             [pluginWindow setCanHide: YES];\r
1287             [hostWindow addChildWindow: pluginWindow\r
1288                                ordered: NSWindowAbove];\r
1289             [hostWindow orderFront: nil];\r
1290             [pluginWindow orderFront: nil];\r
1291         }\r
1293         void resized()\r
1294         {\r
1295             Component* const child = getChildComponent (0);\r
1296             if (child != nullptr)\r
1297                 child->setBounds (getLocalBounds());\r
1298         }\r
1300         void paint (Graphics&) {}\r
1302         void childBoundsChanged (Component*)\r
1303         {\r
1304             if (! recursive)\r
1305             {\r
1306                 recursive = true;\r
1308                 const int w = jmax (32, editor.getWidth());\r
1309                 const int h = jmax (32, editor.getHeight());\r
1311                 SizeControl (parentView, w, h);\r
1313                 if (getWidth() != w || getHeight() != h)\r
1314                     setSize (w, h);\r
1316                 editor.repaint();\r
1318                 updateWindowPos();\r
1319                 addSubWindow(); // (need this for AULab)\r
1321                 recursive = false;\r
1322             }\r
1323         }\r
1325         bool keyPressed (const KeyPress& kp)\r
1326         {\r
1327             if (! kp.getModifiers().isCommandDown())\r
1328             {\r
1329                 // If we have an unused keypress, move the key-focus to a host window\r
1330                 // and re-inject the event..\r
1331                 static NSTimeInterval lastEventTime = 0; // check we're not recursively sending the same event\r
1333                 if (lastEventTime != [[NSApp currentEvent] timestamp])\r
1334                 {\r
1335                     lastEventTime = [[NSApp currentEvent] timestamp];\r
1336                     [[hostWindow parentWindow] makeKeyWindow];\r
1337                     [NSApp postEvent: [NSApp currentEvent] atStart: YES];\r
1338                 }\r
1339             }\r
1341             return false;\r
1342         }\r
1344     private:\r
1345         //==============================================================================\r
1346         HIViewRef parentView;\r
1347         NSWindow* hostWindow;\r
1348         EditorCompHolder editor;\r
1349         bool recursive;\r
1350     };\r
1351 };\r
1353 #endif\r
1355 //==============================================================================\r
1356 #define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \\r
1357 extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \\r
1358 extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \\r
1359 { \\r
1360     return ComponentEntryPoint<Class>::Dispatch(params, obj); \\r
1363 #define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)\r
1365 JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)\r
1367 #if BUILD_AU_CARBON_UI\r
1368   JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)\r
1369 #endif\r
1371 #endif\r