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.

1270 lines
41KB

  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. #if ! (defined (LINUX) || defined (_WIN32))
  24. #include <Carbon/Carbon.h>
  25. #include <AudioToolbox/AudioToolbox.h>
  26. #include <AudioUnit/AudioUnitCarbonView.h>
  27. #include "../../../../../juce.h"
  28. #include "juce_AudioUnitPluginFormat.h"
  29. #include "../juce_PluginDescription.h"
  30. #if JUCE_PLUGINHOST_AU && JUCE_MAC
  31. BEGIN_JUCE_NAMESPACE
  32. extern void juce_callAnyTimersSynchronously();
  33. extern bool juce_isHIViewCreatedByJuce (HIViewRef view);
  34. extern bool juce_isWindowCreatedByJuce (WindowRef window);
  35. END_JUCE_NAMESPACE
  36. #if MACOS_10_3_OR_EARLIER
  37. #define kAudioUnitType_Generator 'augn'
  38. #endif
  39. // Change this to disable logging of various activities
  40. #ifndef AU_LOGGING
  41. #define AU_LOGGING 1
  42. #endif
  43. #if AU_LOGGING
  44. #define log(a) Logger::writeToLog(a);
  45. #else
  46. #define log(a)
  47. #endif
  48. static int insideCallback = 0;
  49. //==============================================================================
  50. class AudioUnitPluginWindow;
  51. //==============================================================================
  52. class AudioUnitPluginInstance : public AudioPluginInstance
  53. {
  54. public:
  55. //==============================================================================
  56. ~AudioUnitPluginInstance();
  57. //==============================================================================
  58. // AudioPluginInstance methods:
  59. const String getName() const { return pluginName; }
  60. const String getManufacturer() const { return manufacturer; }
  61. const String getVersion() const { return version; }
  62. bool isInstrument() const { return componentDesc.componentType == kAudioUnitType_MusicDevice; }
  63. const String getCategory() const;
  64. const String getFormatName() const { return "AudioUnit"; }
  65. const File getFile() const { return file; }
  66. int getUID() const { return file.hashCode(); }
  67. bool acceptsMidi() const { return wantsMidiMessages; }
  68. bool producesMidi() const { return false; }
  69. //==============================================================================
  70. // AudioProcessor methods:
  71. void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
  72. void releaseResources();
  73. void processBlock (AudioSampleBuffer& buffer,
  74. MidiBuffer& midiMessages);
  75. AudioProcessorEditor* createEditor();
  76. const String getInputChannelName (const int index) const;
  77. bool isInputChannelStereoPair (int index) const;
  78. const String getOutputChannelName (const int index) const;
  79. bool isOutputChannelStereoPair (int index) const;
  80. //==============================================================================
  81. int getNumParameters();
  82. float getParameter (int index);
  83. void setParameter (int index, float newValue);
  84. const String getParameterName (int index);
  85. const String getParameterText (int index);
  86. bool isParameterAutomatable (int index) const;
  87. //==============================================================================
  88. int getNumPrograms();
  89. int getCurrentProgram();
  90. void setCurrentProgram (int index);
  91. const String getProgramName (int index);
  92. void changeProgramName (int index, const String& newName);
  93. //==============================================================================
  94. void getStateInformation (MemoryBlock& destData);
  95. void getCurrentProgramStateInformation (MemoryBlock& destData);
  96. void setStateInformation (const void* data, int sizeInBytes);
  97. void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
  98. //==============================================================================
  99. juce_UseDebuggingNewOperator
  100. private:
  101. friend class AudioUnitPluginWindow;
  102. friend class AudioUnitPluginFormat;
  103. ComponentDescription componentDesc;
  104. String pluginName, manufacturer, version;
  105. File file;
  106. CriticalSection lock;
  107. bool initialised, wantsMidiMessages, wasPlaying;
  108. AudioBufferList* outputBufferList;
  109. AudioTimeStamp timeStamp;
  110. AudioSampleBuffer* currentBuffer;
  111. AudioUnit audioUnit;
  112. Array <int> parameterIds;
  113. //==============================================================================
  114. bool getComponentDescFromFile (const File& file);
  115. void initialise();
  116. //==============================================================================
  117. OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
  118. const AudioTimeStamp* inTimeStamp,
  119. UInt32 inBusNumber,
  120. UInt32 inNumberFrames,
  121. AudioBufferList* ioData) const;
  122. static OSStatus renderGetInputCallback (void* inRefCon,
  123. AudioUnitRenderActionFlags* ioActionFlags,
  124. const AudioTimeStamp* inTimeStamp,
  125. UInt32 inBusNumber,
  126. UInt32 inNumberFrames,
  127. AudioBufferList* ioData)
  128. {
  129. return ((AudioUnitPluginInstance*) inRefCon)
  130. ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
  131. }
  132. OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const;
  133. OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
  134. UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const;
  135. OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
  136. Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
  137. Float64* outCycleStartBeat, Float64* outCycleEndBeat);
  138. static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo)
  139. {
  140. return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo);
  141. }
  142. static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat,
  143. Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
  144. Float64* outCurrentMeasureDownBeat)
  145. {
  146. return ((AudioUnitPluginInstance*) inHostUserData)
  147. ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
  148. outTimeSig_Denominator, outCurrentMeasureDownBeat);
  149. }
  150. static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged,
  151. Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
  152. Float64* outCycleStartBeat, Float64* outCycleEndBeat)
  153. {
  154. return ((AudioUnitPluginInstance*) inHostUserData)
  155. ->getTransportState (outIsPlaying, outTransportStateChanged,
  156. outCurrentSampleInTimeLine, outIsCycling,
  157. outCycleStartBeat, outCycleEndBeat);
  158. }
  159. //==============================================================================
  160. void getNumChannels (int& numIns, int& numOuts)
  161. {
  162. numIns = 0;
  163. numOuts = 0;
  164. AUChannelInfo supportedChannels [128];
  165. UInt32 supportedChannelsSize = sizeof (supportedChannels);
  166. if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global,
  167. 0, supportedChannels, &supportedChannelsSize) == noErr
  168. && supportedChannelsSize > 0)
  169. {
  170. for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
  171. {
  172. numIns = jmax (numIns, supportedChannels[i].inChannels);
  173. numOuts = jmax (numOuts, supportedChannels[i].outChannels);
  174. }
  175. }
  176. else
  177. {
  178. // (this really means the plugin will take any number of ins/outs as long
  179. // as they are the same)
  180. numIns = numOuts = 2;
  181. }
  182. }
  183. //==============================================================================
  184. AudioUnitPluginInstance (const File& file);
  185. };
  186. //==============================================================================
  187. AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_)
  188. : file (file_),
  189. initialised (false),
  190. wantsMidiMessages (false),
  191. audioUnit (0),
  192. outputBufferList (0),
  193. currentBuffer (0)
  194. {
  195. try
  196. {
  197. ++insideCallback;
  198. log (T("Opening AU: ") + file.getFullPathName());
  199. if (getComponentDescFromFile (file))
  200. {
  201. ComponentRecord* const comp = FindNextComponent (0, &componentDesc);
  202. if (comp != 0)
  203. {
  204. audioUnit = (AudioUnit) OpenComponent (comp);
  205. wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice
  206. || componentDesc.componentType == kAudioUnitType_MusicEffect;
  207. }
  208. }
  209. --insideCallback;
  210. }
  211. catch (...)
  212. {
  213. --insideCallback;
  214. }
  215. }
  216. AudioUnitPluginInstance::~AudioUnitPluginInstance()
  217. {
  218. {
  219. const ScopedLock sl (lock);
  220. jassert (insideCallback == 0);
  221. if (audioUnit != 0)
  222. {
  223. AudioUnitUninitialize (audioUnit);
  224. CloseComponent (audioUnit);
  225. audioUnit = 0;
  226. }
  227. }
  228. juce_free (outputBufferList);
  229. }
  230. bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file)
  231. {
  232. zerostruct (componentDesc);
  233. if (! file.hasFileExtension (T(".component")))
  234. return false;
  235. const String filename (file.getFullPathName());
  236. const char* const utf8 = filename.toUTF8();
  237. CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
  238. strlen (utf8), file.isDirectory());
  239. if (url != 0)
  240. {
  241. CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
  242. CFRelease (url);
  243. if (bundleRef != 0)
  244. {
  245. CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
  246. if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID())
  247. pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name);
  248. if (pluginName.isEmpty())
  249. pluginName = file.getFileNameWithoutExtension();
  250. CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion"));
  251. if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID())
  252. version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString);
  253. CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString"));
  254. if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID())
  255. manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString);
  256. short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
  257. UseResFile (resFileId);
  258. for (int i = 1; i <= Count1Resources ('thng'); ++i)
  259. {
  260. Handle h = Get1IndResource ('thng', i);
  261. if (h != 0)
  262. {
  263. HLock (h);
  264. const uint32* const types = (const uint32*) *h;
  265. if (types[0] == kAudioUnitType_MusicDevice
  266. || types[0] == kAudioUnitType_MusicEffect
  267. || types[0] == kAudioUnitType_Effect
  268. || types[0] == kAudioUnitType_Generator
  269. || types[0] == kAudioUnitType_Panner)
  270. {
  271. componentDesc.componentType = types[0];
  272. componentDesc.componentSubType = types[1];
  273. componentDesc.componentManufacturer = types[2];
  274. break;
  275. }
  276. HUnlock (h);
  277. ReleaseResource (h);
  278. }
  279. }
  280. CFBundleCloseBundleResourceMap (bundleRef, resFileId);
  281. CFRelease (bundleRef);
  282. }
  283. }
  284. return componentDesc.componentType != 0 && componentDesc.componentSubType != 0;
  285. }
  286. //==============================================================================
  287. void AudioUnitPluginInstance::initialise()
  288. {
  289. if (initialised || audioUnit == 0)
  290. return;
  291. log (T("Initialising AU: ") + pluginName);
  292. parameterIds.clear();
  293. {
  294. UInt32 paramListSize = 0;
  295. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
  296. 0, 0, &paramListSize);
  297. if (paramListSize > 0)
  298. {
  299. parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int));
  300. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
  301. 0, &parameterIds.getReference(0), &paramListSize);
  302. }
  303. }
  304. {
  305. AURenderCallbackStruct info;
  306. zerostruct (info);
  307. info.inputProcRefCon = this;
  308. info.inputProc = renderGetInputCallback;
  309. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
  310. 0, &info, sizeof (info));
  311. }
  312. {
  313. HostCallbackInfo info;
  314. zerostruct (info);
  315. info.hostUserData = this;
  316. info.beatAndTempoProc = getBeatAndTempoCallback;
  317. info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
  318. info.transportStateProc = getTransportStateCallback;
  319. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global,
  320. 0, &info, sizeof (info));
  321. }
  322. int numIns, numOuts;
  323. getNumChannels (numIns, numOuts);
  324. setPlayConfigDetails (numIns, numOuts, 0, 0);
  325. initialised = AudioUnitInitialize (audioUnit) == noErr;
  326. setLatencySamples (0);
  327. }
  328. //==============================================================================
  329. void AudioUnitPluginInstance::prepareToPlay (double sampleRate_,
  330. int samplesPerBlockExpected)
  331. {
  332. initialise();
  333. if (initialised)
  334. {
  335. int numIns, numOuts;
  336. getNumChannels (numIns, numOuts);
  337. setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected);
  338. Float64 latencySecs = 0.0;
  339. UInt32 latencySize = sizeof (latencySecs);
  340. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
  341. 0, &latencySecs, &latencySize);
  342. setLatencySamples (roundDoubleToInt (latencySecs * sampleRate_));
  343. AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
  344. AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
  345. AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
  346. AudioStreamBasicDescription stream;
  347. zerostruct (stream);
  348. stream.mSampleRate = sampleRate_;
  349. stream.mFormatID = kAudioFormatLinearPCM;
  350. stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
  351. stream.mFramesPerPacket = 1;
  352. stream.mBytesPerPacket = 4;
  353. stream.mBytesPerFrame = 4;
  354. stream.mBitsPerChannel = 32;
  355. stream.mChannelsPerFrame = numIns;
  356. OSStatus err = AudioUnitSetProperty (audioUnit,
  357. kAudioUnitProperty_StreamFormat,
  358. kAudioUnitScope_Input,
  359. 0, &stream, sizeof (stream));
  360. stream.mChannelsPerFrame = numOuts;
  361. err = AudioUnitSetProperty (audioUnit,
  362. kAudioUnitProperty_StreamFormat,
  363. kAudioUnitScope_Output,
  364. 0, &stream, sizeof (stream));
  365. juce_free (outputBufferList);
  366. outputBufferList = (AudioBufferList*) juce_calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1));
  367. outputBufferList->mNumberBuffers = numOuts;
  368. for (int i = numOuts; --i >= 0;)
  369. outputBufferList->mBuffers[i].mNumberChannels = 1;
  370. zerostruct (timeStamp);
  371. timeStamp.mSampleTime = 0;
  372. timeStamp.mHostTime = AudioGetCurrentHostTime();
  373. timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
  374. currentBuffer = 0;
  375. wasPlaying = false;
  376. }
  377. }
  378. void AudioUnitPluginInstance::releaseResources()
  379. {
  380. if (initialised)
  381. {
  382. AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
  383. AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
  384. AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
  385. juce_free (outputBufferList);
  386. outputBufferList = 0;
  387. currentBuffer = 0;
  388. }
  389. }
  390. OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
  391. const AudioTimeStamp* inTimeStamp,
  392. UInt32 inBusNumber,
  393. UInt32 inNumberFrames,
  394. AudioBufferList* ioData) const
  395. {
  396. if (inBusNumber == 0
  397. && currentBuffer != 0)
  398. {
  399. jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling
  400. for (int i = 0; i < ioData->mNumberBuffers; ++i)
  401. {
  402. if (i < currentBuffer->getNumChannels())
  403. {
  404. memcpy (ioData->mBuffers[i].mData,
  405. currentBuffer->getSampleData (i, 0),
  406. sizeof (float) * inNumberFrames);
  407. }
  408. else
  409. {
  410. zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames);
  411. }
  412. }
  413. }
  414. return noErr;
  415. }
  416. void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer,
  417. MidiBuffer& midiMessages)
  418. {
  419. const int numSamples = buffer.getNumSamples();
  420. if (initialised)
  421. {
  422. AudioUnitRenderActionFlags flags = 0;
  423. timeStamp.mHostTime = AudioGetCurrentHostTime();
  424. for (int i = getNumOutputChannels(); --i >= 0;)
  425. {
  426. outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples;
  427. outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0);
  428. }
  429. currentBuffer = &buffer;
  430. if (wantsMidiMessages)
  431. {
  432. const uint8* midiEventData;
  433. int midiEventSize, midiEventPosition;
  434. MidiBuffer::Iterator i (midiMessages);
  435. while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
  436. {
  437. if (midiEventSize <= 3)
  438. MusicDeviceMIDIEvent (audioUnit,
  439. midiEventData[0], midiEventData[1], midiEventData[2],
  440. midiEventPosition);
  441. else
  442. MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize);
  443. }
  444. midiMessages.clear();
  445. }
  446. AudioUnitRender (audioUnit, &flags, &timeStamp,
  447. 0, numSamples, outputBufferList);
  448. timeStamp.mSampleTime += numSamples;
  449. }
  450. else
  451. {
  452. // Not initialised, so just bypass..
  453. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  454. buffer.clear (i, 0, buffer.getNumSamples());
  455. }
  456. }
  457. //==============================================================================
  458. OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const
  459. {
  460. AudioPlayHead* const ph = getPlayHead();
  461. AudioPlayHead::CurrentPositionInfo result;
  462. if (ph != 0 && ph->getCurrentPosition (result))
  463. {
  464. *outCurrentBeat = result.ppqPosition;
  465. *outCurrentTempo = result.bpm;
  466. }
  467. else
  468. {
  469. *outCurrentBeat = 0;
  470. *outCurrentTempo = 120.0;
  471. }
  472. return noErr;
  473. }
  474. OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat,
  475. Float32* outTimeSig_Numerator,
  476. UInt32* outTimeSig_Denominator,
  477. Float64* outCurrentMeasureDownBeat) const
  478. {
  479. AudioPlayHead* const ph = getPlayHead();
  480. AudioPlayHead::CurrentPositionInfo result;
  481. if (ph != 0 && ph->getCurrentPosition (result))
  482. {
  483. *outTimeSig_Numerator = result.timeSigNumerator;
  484. *outTimeSig_Denominator = result.timeSigDenominator;
  485. *outDeltaSampleOffsetToNextBeat = 0; //xxx
  486. *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong
  487. }
  488. else
  489. {
  490. *outDeltaSampleOffsetToNextBeat = 0;
  491. *outTimeSig_Numerator = 4;
  492. *outTimeSig_Denominator = 4;
  493. *outCurrentMeasureDownBeat = 0;
  494. }
  495. return noErr;
  496. }
  497. OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying,
  498. Boolean* outTransportStateChanged,
  499. Float64* outCurrentSampleInTimeLine,
  500. Boolean* outIsCycling,
  501. Float64* outCycleStartBeat,
  502. Float64* outCycleEndBeat)
  503. {
  504. AudioPlayHead* const ph = getPlayHead();
  505. AudioPlayHead::CurrentPositionInfo result;
  506. if (ph != 0 && ph->getCurrentPosition (result))
  507. {
  508. *outIsPlaying = result.isPlaying;
  509. *outTransportStateChanged = result.isPlaying != wasPlaying;
  510. wasPlaying = result.isPlaying;
  511. *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate());
  512. *outIsCycling = false;
  513. *outCycleStartBeat = 0;
  514. *outCycleEndBeat = 0;
  515. }
  516. else
  517. {
  518. *outIsPlaying = false;
  519. *outTransportStateChanged = false;
  520. *outCurrentSampleInTimeLine = 0;
  521. *outIsCycling = false;
  522. *outCycleStartBeat = 0;
  523. *outCycleEndBeat = 0;
  524. }
  525. return noErr;
  526. }
  527. //==============================================================================
  528. static VoidArray activeWindows;
  529. //==============================================================================
  530. class AudioUnitPluginWindow : public AudioProcessorEditor,
  531. public Timer
  532. {
  533. public:
  534. //==============================================================================
  535. AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_)
  536. : AudioProcessorEditor (&plugin_),
  537. plugin (plugin_),
  538. isOpen (false),
  539. pluginWantsKeys (false),
  540. wasShowing (false),
  541. recursiveResize (false),
  542. viewComponent (0),
  543. pluginViewRef (0)
  544. {
  545. movementWatcher = new CompMovementWatcher (this);
  546. activeWindows.add (this);
  547. setOpaque (true);
  548. setVisible (true);
  549. setSize (1, 1);
  550. ComponentDescription viewList [16];
  551. UInt32 viewListSize = sizeof (viewList);
  552. AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global,
  553. 0, &viewList, &viewListSize);
  554. componentRecord = FindNextComponent (0, &viewList[0]);
  555. }
  556. ~AudioUnitPluginWindow()
  557. {
  558. deleteAndZero (movementWatcher);
  559. closePluginWindow();
  560. activeWindows.removeValue (this);
  561. plugin.editorBeingDeleted (this);
  562. }
  563. bool isValid() const throw() { return componentRecord != 0; }
  564. //==============================================================================
  565. void componentMovedOrResized()
  566. {
  567. if (recursiveResize)
  568. return;
  569. Component* const topComp = getTopLevelComponent();
  570. if (topComp->getPeer() != 0)
  571. {
  572. int x = 0, y = 0;
  573. relativePositionToOtherComponent (topComp, x, y);
  574. recursiveResize = true;
  575. if (pluginViewRef != 0)
  576. {
  577. HIRect r;
  578. r.origin.x = (float) x;
  579. r.origin.y = (float) y;
  580. r.size.width = (float) getWidth();
  581. r.size.height = (float) getHeight();
  582. HIViewSetFrame (pluginViewRef, &r);
  583. }
  584. recursiveResize = false;
  585. }
  586. }
  587. void componentVisibilityChanged()
  588. {
  589. const bool isShowingNow = isShowing();
  590. if (wasShowing != isShowingNow)
  591. {
  592. wasShowing = isShowingNow;
  593. if (isShowingNow)
  594. openPluginWindow();
  595. else
  596. closePluginWindow();
  597. }
  598. componentMovedOrResized();
  599. }
  600. void componentPeerChanged()
  601. {
  602. closePluginWindow();
  603. openPluginWindow();
  604. }
  605. void timerCallback()
  606. {
  607. if (pluginViewRef != 0)
  608. {
  609. HIRect bounds;
  610. HIViewGetBounds (pluginViewRef, &bounds);
  611. const int w = jmax (32, (int) bounds.size.width);
  612. const int h = jmax (32, (int) bounds.size.height);
  613. if (w != getWidth() || h != getHeight())
  614. {
  615. setSize (w, h);
  616. startTimer (50);
  617. }
  618. else
  619. {
  620. startTimer (jlimit (50, 500, getTimerInterval() + 20));
  621. }
  622. }
  623. }
  624. //==============================================================================
  625. bool keyStateChanged()
  626. {
  627. return pluginWantsKeys;
  628. }
  629. bool keyPressed (const KeyPress&)
  630. {
  631. return pluginWantsKeys;
  632. }
  633. //==============================================================================
  634. void paint (Graphics& g)
  635. {
  636. if (isOpen)
  637. {
  638. ComponentPeer* const peer = getPeer();
  639. if (peer != 0)
  640. {
  641. peer->addMaskedRegion (getScreenX() - peer->getScreenX(),
  642. getScreenY() - peer->getScreenY(),
  643. getWidth(), getHeight());
  644. }
  645. }
  646. else
  647. {
  648. g.fillAll (Colours::black);
  649. }
  650. }
  651. //==============================================================================
  652. void broughtToFront()
  653. {
  654. activeWindows.removeValue (this);
  655. activeWindows.add (this);
  656. }
  657. //==============================================================================
  658. juce_UseDebuggingNewOperator
  659. private:
  660. AudioUnitPluginInstance& plugin;
  661. bool isOpen, wasShowing, recursiveResize;
  662. bool pluginWantsKeys;
  663. ComponentRecord* componentRecord;
  664. AudioUnitCarbonView viewComponent;
  665. HIViewRef pluginViewRef;
  666. //==============================================================================
  667. void openPluginWindow()
  668. {
  669. if (isOpen || getWindowHandle() == 0 || componentRecord == 0)
  670. return;
  671. log (T("Opening AU GUI: ") + plugin.getName());
  672. isOpen = true;
  673. pluginWantsKeys = true; //xxx any way to find this out? Does it matter?
  674. viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord);
  675. if (viewComponent != 0)
  676. {
  677. Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(),
  678. getScreenY() - getTopLevelComponent()->getScreenY() };
  679. Float32Point size = { 250, 200 };
  680. AudioUnitCarbonViewCreate (viewComponent,
  681. plugin.audioUnit,
  682. (WindowRef) getWindowHandle(),
  683. HIViewGetRoot ((WindowRef) getWindowHandle()),
  684. &pos, &size,
  685. (ControlRef*) &pluginViewRef);
  686. }
  687. timerCallback(); // to set our comp to the right size
  688. repaint();
  689. }
  690. //==============================================================================
  691. void closePluginWindow()
  692. {
  693. stopTimer();
  694. if (isOpen)
  695. {
  696. log (T("Closing AU GUI: ") + plugin.getName());
  697. isOpen = false;
  698. if (viewComponent != 0)
  699. CloseComponent (viewComponent);
  700. pluginViewRef = 0;
  701. }
  702. }
  703. //==============================================================================
  704. class CompMovementWatcher : public ComponentMovementWatcher
  705. {
  706. public:
  707. CompMovementWatcher (AudioUnitPluginWindow* const owner_)
  708. : ComponentMovementWatcher (owner_),
  709. owner (owner_)
  710. {
  711. }
  712. //==============================================================================
  713. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  714. {
  715. owner->componentMovedOrResized();
  716. }
  717. void componentPeerChanged()
  718. {
  719. owner->componentPeerChanged();
  720. }
  721. void componentVisibilityChanged (Component&)
  722. {
  723. owner->componentVisibilityChanged();
  724. }
  725. private:
  726. AudioUnitPluginWindow* const owner;
  727. };
  728. CompMovementWatcher* movementWatcher;
  729. };
  730. //==============================================================================
  731. AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
  732. {
  733. AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this);
  734. if (! w->isValid())
  735. deleteAndZero (w);
  736. return w;
  737. }
  738. //==============================================================================
  739. const String AudioUnitPluginInstance::getCategory() const
  740. {
  741. const char* result = 0;
  742. switch (componentDesc.componentType)
  743. {
  744. case kAudioUnitType_Effect:
  745. case kAudioUnitType_MusicEffect:
  746. result = "Effect";
  747. break;
  748. case kAudioUnitType_MusicDevice:
  749. result = "Synth";
  750. break;
  751. case kAudioUnitType_Generator:
  752. result = "Generator";
  753. break;
  754. case kAudioUnitType_Panner:
  755. result = "Panner";
  756. break;
  757. default:
  758. break;
  759. }
  760. return result;
  761. }
  762. //==============================================================================
  763. int AudioUnitPluginInstance::getNumParameters()
  764. {
  765. return parameterIds.size();
  766. }
  767. float AudioUnitPluginInstance::getParameter (int index)
  768. {
  769. const ScopedLock sl (lock);
  770. Float32 value = 0.0f;
  771. if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
  772. {
  773. AudioUnitGetParameter (audioUnit,
  774. (UInt32) parameterIds.getUnchecked (index),
  775. kAudioUnitScope_Global, 0,
  776. &value);
  777. }
  778. return value;
  779. }
  780. void AudioUnitPluginInstance::setParameter (int index, float newValue)
  781. {
  782. const ScopedLock sl (lock);
  783. if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size())
  784. {
  785. AudioUnitSetParameter (audioUnit,
  786. (UInt32) parameterIds.getUnchecked (index),
  787. kAudioUnitScope_Global, 0,
  788. newValue, 0);
  789. }
  790. }
  791. const String AudioUnitPluginInstance::getParameterName (int index)
  792. {
  793. AudioUnitParameterInfo info;
  794. zerostruct (info);
  795. UInt32 sz = sizeof (info);
  796. String name;
  797. if (AudioUnitGetProperty (audioUnit,
  798. kAudioUnitProperty_ParameterInfo,
  799. kAudioUnitScope_Global,
  800. parameterIds [index], &info, &sz) == noErr)
  801. {
  802. if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
  803. name = PlatformUtilities::cfStringToJuceString (info.cfNameString);
  804. else
  805. name = String (info.name, sizeof (info.name));
  806. }
  807. return name;
  808. }
  809. const String AudioUnitPluginInstance::getParameterText (int index)
  810. {
  811. return String (getParameter (index));
  812. }
  813. bool AudioUnitPluginInstance::isParameterAutomatable (int index) const
  814. {
  815. AudioUnitParameterInfo info;
  816. UInt32 sz = sizeof (info);
  817. if (AudioUnitGetProperty (audioUnit,
  818. kAudioUnitProperty_ParameterInfo,
  819. kAudioUnitScope_Global,
  820. parameterIds [index], &info, &sz) == noErr)
  821. {
  822. return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
  823. }
  824. return true;
  825. }
  826. //==============================================================================
  827. int AudioUnitPluginInstance::getNumPrograms()
  828. {
  829. CFArrayRef presets;
  830. UInt32 sz = sizeof (CFArrayRef);
  831. int num = 0;
  832. if (AudioUnitGetProperty (audioUnit,
  833. kAudioUnitProperty_FactoryPresets,
  834. kAudioUnitScope_Global,
  835. 0, &presets, &sz) == noErr)
  836. {
  837. num = (int) CFArrayGetCount (presets);
  838. CFRelease (presets);
  839. }
  840. return num;
  841. }
  842. int AudioUnitPluginInstance::getCurrentProgram()
  843. {
  844. AUPreset current;
  845. current.presetNumber = 0;
  846. UInt32 sz = sizeof (AUPreset);
  847. AudioUnitGetProperty (audioUnit,
  848. kAudioUnitProperty_FactoryPresets,
  849. kAudioUnitScope_Global,
  850. 0, &current, &sz);
  851. return current.presetNumber;
  852. }
  853. void AudioUnitPluginInstance::setCurrentProgram (int newIndex)
  854. {
  855. AUPreset current;
  856. current.presetNumber = newIndex;
  857. current.presetName = 0;
  858. AudioUnitSetProperty (audioUnit,
  859. kAudioUnitProperty_FactoryPresets,
  860. kAudioUnitScope_Global,
  861. 0, &current, sizeof (AUPreset));
  862. }
  863. const String AudioUnitPluginInstance::getProgramName (int index)
  864. {
  865. String s;
  866. CFArrayRef presets;
  867. UInt32 sz = sizeof (CFArrayRef);
  868. if (AudioUnitGetProperty (audioUnit,
  869. kAudioUnitProperty_FactoryPresets,
  870. kAudioUnitScope_Global,
  871. 0, &presets, &sz) == noErr)
  872. {
  873. for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i)
  874. {
  875. const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i);
  876. if (p != 0 && p->presetNumber == index)
  877. {
  878. s = PlatformUtilities::cfStringToJuceString (p->presetName);
  879. break;
  880. }
  881. }
  882. CFRelease (presets);
  883. }
  884. return s;
  885. }
  886. void AudioUnitPluginInstance::changeProgramName (int index, const String& newName)
  887. {
  888. jassertfalse // xxx not implemented!
  889. }
  890. //==============================================================================
  891. const String AudioUnitPluginInstance::getInputChannelName (const int index) const
  892. {
  893. if (((unsigned int) index) < (unsigned int) getNumInputChannels())
  894. return T("Input ") + String (index + 1);
  895. return String::empty;
  896. }
  897. bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const
  898. {
  899. if (((unsigned int) index) >= (unsigned int) getNumInputChannels())
  900. return false;
  901. return true;
  902. }
  903. const String AudioUnitPluginInstance::getOutputChannelName (const int index) const
  904. {
  905. if (((unsigned int) index) < (unsigned int) getNumOutputChannels())
  906. return T("Output ") + String (index + 1);
  907. return String::empty;
  908. }
  909. bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const
  910. {
  911. if (((unsigned int) index) >= (unsigned int) getNumOutputChannels())
  912. return false;
  913. return true;
  914. }
  915. //==============================================================================
  916. void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData)
  917. {
  918. getCurrentProgramStateInformation (destData);
  919. }
  920. void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
  921. {
  922. CFPropertyListRef propertyList = 0;
  923. UInt32 sz = sizeof (CFPropertyListRef);
  924. if (AudioUnitGetProperty (audioUnit,
  925. kAudioUnitProperty_ClassInfo,
  926. kAudioUnitScope_Global,
  927. 0, &propertyList, &sz) == noErr)
  928. {
  929. CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault);
  930. CFWriteStreamOpen (stream);
  931. CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0);
  932. CFWriteStreamClose (stream);
  933. CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten);
  934. destData.setSize (bytesWritten);
  935. destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize());
  936. CFRelease (data);
  937. CFRelease (stream);
  938. CFRelease (propertyList);
  939. }
  940. }
  941. void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes)
  942. {
  943. setCurrentProgramStateInformation (data, sizeInBytes);
  944. }
  945. void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
  946. {
  947. CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault,
  948. (const UInt8*) data,
  949. sizeInBytes,
  950. kCFAllocatorNull);
  951. CFReadStreamOpen (stream);
  952. CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0;
  953. CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault,
  954. stream,
  955. 0,
  956. kCFPropertyListImmutable,
  957. &format,
  958. 0);
  959. CFRelease (stream);
  960. if (propertyList != 0)
  961. AudioUnitSetProperty (audioUnit,
  962. kAudioUnitProperty_ClassInfo,
  963. kAudioUnitScope_Global,
  964. 0, &propertyList, sizeof (propertyList));
  965. }
  966. //==============================================================================
  967. //==============================================================================
  968. AudioUnitPluginFormat::AudioUnitPluginFormat()
  969. {
  970. }
  971. AudioUnitPluginFormat::~AudioUnitPluginFormat()
  972. {
  973. }
  974. void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
  975. const File& file)
  976. {
  977. if (! fileMightContainThisPluginType (file))
  978. return;
  979. PluginDescription desc;
  980. desc.file = file;
  981. desc.uid = 0;
  982. AudioUnitPluginInstance* instance = dynamic_cast <AudioUnitPluginInstance*> (createInstanceFromDescription (desc));
  983. if (instance == 0)
  984. return;
  985. try
  986. {
  987. desc.fillInFromInstance (*instance);
  988. results.add (new PluginDescription (desc));
  989. }
  990. catch (...)
  991. {
  992. // crashed while loading...
  993. }
  994. deleteAndZero (instance);
  995. }
  996. AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
  997. {
  998. AudioUnitPluginInstance* result = 0;
  999. if (fileMightContainThisPluginType (desc.file))
  1000. {
  1001. result = new AudioUnitPluginInstance (desc.file);
  1002. if (result->audioUnit != 0)
  1003. {
  1004. result->initialise();
  1005. }
  1006. else
  1007. {
  1008. deleteAndZero (result);
  1009. }
  1010. }
  1011. return result;
  1012. }
  1013. bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f)
  1014. {
  1015. return f.hasFileExtension (T(".component"))
  1016. && f.isDirectory();
  1017. }
  1018. const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
  1019. {
  1020. return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components");
  1021. }
  1022. #endif
  1023. #endif