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.

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