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.

898 lines
30KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include <AudioUnit/AudioUnit.h>
  24. #include "AUMIDIEffectBase.h"
  25. #include "AUCarbonViewBase.h"
  26. #include "../../juce_AudioFilterBase.h"
  27. #include "../../juce_IncludeCharacteristics.h"
  28. //==============================================================================
  29. #define juceFilterObjectPropertyID 0x1a45ffe9
  30. static VoidArray activePlugins;
  31. static const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  32. static const int numChannelConfigs = numElementsInArray (channelConfigs);
  33. BEGIN_JUCE_NAMESPACE
  34. extern void juce_setCurrentExecutableFileNameFromBundleId (const String& bundleId) throw();
  35. END_JUCE_NAMESPACE
  36. //==============================================================================
  37. class JuceAU : public AUMIDIEffectBase,
  38. public AudioFilterBase::HostCallbacks
  39. {
  40. public:
  41. //==============================================================================
  42. JuceAU (AudioUnit component)
  43. : AUMIDIEffectBase (component),
  44. juceFilter (0),
  45. bufferSpace (2, 16),
  46. channels (0),
  47. prepared (false)
  48. {
  49. CreateElements();
  50. if (activePlugins.size() == 0)
  51. {
  52. initialiseJuce_GUI();
  53. #ifdef JucePlugin_CFBundleIdentifier
  54. juce_setCurrentExecutableFileNameFromBundleId (JucePlugin_CFBundleIdentifier);
  55. #endif
  56. MessageManager::getInstance()->setTimeBeforeShowingWaitCursor (0);
  57. }
  58. juceFilter = createPluginFilter();
  59. juceFilter->setHostCallbacks (this);
  60. jassert (juceFilter != 0);
  61. Globals()->UseIndexedParameters (juceFilter->getNumParameters());
  62. activePlugins.add (this);
  63. zerostruct (auEvent);
  64. auEvent.mArgument.mParameter.mAudioUnit = GetComponentInstance();
  65. auEvent.mArgument.mParameter.mScope = kAudioUnitScope_Global;
  66. auEvent.mArgument.mParameter.mElement = 0;
  67. }
  68. ~JuceAU()
  69. {
  70. delete juceFilter;
  71. juceFilter = 0;
  72. juce_free (channels);
  73. channels = 0;
  74. jassert (activePlugins.contains (this));
  75. activePlugins.removeValue (this);
  76. if (activePlugins.size() == 0)
  77. shutdownJuce_GUI();
  78. }
  79. //==============================================================================
  80. ComponentResult GetPropertyInfo (AudioUnitPropertyID inID,
  81. AudioUnitScope inScope,
  82. AudioUnitElement inElement,
  83. UInt32& outDataSize,
  84. Boolean& outWritable)
  85. {
  86. if (inScope == kAudioUnitScope_Global)
  87. {
  88. if (inID == juceFilterObjectPropertyID)
  89. {
  90. outWritable = false;
  91. outDataSize = sizeof (void*);
  92. return noErr;
  93. }
  94. }
  95. return AUMIDIEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
  96. }
  97. ComponentResult GetProperty (AudioUnitPropertyID inID,
  98. AudioUnitScope inScope,
  99. AudioUnitElement inElement,
  100. void* outData)
  101. {
  102. if (inScope == kAudioUnitScope_Global)
  103. {
  104. if (inID == juceFilterObjectPropertyID)
  105. {
  106. *((void**) outData) = (void*) juceFilter;
  107. return noErr;
  108. }
  109. }
  110. return AUMIDIEffectBase::GetProperty (inID, inScope, inElement, outData);
  111. }
  112. ComponentResult SaveState (CFPropertyListRef* outData)
  113. {
  114. ComponentResult err = AUMIDIEffectBase::SaveState (outData);
  115. if (err != noErr)
  116. return err;
  117. jassert (CFGetTypeID (*outData) == CFDictionaryGetTypeID());
  118. CFMutableDictionaryRef dict = (CFMutableDictionaryRef) *outData;
  119. if (juceFilter != 0)
  120. {
  121. JUCE_NAMESPACE::MemoryBlock state;
  122. juceFilter->getStateInformation (state);
  123. if (state.getSize() > 0)
  124. {
  125. CFDataRef ourState = CFDataCreate (kCFAllocatorDefault, (const uint8*) state, state.getSize());
  126. CFDictionarySetValue (dict, CFSTR("jucePluginState"), ourState);
  127. CFRelease (ourState);
  128. }
  129. }
  130. return noErr;
  131. }
  132. ComponentResult RestoreState (CFPropertyListRef inData)
  133. {
  134. ComponentResult err = AUMIDIEffectBase::RestoreState (inData);
  135. if (err != noErr)
  136. return err;
  137. if (juceFilter != 0)
  138. {
  139. CFDictionaryRef dict = (CFDictionaryRef) inData;
  140. CFDataRef data = 0;
  141. if (CFDictionaryGetValueIfPresent (dict, CFSTR("jucePluginState"),
  142. (const void**) &data))
  143. {
  144. if (data != 0)
  145. {
  146. const int numBytes = (int) CFDataGetLength (data);
  147. const uint8* const rawBytes = CFDataGetBytePtr (data);
  148. if (numBytes > 0)
  149. juceFilter->setStateInformation (rawBytes, numBytes);
  150. }
  151. }
  152. }
  153. return noErr;
  154. }
  155. UInt32 SupportedNumChannels (const AUChannelInfo** outInfo)
  156. {
  157. if (juceFilter == 0)
  158. return 0;
  159. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  160. // value in your JucePluginCharacteristics.h file..
  161. jassert (numChannelConfigs > 0);
  162. if (outInfo != 0)
  163. {
  164. for (int i = 0; i < numChannelConfigs; ++i)
  165. {
  166. channelInfo[i].inChannels = channelConfigs[i][0];
  167. channelInfo[i].outChannels = channelConfigs[i][1];
  168. outInfo[i] = channelInfo + i;
  169. }
  170. }
  171. return numChannelConfigs;
  172. }
  173. //==============================================================================
  174. ComponentResult GetParameterInfo (AudioUnitScope inScope,
  175. AudioUnitParameterID inParameterID,
  176. AudioUnitParameterInfo& outParameterInfo)
  177. {
  178. const int index = (int) inParameterID;
  179. if (inScope == kAudioUnitScope_Global
  180. && juceFilter != 0
  181. && index < juceFilter->getNumParameters())
  182. {
  183. outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable
  184. | kAudioUnitParameterFlag_IsReadable
  185. | kAudioUnitParameterFlag_HasCFNameString;
  186. const String name (juceFilter->getParameterName (index));
  187. CharacterFunctions::copy ((char*) outParameterInfo.name,
  188. (const char*) name.toUTF8(),
  189. sizeof (outParameterInfo.name) - 1);
  190. // set whether the param is automatable (unnamed parameters aren't allowed to be automated)
  191. if (name.isEmpty() || ! juceFilter->isParameterAutomatable (index))
  192. outParameterInfo.flags |= kAudioUnitParameterFlag_NonRealTime;
  193. outParameterInfo.cfNameString = PlatformUtilities::juceStringToCFString (name);
  194. outParameterInfo.minValue = 0.0f;
  195. outParameterInfo.maxValue = 1.0f;
  196. outParameterInfo.defaultValue = 0.0f;
  197. outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
  198. return noErr;
  199. }
  200. else
  201. {
  202. return kAudioUnitErr_InvalidParameter;
  203. }
  204. }
  205. ComponentResult GetParameter (AudioUnitParameterID inID,
  206. AudioUnitScope inScope,
  207. AudioUnitElement inElement,
  208. Float32& outValue)
  209. {
  210. if (inScope == kAudioUnitScope_Global && juceFilter != 0)
  211. {
  212. outValue = juceFilter->getParameter ((int) inID);
  213. return noErr;
  214. }
  215. return AUBase::GetParameter (inID, inScope, inElement, outValue);
  216. }
  217. ComponentResult SetParameter (AudioUnitParameterID inID,
  218. AudioUnitScope inScope,
  219. AudioUnitElement inElement,
  220. Float32 inValue,
  221. UInt32 inBufferOffsetInFrames)
  222. {
  223. if (inScope == kAudioUnitScope_Global && juceFilter != 0)
  224. {
  225. juceFilter->setParameter ((int) inID, inValue);
  226. return noErr;
  227. }
  228. return AUBase::SetParameter (inID, inScope, inElement, inValue, inBufferOffsetInFrames);
  229. }
  230. //==============================================================================
  231. ComponentResult Version() { return JucePlugin_VersionCode; }
  232. bool SupportsTail() { return true; }
  233. Float64 GetTailTime() { return 0; }
  234. Float64 GetLatency()
  235. {
  236. jassert (GetSampleRate() > 0);
  237. if (GetSampleRate() <= 0)
  238. return 0.0;
  239. return getLatencySamples() / GetSampleRate();
  240. }
  241. //==============================================================================
  242. int GetNumCustomUIComponents() { return 1; }
  243. void GetUIComponentDescs (ComponentDescription* inDescArray)
  244. {
  245. inDescArray[0].componentType = kAudioUnitCarbonViewComponentType;
  246. inDescArray[0].componentSubType = JucePlugin_AUSubType;
  247. inDescArray[0].componentManufacturer = JucePlugin_AUManufacturerCode;
  248. inDescArray[0].componentFlags = 0;
  249. inDescArray[0].componentFlagsMask = 0;
  250. }
  251. //==============================================================================
  252. bool getCurrentPositionInfo (AudioFilterBase::CurrentPositionInfo& info)
  253. {
  254. info.timeSigNumerator = 0;
  255. info.timeSigDenominator = 0;
  256. info.timeInSeconds = 0;
  257. info.editOriginTime = 0;
  258. info.ppqPositionOfLastBarStart = 0;
  259. info.isPlaying = false;
  260. info.isRecording = false;
  261. switch (lastSMPTETime.mType)
  262. {
  263. case kSMPTETimeType24:
  264. info.frameRate = AudioFilterBase::CurrentPositionInfo::fps24;
  265. break;
  266. case kSMPTETimeType25:
  267. info.frameRate = AudioFilterBase::CurrentPositionInfo::fps25;
  268. break;
  269. case kSMPTETimeType30Drop:
  270. info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30drop;
  271. break;
  272. case kSMPTETimeType30:
  273. info.frameRate = AudioFilterBase::CurrentPositionInfo::fps30;
  274. break;
  275. case kSMPTETimeType2997:
  276. info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997;
  277. break;
  278. case kSMPTETimeType2997Drop:
  279. info.frameRate = AudioFilterBase::CurrentPositionInfo::fps2997drop;
  280. break;
  281. //case kSMPTETimeType60:
  282. //case kSMPTETimeType5994:
  283. default:
  284. info.frameRate = AudioFilterBase::CurrentPositionInfo::fpsUnknown;
  285. break;
  286. }
  287. if (CallHostBeatAndTempo (&info.ppqPosition, &info.bpm) != noErr)
  288. {
  289. info.ppqPosition = 0;
  290. info.bpm = 0;
  291. }
  292. UInt32 outDeltaSampleOffsetToNextBeat;
  293. double outCurrentMeasureDownBeat;
  294. float num;
  295. UInt32 den;
  296. if (CallHostMusicalTimeLocation (&outDeltaSampleOffsetToNextBeat, &num, &den,
  297. &outCurrentMeasureDownBeat) == noErr)
  298. {
  299. info.timeSigNumerator = (int) num;
  300. info.timeSigDenominator = den;
  301. info.ppqPositionOfLastBarStart = outCurrentMeasureDownBeat;
  302. }
  303. double outCurrentSampleInTimeLine, outCycleStartBeat, outCycleEndBeat;
  304. Boolean playing, playchanged, looping;
  305. if (CallHostTransportState (&playing,
  306. &playchanged,
  307. &outCurrentSampleInTimeLine,
  308. &looping,
  309. &outCycleStartBeat,
  310. &outCycleEndBeat) == noErr)
  311. {
  312. info.isPlaying = playing;
  313. info.timeInSeconds = outCurrentSampleInTimeLine / GetSampleRate();
  314. }
  315. return true;
  316. }
  317. void sendAUEvent (const AudioUnitEventType type, const int index) throw()
  318. {
  319. if (AUEventListenerNotify != 0)
  320. {
  321. auEvent.mEventType = type;
  322. auEvent.mArgument.mParameter.mParameterID = (AudioUnitParameterID) index;
  323. AUEventListenerNotify (0, 0, &auEvent);
  324. }
  325. }
  326. void informHostOfParameterChange (int index, float newValue)
  327. {
  328. if (juceFilter != 0)
  329. {
  330. juceFilter->setParameter (index, newValue);
  331. sendAUEvent (kAudioUnitEvent_ParameterValueChange, index);
  332. }
  333. }
  334. void informHostOfParameterGestureBegin (int index)
  335. {
  336. if (juceFilter != 0)
  337. sendAUEvent (kAudioUnitEvent_BeginParameterChangeGesture, index);
  338. }
  339. void informHostOfParameterGestureEnd (int index)
  340. {
  341. if (juceFilter != 0)
  342. sendAUEvent (kAudioUnitEvent_EndParameterChangeGesture, index);
  343. }
  344. void informHostOfStateChange()
  345. {
  346. // xxx is there an AU equivalent?
  347. }
  348. //==============================================================================
  349. ComponentResult Initialize()
  350. {
  351. AUMIDIEffectBase::Initialize();
  352. const int numIns = GetInput(0) != 0 ? GetInput(0)->GetStreamFormat().mChannelsPerFrame : 0;
  353. const int numOuts = GetOutput(0) != 0 ? GetOutput(0)->GetStreamFormat().mChannelsPerFrame : 0;
  354. bool isValidChannelConfig = false;
  355. for (int i = 0; i < numChannelConfigs; ++i)
  356. if (numIns == channelConfigs[i][0] && numOuts == channelConfigs[i][1])
  357. isValidChannelConfig = true;
  358. if (! isValidChannelConfig)
  359. return kAudioUnitErr_FormatNotSupported;
  360. prepareToPlay();
  361. return noErr;
  362. }
  363. void Cleanup()
  364. {
  365. AUMIDIEffectBase::Cleanup();
  366. if (juceFilter != 0)
  367. juceFilter->releaseResources();
  368. bufferSpace.setSize (2, 16);
  369. midiEvents.clear();
  370. prepared = false;
  371. }
  372. ComponentResult Reset (AudioUnitScope inScope, AudioUnitElement inElement)
  373. {
  374. if (! prepared)
  375. prepareToPlay();
  376. return AUMIDIEffectBase::Reset (inScope, inElement);
  377. }
  378. void prepareToPlay()
  379. {
  380. if (juceFilter != 0)
  381. {
  382. juceFilter->setPlayConfigDetails (GetInput(0)->GetStreamFormat().mChannelsPerFrame,
  383. GetOutput(0)->GetStreamFormat().mChannelsPerFrame,
  384. GetSampleRate(),
  385. GetMaxFramesPerSlice());
  386. bufferSpace.setSize (juceFilter->getNumInputChannels() + juceFilter->getNumOutputChannels(),
  387. GetMaxFramesPerSlice() + 32);
  388. juceFilter->prepareToPlay (GetSampleRate(),
  389. GetMaxFramesPerSlice());
  390. midiEvents.clear();
  391. juce_free (channels);
  392. channels = (float**) juce_calloc (sizeof (float*) * jmax (juceFilter->getNumInputChannels(),
  393. juceFilter->getNumOutputChannels()) + 4);
  394. prepared = true;
  395. }
  396. }
  397. ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags,
  398. const AudioTimeStamp& inTimeStamp,
  399. UInt32 nFrames)
  400. {
  401. lastSMPTETime = inTimeStamp.mSMPTETime;
  402. return AUMIDIEffectBase::Render (ioActionFlags, inTimeStamp, nFrames);
  403. }
  404. OSStatus ProcessBufferLists (AudioUnitRenderActionFlags& ioActionFlags,
  405. const AudioBufferList& inBuffer,
  406. AudioBufferList& outBuffer,
  407. UInt32 numSamples)
  408. {
  409. if (juceFilter != 0)
  410. {
  411. jassert (prepared);
  412. int numOutChans = 0;
  413. int nextSpareBufferChan = 0;
  414. bool needToReinterleave = false;
  415. const int numIn = juceFilter->getNumInputChannels();
  416. const int numOut = juceFilter->getNumOutputChannels();
  417. unsigned int i;
  418. for (i = 0; i < outBuffer.mNumberBuffers; ++i)
  419. {
  420. AudioBuffer& buf = outBuffer.mBuffers[i];
  421. if (buf.mNumberChannels == 1)
  422. {
  423. channels [numOutChans++] = (float*) buf.mData;
  424. }
  425. else
  426. {
  427. needToReinterleave = true;
  428. for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numOutChans < numOut; ++subChan)
  429. channels [numOutChans++] = bufferSpace.getSampleData (nextSpareBufferChan++);
  430. }
  431. if (numOutChans >= numOut)
  432. break;
  433. }
  434. int numInChans = 0;
  435. for (i = 0; i < inBuffer.mNumberBuffers; ++i)
  436. {
  437. const AudioBuffer& buf = inBuffer.mBuffers[i];
  438. if (buf.mNumberChannels == 1)
  439. {
  440. if (numInChans < numOut)
  441. memcpy (channels [numInChans], (const float*) buf.mData, sizeof (float) * numSamples);
  442. else
  443. channels [numInChans] = (float*) buf.mData;
  444. }
  445. else
  446. {
  447. // need to de-interleave..
  448. for (unsigned int subChan = 0; subChan < buf.mNumberChannels && numInChans < numIn; ++subChan)
  449. {
  450. float* dest;
  451. if (numInChans >= numOut)
  452. {
  453. dest = bufferSpace.getSampleData (nextSpareBufferChan++);
  454. channels [numInChans++] = dest;
  455. }
  456. else
  457. {
  458. dest = channels [numInChans++];
  459. }
  460. const float* src = ((const float*) buf.mData) + subChan;
  461. for (int j = numSamples; --j >= 0;)
  462. {
  463. *dest++ = *src;
  464. src += buf.mNumberChannels;
  465. }
  466. }
  467. }
  468. if (numInChans >= numIn)
  469. break;
  470. }
  471. {
  472. AudioSampleBuffer buffer (channels, jmax (numIn, numOut), numSamples);
  473. const ScopedLock sl (juceFilter->getCallbackLock());
  474. if (juceFilter->isSuspended())
  475. {
  476. for (int i = 0; i < numOut; ++i)
  477. zeromem (channels [i], sizeof (float) * numSamples);
  478. }
  479. else
  480. {
  481. juceFilter->processBlock (buffer, midiEvents);
  482. }
  483. }
  484. if (! midiEvents.isEmpty())
  485. {
  486. #if JucePlugin_ProducesMidiOutput
  487. const uint8* midiEventData;
  488. int midiEventSize, midiEventPosition;
  489. MidiBuffer::Iterator i (midiEvents);
  490. while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
  491. {
  492. jassert (midiEventPosition >= 0 && midiEventPosition < (int) numSamples);
  493. //xxx
  494. }
  495. #else
  496. // if your plugin creates midi messages, you'll need to set
  497. // the JucePlugin_ProducesMidiOutput macro to 1 in your
  498. // JucePluginCharacteristics.h file
  499. //jassert (midiEvents.getNumEvents() <= numMidiEventsComingIn);
  500. #endif
  501. midiEvents.clear();
  502. }
  503. if (needToReinterleave)
  504. {
  505. nextSpareBufferChan = 0;
  506. for (i = 0; i < outBuffer.mNumberBuffers; ++i)
  507. {
  508. AudioBuffer& buf = outBuffer.mBuffers[i];
  509. if (buf.mNumberChannels > 1)
  510. {
  511. for (unsigned int subChan = 0; subChan < buf.mNumberChannels; ++subChan)
  512. {
  513. const float* src = bufferSpace.getSampleData (nextSpareBufferChan++);
  514. float* dest = ((float*) buf.mData) + subChan;
  515. for (int j = numSamples; --j >= 0;)
  516. {
  517. *dest = *src++;
  518. dest += buf.mNumberChannels;
  519. }
  520. }
  521. }
  522. }
  523. }
  524. #if ! JucePlugin_SilenceInProducesSilenceOut
  525. ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
  526. #endif
  527. }
  528. return noErr;
  529. }
  530. protected:
  531. OSStatus HandleMidiEvent (UInt8 nStatus,
  532. UInt8 inChannel,
  533. UInt8 inData1,
  534. UInt8 inData2,
  535. long inStartFrame)
  536. {
  537. #if JucePlugin_WantsMidiInput
  538. uint8 data [4];
  539. data[0] = nStatus | inChannel;
  540. data[1] = inData1;
  541. data[2] = inData2;
  542. midiEvents.addEvent (data, 3, inStartFrame);
  543. #endif
  544. return noErr;
  545. }
  546. //==============================================================================
  547. private:
  548. AudioFilterBase* juceFilter;
  549. AudioSampleBuffer bufferSpace;
  550. float** channels;
  551. MidiBuffer midiEvents;
  552. bool prepared;
  553. SMPTETime lastSMPTETime;
  554. AUChannelInfo channelInfo [numChannelConfigs];
  555. AudioUnitEvent auEvent;
  556. };
  557. //==============================================================================
  558. class JuceAUComponentHolder : public Component
  559. {
  560. public:
  561. JuceAUComponentHolder (Component* const editorComp)
  562. {
  563. addAndMakeVisible (editorComp);
  564. setOpaque (true);
  565. setVisible (true);
  566. setBroughtToFrontOnMouseClick (true);
  567. #if ! JucePlugin_EditorRequiresKeyboardFocus
  568. setWantsKeyboardFocus (false);
  569. #endif
  570. }
  571. ~JuceAUComponentHolder()
  572. {
  573. }
  574. void resized()
  575. {
  576. if (getNumChildComponents() > 0)
  577. getChildComponent (0)->setBounds (0, 0, getWidth(), getHeight());
  578. }
  579. void paint (Graphics& g)
  580. {
  581. }
  582. };
  583. //==============================================================================
  584. class JuceAUView : public AUCarbonViewBase,
  585. public ComponentListener,
  586. public MouseListener,
  587. public Timer
  588. {
  589. AudioFilterBase* juceFilter;
  590. AudioFilterEditor* editorComp;
  591. Component* windowComp;
  592. bool recursive;
  593. int mx, my;
  594. public:
  595. JuceAUView (AudioUnitCarbonView auview)
  596. : AUCarbonViewBase (auview),
  597. juceFilter (0),
  598. editorComp (0),
  599. windowComp (0),
  600. recursive (false),
  601. mx (0),
  602. my (0)
  603. {
  604. }
  605. ~JuceAUView()
  606. {
  607. deleteUI();
  608. }
  609. ComponentResult CreateUI (Float32 inXOffset, Float32 inYOffset)
  610. {
  611. if (juceFilter == 0)
  612. {
  613. UInt32 propertySize = sizeof (&juceFilter);
  614. AudioUnitGetProperty (GetEditAudioUnit(),
  615. juceFilterObjectPropertyID,
  616. kAudioUnitScope_Global,
  617. 0,
  618. &juceFilter,
  619. &propertySize);
  620. }
  621. if (juceFilter != 0)
  622. {
  623. deleteUI();
  624. editorComp = juceFilter->createEditorIfNeeded();
  625. const int w = editorComp->getWidth();
  626. const int h = editorComp->getHeight();
  627. editorComp->setOpaque (true);
  628. editorComp->setVisible (true);
  629. windowComp = new JuceAUComponentHolder (editorComp);
  630. windowComp->setBounds ((int) inXOffset, (int) inYOffset, w, h);
  631. windowComp->addToDesktop (0, (void*) mCarbonPane);
  632. SizeControl (mCarbonPane, w, h);
  633. editorComp->addComponentListener (this);
  634. windowComp->addMouseListener (this, true);
  635. startTimer (20);
  636. }
  637. else
  638. {
  639. jassertfalse // can't get a pointer to our effect
  640. }
  641. return noErr;
  642. }
  643. void componentMovedOrResized (Component& component,
  644. bool wasMoved,
  645. bool wasResized)
  646. {
  647. if (! recursive)
  648. {
  649. recursive = true;
  650. if (editorComp != 0 && wasResized)
  651. {
  652. const int w = jmax (32, editorComp->getWidth());
  653. const int h = jmax (32, editorComp->getHeight());
  654. SizeControl (mCarbonPane, w, h);
  655. if (windowComp->getWidth() != w
  656. || windowComp->getHeight() != h)
  657. {
  658. windowComp->setSize (w, h);
  659. }
  660. editorComp->repaint();
  661. }
  662. recursive = false;
  663. }
  664. }
  665. void timerCallback()
  666. {
  667. // for some stupid Apple-related reason, mouse move events just don't seem to get sent
  668. // to the windows in an AU, so we have to bodge it here and simulate them with a
  669. // timer..
  670. if (editorComp != 0)
  671. {
  672. int x, y;
  673. Desktop::getInstance().getMousePosition (x, y);
  674. if (x != mx || y != my)
  675. {
  676. mx = x;
  677. my = y;
  678. if (! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown())
  679. {
  680. for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
  681. {
  682. ComponentPeer* const peer = ComponentPeer::getPeer (i);
  683. const int rx = x - peer->getComponent()->getX();
  684. const int ry = y - peer->getComponent()->getY();
  685. if (peer->contains (rx, ry, false) && peer->getComponent()->isShowing())
  686. {
  687. peer->handleMouseMove (rx, ry, Time::currentTimeMillis());
  688. break;
  689. }
  690. }
  691. }
  692. }
  693. }
  694. }
  695. void mouseMove (const MouseEvent& e)
  696. {
  697. Desktop::getInstance().getMousePosition (mx, my);
  698. startTimer (20);
  699. }
  700. private:
  701. void deleteUI()
  702. {
  703. PopupMenu::dismissAllActiveMenus();
  704. // there's some kind of component currently modal, but the host
  705. // is trying to delete our plugin..
  706. jassert (Component::getCurrentlyModalComponent() == 0);
  707. if (editorComp != 0)
  708. juceFilter->editorBeingDeleted (editorComp);
  709. deleteAndZero (editorComp);
  710. deleteAndZero (windowComp);
  711. }
  712. };
  713. //==============================================================================
  714. #define JUCE_COMPONENT_ENTRYX(Class, Name, Suffix) \
  715. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj); \
  716. extern "C" __attribute__((visibility("default"))) ComponentResult Name ## Suffix (ComponentParameters* params, Class* obj) \
  717. { \
  718. return ComponentEntryPoint<Class>::Dispatch(params, obj); \
  719. }
  720. #define JUCE_COMPONENT_ENTRY(Class, Name, Suffix) JUCE_COMPONENT_ENTRYX(Class, Name, Suffix)
  721. JUCE_COMPONENT_ENTRY (JuceAU, JucePlugin_AUExportPrefix, Entry)
  722. JUCE_COMPONENT_ENTRY (JuceAUView, JucePlugin_AUExportPrefix, ViewEntry)