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.

1573 lines
54KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "../../../core/juce_TargetPlatform.h"
  19. #include "../../../../juce_Config.h"
  20. #if JUCE_PLUGINHOST_AU && ! (JUCE_LINUX || JUCE_WINDOWS)
  21. #include <AudioUnit/AudioUnit.h>
  22. #include <AudioUnit/AUCocoaUIView.h>
  23. #include <CoreAudioKit/AUGenericView.h>
  24. #if JUCE_SUPPORT_CARBON
  25. #include <AudioToolbox/AudioUnitUtilities.h>
  26. #include <AudioUnit/AudioUnitCarbonView.h>
  27. #endif
  28. #include "../../../core/juce_StandardHeader.h"
  29. BEGIN_JUCE_NAMESPACE
  30. #include "juce_AudioUnitPluginFormat.h"
  31. #include "../juce_PluginDescription.h"
  32. #include "../../../threads/juce_CriticalSection.h"
  33. #include "../../../events/juce_Timer.h"
  34. #include "../../../core/juce_PlatformUtilities.h"
  35. #include "../../../gui/components/layout/juce_ComponentMovementWatcher.h"
  36. #include "../../../gui/components/windows/juce_ComponentPeer.h"
  37. #include "../../../gui/components/special/juce_NSViewComponent.h"
  38. #if JUCE_MAC && JUCE_SUPPORT_CARBON
  39. #include "../../../native/mac/juce_mac_CarbonViewWrapperComponent.h"
  40. #endif
  41. #if JUCE_MAC
  42. // Change this to disable logging of various activities
  43. #ifndef AU_LOGGING
  44. #define AU_LOGGING 1
  45. #endif
  46. #if AU_LOGGING
  47. #define log(a) Logger::writeToLog(a);
  48. #else
  49. #define log(a)
  50. #endif
  51. namespace AudioUnitFormatHelpers
  52. {
  53. static int insideCallback = 0;
  54. const String osTypeToString (OSType type)
  55. {
  56. const juce_wchar s[4] = { (juce_wchar) ((type >> 24) & 0xff),
  57. (juce_wchar) ((type >> 16) & 0xff),
  58. (juce_wchar) ((type >> 8) & 0xff),
  59. (juce_wchar) (type & 0xff) };
  60. return String (s, 4);
  61. }
  62. OSType stringToOSType (const String& s1)
  63. {
  64. const String s (s1 + " ");
  65. return (((OSType) (unsigned char) s[0]) << 24)
  66. | (((OSType) (unsigned char) s[1]) << 16)
  67. | (((OSType) (unsigned char) s[2]) << 8)
  68. | ((OSType) (unsigned char) s[3]);
  69. }
  70. static const char* auIdentifierPrefix = "AudioUnit:";
  71. const String createAUPluginIdentifier (const ComponentDescription& desc)
  72. {
  73. jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong..
  74. jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto
  75. String s (auIdentifierPrefix);
  76. if (desc.componentType == kAudioUnitType_MusicDevice)
  77. s << "Synths/";
  78. else if (desc.componentType == kAudioUnitType_MusicEffect
  79. || desc.componentType == kAudioUnitType_Effect)
  80. s << "Effects/";
  81. else if (desc.componentType == kAudioUnitType_Generator)
  82. s << "Generators/";
  83. else if (desc.componentType == kAudioUnitType_Panner)
  84. s << "Panners/";
  85. s << osTypeToString (desc.componentType) << ","
  86. << osTypeToString (desc.componentSubType) << ","
  87. << osTypeToString (desc.componentManufacturer);
  88. return s;
  89. }
  90. void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer)
  91. {
  92. Handle componentNameHandle = NewHandle (sizeof (void*));
  93. Handle componentInfoHandle = NewHandle (sizeof (void*));
  94. if (componentNameHandle != 0 && componentInfoHandle != 0)
  95. {
  96. ComponentDescription desc;
  97. if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr)
  98. {
  99. ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle);
  100. ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle);
  101. if (nameString != 0 && nameString[0] != 0)
  102. {
  103. const String all ((const char*) nameString + 1, nameString[0]);
  104. DBG ("name: "+ all);
  105. manufacturer = all.upToFirstOccurrenceOf (":", false, false).trim();
  106. name = all.fromFirstOccurrenceOf (":", false, false).trim();
  107. }
  108. if (infoString != 0 && infoString[0] != 0)
  109. {
  110. DBG ("info: " + String ((const char*) infoString + 1, infoString[0]));
  111. }
  112. if (name.isEmpty())
  113. name = "<Unknown>";
  114. }
  115. DisposeHandle (componentNameHandle);
  116. DisposeHandle (componentInfoHandle);
  117. }
  118. }
  119. bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc,
  120. String& name, String& version, String& manufacturer)
  121. {
  122. zerostruct (desc);
  123. if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix))
  124. {
  125. String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'),
  126. fileOrIdentifier.lastIndexOfChar ('/')) + 1));
  127. StringArray tokens;
  128. tokens.addTokens (s, ",", String::empty);
  129. tokens.trim();
  130. tokens.removeEmptyStrings();
  131. if (tokens.size() == 3)
  132. {
  133. desc.componentType = stringToOSType (tokens[0]);
  134. desc.componentSubType = stringToOSType (tokens[1]);
  135. desc.componentManufacturer = stringToOSType (tokens[2]);
  136. ComponentRecord* comp = FindNextComponent (0, &desc);
  137. if (comp != 0)
  138. {
  139. getAUDetails (comp, name, manufacturer);
  140. return true;
  141. }
  142. }
  143. }
  144. return false;
  145. }
  146. }
  147. //==============================================================================
  148. class AudioUnitPluginWindowCarbon;
  149. class AudioUnitPluginWindowCocoa;
  150. //==============================================================================
  151. class AudioUnitPluginInstance : public AudioPluginInstance
  152. {
  153. public:
  154. //==============================================================================
  155. ~AudioUnitPluginInstance();
  156. void initialise();
  157. //==============================================================================
  158. // AudioPluginInstance methods:
  159. void fillInPluginDescription (PluginDescription& desc) const
  160. {
  161. desc.name = pluginName;
  162. desc.descriptiveName = pluginName;
  163. desc.fileOrIdentifier = AudioUnitFormatHelpers::createAUPluginIdentifier (componentDesc);
  164. desc.uid = ((int) componentDesc.componentType)
  165. ^ ((int) componentDesc.componentSubType)
  166. ^ ((int) componentDesc.componentManufacturer);
  167. desc.lastFileModTime = Time();
  168. desc.pluginFormatName = "AudioUnit";
  169. desc.category = getCategory();
  170. desc.manufacturerName = manufacturer;
  171. desc.version = version;
  172. desc.numInputChannels = getNumInputChannels();
  173. desc.numOutputChannels = getNumOutputChannels();
  174. desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice);
  175. }
  176. void* getPlatformSpecificData() { return audioUnit; }
  177. const String getName() const { return pluginName; }
  178. bool acceptsMidi() const { return wantsMidiMessages; }
  179. bool producesMidi() const { return false; }
  180. //==============================================================================
  181. // AudioProcessor methods:
  182. void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock);
  183. void releaseResources();
  184. void processBlock (AudioSampleBuffer& buffer,
  185. MidiBuffer& midiMessages);
  186. bool hasEditor() const;
  187. AudioProcessorEditor* createEditor();
  188. const String getInputChannelName (int index) const;
  189. bool isInputChannelStereoPair (int index) const;
  190. const String getOutputChannelName (int index) const;
  191. bool isOutputChannelStereoPair (int index) const;
  192. //==============================================================================
  193. int getNumParameters();
  194. float getParameter (int index);
  195. void setParameter (int index, float newValue);
  196. const String getParameterName (int index);
  197. const String getParameterText (int index);
  198. bool isParameterAutomatable (int index) const;
  199. //==============================================================================
  200. int getNumPrograms();
  201. int getCurrentProgram();
  202. void setCurrentProgram (int index);
  203. const String getProgramName (int index);
  204. void changeProgramName (int index, const String& newName);
  205. //==============================================================================
  206. void getStateInformation (MemoryBlock& destData);
  207. void getCurrentProgramStateInformation (MemoryBlock& destData);
  208. void setStateInformation (const void* data, int sizeInBytes);
  209. void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
  210. void refreshParameterListFromPlugin();
  211. private:
  212. //==============================================================================
  213. friend class AudioUnitPluginWindowCarbon;
  214. friend class AudioUnitPluginWindowCocoa;
  215. friend class AudioUnitPluginFormat;
  216. ComponentDescription componentDesc;
  217. String pluginName, manufacturer, version;
  218. String fileOrIdentifier;
  219. CriticalSection lock;
  220. bool wantsMidiMessages, wasPlaying, prepared;
  221. HeapBlock <AudioBufferList> outputBufferList;
  222. AudioTimeStamp timeStamp;
  223. AudioSampleBuffer* currentBuffer;
  224. AudioUnit audioUnit;
  225. Array <int> parameterIds;
  226. //==============================================================================
  227. bool getComponentDescFromFile (const String& fileOrIdentifier);
  228. void setPluginCallbacks();
  229. //==============================================================================
  230. OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
  231. const AudioTimeStamp* inTimeStamp,
  232. UInt32 inBusNumber,
  233. UInt32 inNumberFrames,
  234. AudioBufferList* ioData) const;
  235. static OSStatus renderGetInputCallback (void* inRefCon,
  236. AudioUnitRenderActionFlags* ioActionFlags,
  237. const AudioTimeStamp* inTimeStamp,
  238. UInt32 inBusNumber,
  239. UInt32 inNumberFrames,
  240. AudioBufferList* ioData)
  241. {
  242. return ((AudioUnitPluginInstance*) inRefCon)
  243. ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
  244. }
  245. OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const;
  246. OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
  247. UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const;
  248. OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged,
  249. Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
  250. Float64* outCycleStartBeat, Float64* outCycleEndBeat);
  251. static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo)
  252. {
  253. return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo);
  254. }
  255. static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat,
  256. Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator,
  257. Float64* outCurrentMeasureDownBeat)
  258. {
  259. return ((AudioUnitPluginInstance*) inHostUserData)
  260. ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator,
  261. outTimeSig_Denominator, outCurrentMeasureDownBeat);
  262. }
  263. static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged,
  264. Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
  265. Float64* outCycleStartBeat, Float64* outCycleEndBeat)
  266. {
  267. return ((AudioUnitPluginInstance*) inHostUserData)
  268. ->getTransportState (outIsPlaying, outTransportStateChanged,
  269. outCurrentSampleInTimeLine, outIsCycling,
  270. outCycleStartBeat, outCycleEndBeat);
  271. }
  272. //==============================================================================
  273. void getNumChannels (int& numIns, int& numOuts)
  274. {
  275. numIns = 0;
  276. numOuts = 0;
  277. AUChannelInfo supportedChannels [128];
  278. UInt32 supportedChannelsSize = sizeof (supportedChannels);
  279. if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global,
  280. 0, supportedChannels, &supportedChannelsSize) == noErr
  281. && supportedChannelsSize > 0)
  282. {
  283. int explicitNumIns = 0;
  284. int explicitNumOuts = 0;
  285. int maximumNumIns = 0;
  286. int maximumNumOuts = 0;
  287. for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i)
  288. {
  289. const int inChannels = (int) supportedChannels[i].inChannels;
  290. const int outChannels = (int) supportedChannels[i].outChannels;
  291. if (inChannels < 0)
  292. maximumNumIns = jmin (maximumNumIns, inChannels);
  293. else
  294. explicitNumIns = jmax (explicitNumIns, inChannels);
  295. if (outChannels < 0)
  296. maximumNumOuts = jmin (maximumNumOuts, outChannels);
  297. else
  298. explicitNumOuts = jmax (explicitNumOuts, outChannels);
  299. }
  300. if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match)
  301. || (maximumNumIns == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match)
  302. || (maximumNumIns == -1 && maximumNumOuts == -2))
  303. {
  304. numIns = numOuts = 2;
  305. }
  306. else
  307. {
  308. numIns = explicitNumIns;
  309. numOuts = explicitNumOuts;
  310. if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns))
  311. numIns = 2;
  312. if (maximumNumOuts == -1 || (maximumNumOuts < 0 && explicitNumOuts <= -maximumNumOuts))
  313. numOuts = 2;
  314. }
  315. }
  316. else
  317. {
  318. // (this really means the plugin will take any number of ins/outs as long
  319. // as they are the same)
  320. numIns = numOuts = 2;
  321. }
  322. }
  323. const String getCategory() const;
  324. //==============================================================================
  325. AudioUnitPluginInstance (const String& fileOrIdentifier);
  326. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance);
  327. };
  328. //==============================================================================
  329. AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier)
  330. : fileOrIdentifier (fileOrIdentifier),
  331. wantsMidiMessages (false), wasPlaying (false), prepared (false),
  332. currentBuffer (nullptr),
  333. audioUnit (0)
  334. {
  335. using namespace AudioUnitFormatHelpers;
  336. try
  337. {
  338. ++insideCallback;
  339. log ("Opening AU: " + fileOrIdentifier);
  340. if (getComponentDescFromFile (fileOrIdentifier))
  341. {
  342. ComponentRecord* const comp = FindNextComponent (0, &componentDesc);
  343. if (comp != 0)
  344. {
  345. audioUnit = (AudioUnit) OpenComponent (comp);
  346. wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice
  347. || componentDesc.componentType == kAudioUnitType_MusicEffect;
  348. }
  349. }
  350. --insideCallback;
  351. }
  352. catch (...)
  353. {
  354. --insideCallback;
  355. }
  356. }
  357. AudioUnitPluginInstance::~AudioUnitPluginInstance()
  358. {
  359. const ScopedLock sl (lock);
  360. jassert (AudioUnitFormatHelpers::insideCallback == 0);
  361. if (audioUnit != 0)
  362. {
  363. AudioUnitUninitialize (audioUnit);
  364. CloseComponent (audioUnit);
  365. audioUnit = 0;
  366. }
  367. }
  368. bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIdentifier)
  369. {
  370. zerostruct (componentDesc);
  371. if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer))
  372. return true;
  373. const File file (fileOrIdentifier);
  374. if (! file.hasFileExtension (".component"))
  375. return false;
  376. const char* const utf8 = fileOrIdentifier.toUTF8();
  377. CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8,
  378. strlen (utf8), file.isDirectory());
  379. if (url != 0)
  380. {
  381. CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url);
  382. CFRelease (url);
  383. if (bundleRef != 0)
  384. {
  385. CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"));
  386. if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID())
  387. pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name);
  388. if (pluginName.isEmpty())
  389. pluginName = file.getFileNameWithoutExtension();
  390. CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion"));
  391. if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID())
  392. version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString);
  393. CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString"));
  394. if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID())
  395. manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString);
  396. short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
  397. UseResFile (resFileId);
  398. for (int i = 1; i <= Count1Resources ('thng'); ++i)
  399. {
  400. Handle h = Get1IndResource ('thng', i);
  401. if (h != 0)
  402. {
  403. HLock (h);
  404. const uint32* const types = (const uint32*) *h;
  405. if (types[0] == kAudioUnitType_MusicDevice
  406. || types[0] == kAudioUnitType_MusicEffect
  407. || types[0] == kAudioUnitType_Effect
  408. || types[0] == kAudioUnitType_Generator
  409. || types[0] == kAudioUnitType_Panner)
  410. {
  411. componentDesc.componentType = types[0];
  412. componentDesc.componentSubType = types[1];
  413. componentDesc.componentManufacturer = types[2];
  414. break;
  415. }
  416. HUnlock (h);
  417. ReleaseResource (h);
  418. }
  419. }
  420. CFBundleCloseBundleResourceMap (bundleRef, resFileId);
  421. CFRelease (bundleRef);
  422. }
  423. }
  424. return componentDesc.componentType != 0 && componentDesc.componentSubType != 0;
  425. }
  426. //==============================================================================
  427. void AudioUnitPluginInstance::initialise()
  428. {
  429. refreshParameterListFromPlugin();
  430. setPluginCallbacks();
  431. int numIns, numOuts;
  432. getNumChannels (numIns, numOuts);
  433. setPlayConfigDetails (numIns, numOuts, 0, 0);
  434. setLatencySamples (0);
  435. }
  436. void AudioUnitPluginInstance::refreshParameterListFromPlugin()
  437. {
  438. parameterIds.clear();
  439. if (audioUnit != 0)
  440. {
  441. UInt32 paramListSize = 0;
  442. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
  443. 0, 0, &paramListSize);
  444. if (paramListSize > 0)
  445. {
  446. parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int));
  447. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
  448. 0, parameterIds.getRawDataPointer(), &paramListSize);
  449. }
  450. }
  451. }
  452. void AudioUnitPluginInstance::setPluginCallbacks()
  453. {
  454. if (audioUnit != 0)
  455. {
  456. {
  457. AURenderCallbackStruct info = { 0 };
  458. info.inputProcRefCon = this;
  459. info.inputProc = renderGetInputCallback;
  460. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
  461. 0, &info, sizeof (info));
  462. }
  463. {
  464. HostCallbackInfo info = { 0 };
  465. info.hostUserData = this;
  466. info.beatAndTempoProc = getBeatAndTempoCallback;
  467. info.musicalTimeLocationProc = getMusicalTimeLocationCallback;
  468. info.transportStateProc = getTransportStateCallback;
  469. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global,
  470. 0, &info, sizeof (info));
  471. }
  472. }
  473. }
  474. //==============================================================================
  475. void AudioUnitPluginInstance::prepareToPlay (double sampleRate_,
  476. int samplesPerBlockExpected)
  477. {
  478. if (audioUnit != 0)
  479. {
  480. releaseResources();
  481. Float64 sampleRateIn = 0, sampleRateOut = 0;
  482. UInt32 sampleRateSize = sizeof (sampleRateIn);
  483. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize);
  484. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRateOut, &sampleRateSize);
  485. if (sampleRateIn != sampleRate_ || sampleRateOut != sampleRate_)
  486. {
  487. Float64 sr = sampleRate_;
  488. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (Float64));
  489. AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sr, sizeof (Float64));
  490. }
  491. int numIns, numOuts;
  492. getNumChannels (numIns, numOuts);
  493. setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected);
  494. Float64 latencySecs = 0.0;
  495. UInt32 latencySize = sizeof (latencySecs);
  496. AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global,
  497. 0, &latencySecs, &latencySize);
  498. setLatencySamples (roundToInt (latencySecs * sampleRate_));
  499. AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
  500. AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
  501. AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
  502. {
  503. AudioStreamBasicDescription stream = { 0 };
  504. stream.mSampleRate = sampleRate_;
  505. stream.mFormatID = kAudioFormatLinearPCM;
  506. stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
  507. stream.mFramesPerPacket = 1;
  508. stream.mBytesPerPacket = 4;
  509. stream.mBytesPerFrame = 4;
  510. stream.mBitsPerChannel = 32;
  511. stream.mChannelsPerFrame = numIns;
  512. OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
  513. 0, &stream, sizeof (stream));
  514. stream.mChannelsPerFrame = numOuts;
  515. err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output,
  516. 0, &stream, sizeof (stream));
  517. }
  518. outputBufferList.calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1), 1);
  519. outputBufferList->mNumberBuffers = numOuts;
  520. for (int i = numOuts; --i >= 0;)
  521. outputBufferList->mBuffers[i].mNumberChannels = 1;
  522. zerostruct (timeStamp);
  523. timeStamp.mSampleTime = 0;
  524. timeStamp.mHostTime = AudioGetCurrentHostTime();
  525. timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid;
  526. currentBuffer = 0;
  527. wasPlaying = false;
  528. prepared = (AudioUnitInitialize (audioUnit) == noErr);
  529. }
  530. }
  531. void AudioUnitPluginInstance::releaseResources()
  532. {
  533. if (prepared)
  534. {
  535. AudioUnitUninitialize (audioUnit);
  536. AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0);
  537. AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0);
  538. AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0);
  539. outputBufferList.free();
  540. currentBuffer = 0;
  541. prepared = false;
  542. }
  543. }
  544. OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags,
  545. const AudioTimeStamp* inTimeStamp,
  546. UInt32 inBusNumber,
  547. UInt32 inNumberFrames,
  548. AudioBufferList* ioData) const
  549. {
  550. if (inBusNumber == 0
  551. && currentBuffer != 0)
  552. {
  553. jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling
  554. for (int i = 0; i < ioData->mNumberBuffers; ++i)
  555. {
  556. if (i < currentBuffer->getNumChannels())
  557. {
  558. memcpy (ioData->mBuffers[i].mData,
  559. currentBuffer->getSampleData (i, 0),
  560. sizeof (float) * inNumberFrames);
  561. }
  562. else
  563. {
  564. zeromem (ioData->mBuffers[i].mData,
  565. sizeof (float) * inNumberFrames);
  566. }
  567. }
  568. }
  569. return noErr;
  570. }
  571. void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer,
  572. MidiBuffer& midiMessages)
  573. {
  574. const int numSamples = buffer.getNumSamples();
  575. if (prepared)
  576. {
  577. AudioUnitRenderActionFlags flags = 0;
  578. timeStamp.mHostTime = AudioGetCurrentHostTime();
  579. for (int i = getNumOutputChannels(); --i >= 0;)
  580. {
  581. outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples;
  582. outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0);
  583. }
  584. currentBuffer = &buffer;
  585. if (wantsMidiMessages)
  586. {
  587. const uint8* midiEventData;
  588. int midiEventSize, midiEventPosition;
  589. MidiBuffer::Iterator i (midiMessages);
  590. while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition))
  591. {
  592. if (midiEventSize <= 3)
  593. MusicDeviceMIDIEvent (audioUnit,
  594. midiEventData[0], midiEventData[1], midiEventData[2],
  595. midiEventPosition);
  596. else
  597. MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize);
  598. }
  599. midiMessages.clear();
  600. }
  601. AudioUnitRender (audioUnit, &flags, &timeStamp,
  602. 0, numSamples, outputBufferList);
  603. timeStamp.mSampleTime += numSamples;
  604. }
  605. else
  606. {
  607. // Plugin not working correctly, so just bypass..
  608. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
  609. buffer.clear (i, 0, buffer.getNumSamples());
  610. }
  611. }
  612. //==============================================================================
  613. OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const
  614. {
  615. AudioPlayHead* const ph = getPlayHead();
  616. AudioPlayHead::CurrentPositionInfo result;
  617. if (ph != nullptr && ph->getCurrentPosition (result))
  618. {
  619. if (outCurrentBeat != nullptr)
  620. *outCurrentBeat = result.ppqPosition;
  621. if (outCurrentTempo != nullptr)
  622. *outCurrentTempo = result.bpm;
  623. }
  624. else
  625. {
  626. if (outCurrentBeat != nullptr)
  627. *outCurrentBeat = 0;
  628. if (outCurrentTempo != nullptr)
  629. *outCurrentTempo = 120.0;
  630. }
  631. return noErr;
  632. }
  633. OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat,
  634. Float32* outTimeSig_Numerator,
  635. UInt32* outTimeSig_Denominator,
  636. Float64* outCurrentMeasureDownBeat) const
  637. {
  638. AudioPlayHead* const ph = getPlayHead();
  639. AudioPlayHead::CurrentPositionInfo result;
  640. if (ph != nullptr && ph->getCurrentPosition (result))
  641. {
  642. if (outTimeSig_Numerator != nullptr)
  643. *outTimeSig_Numerator = result.timeSigNumerator;
  644. if (outTimeSig_Denominator != nullptr)
  645. *outTimeSig_Denominator = result.timeSigDenominator;
  646. if (outDeltaSampleOffsetToNextBeat != nullptr)
  647. *outDeltaSampleOffsetToNextBeat = 0; //xxx
  648. if (outCurrentMeasureDownBeat != nullptr)
  649. *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong
  650. }
  651. else
  652. {
  653. if (outDeltaSampleOffsetToNextBeat != nullptr)
  654. *outDeltaSampleOffsetToNextBeat = 0;
  655. if (outTimeSig_Numerator != nullptr)
  656. *outTimeSig_Numerator = 4;
  657. if (outTimeSig_Denominator != nullptr)
  658. *outTimeSig_Denominator = 4;
  659. if (outCurrentMeasureDownBeat != nullptr)
  660. *outCurrentMeasureDownBeat = 0;
  661. }
  662. return noErr;
  663. }
  664. OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying,
  665. Boolean* outTransportStateChanged,
  666. Float64* outCurrentSampleInTimeLine,
  667. Boolean* outIsCycling,
  668. Float64* outCycleStartBeat,
  669. Float64* outCycleEndBeat)
  670. {
  671. AudioPlayHead* const ph = getPlayHead();
  672. AudioPlayHead::CurrentPositionInfo result;
  673. if (ph != nullptr && ph->getCurrentPosition (result))
  674. {
  675. if (outIsPlaying != nullptr)
  676. *outIsPlaying = result.isPlaying;
  677. if (outTransportStateChanged != nullptr)
  678. {
  679. *outTransportStateChanged = result.isPlaying != wasPlaying;
  680. wasPlaying = result.isPlaying;
  681. }
  682. if (outCurrentSampleInTimeLine != nullptr)
  683. *outCurrentSampleInTimeLine = roundToInt (result.timeInSeconds * getSampleRate());
  684. if (outIsCycling != nullptr)
  685. *outIsCycling = false;
  686. if (outCycleStartBeat != nullptr)
  687. *outCycleStartBeat = 0;
  688. if (outCycleEndBeat != nullptr)
  689. *outCycleEndBeat = 0;
  690. }
  691. else
  692. {
  693. if (outIsPlaying != nullptr)
  694. *outIsPlaying = false;
  695. if (outTransportStateChanged != nullptr)
  696. *outTransportStateChanged = false;
  697. if (outCurrentSampleInTimeLine != nullptr)
  698. *outCurrentSampleInTimeLine = 0;
  699. if (outIsCycling != nullptr)
  700. *outIsCycling = false;
  701. if (outCycleStartBeat != nullptr)
  702. *outCycleStartBeat = 0;
  703. if (outCycleEndBeat != nullptr)
  704. *outCycleEndBeat = 0;
  705. }
  706. return noErr;
  707. }
  708. //==============================================================================
  709. class AudioUnitPluginWindowCocoa : public AudioProcessorEditor,
  710. public Timer
  711. {
  712. public:
  713. AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& plugin_, const bool createGenericViewIfNeeded)
  714. : AudioProcessorEditor (&plugin_),
  715. plugin (plugin_)
  716. {
  717. addAndMakeVisible (&wrapper);
  718. setOpaque (true);
  719. setVisible (true);
  720. setSize (100, 100);
  721. createView (createGenericViewIfNeeded);
  722. }
  723. ~AudioUnitPluginWindowCocoa()
  724. {
  725. const bool wasValid = isValid();
  726. wrapper.setView (0);
  727. if (wasValid)
  728. plugin.editorBeingDeleted (this);
  729. }
  730. bool isValid() const { return wrapper.getView() != 0; }
  731. void paint (Graphics& g)
  732. {
  733. g.fillAll (Colours::white);
  734. }
  735. void resized()
  736. {
  737. wrapper.setSize (getWidth(), getHeight());
  738. }
  739. void timerCallback()
  740. {
  741. wrapper.resizeToFitView();
  742. startTimer (jmin (713, getTimerInterval() + 51));
  743. }
  744. void childBoundsChanged (Component* child)
  745. {
  746. setSize (wrapper.getWidth(), wrapper.getHeight());
  747. startTimer (70);
  748. }
  749. private:
  750. AudioUnitPluginInstance& plugin;
  751. NSViewComponent wrapper;
  752. bool createView (const bool createGenericViewIfNeeded)
  753. {
  754. NSView* pluginView = nil;
  755. UInt32 dataSize = 0;
  756. Boolean isWritable = false;
  757. AudioUnitInitialize (plugin.audioUnit);
  758. if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
  759. 0, &dataSize, &isWritable) == noErr
  760. && dataSize != 0
  761. && AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
  762. 0, &dataSize, &isWritable) == noErr)
  763. {
  764. HeapBlock <AudioUnitCocoaViewInfo> info;
  765. info.calloc (dataSize, 1);
  766. if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
  767. 0, info, &dataSize) == noErr)
  768. {
  769. NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]);
  770. NSString* path = (NSString*) CFURLCopyPath (info->mCocoaAUViewBundleLocation);
  771. NSBundle* viewBundle = [NSBundle bundleWithPath: [path autorelease]];
  772. Class viewClass = [viewBundle classNamed: viewClassName];
  773. if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)]
  774. && [viewClass instancesRespondToSelector: @selector (interfaceVersion)]
  775. && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)])
  776. {
  777. id factory = [[[viewClass alloc] init] autorelease];
  778. pluginView = [factory uiViewForAudioUnit: plugin.audioUnit
  779. withSize: NSMakeSize (getWidth(), getHeight())];
  780. }
  781. for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;)
  782. CFRelease (info->mCocoaAUViewClass[i]);
  783. CFRelease (info->mCocoaAUViewBundleLocation);
  784. }
  785. }
  786. if (createGenericViewIfNeeded && (pluginView == 0))
  787. pluginView = [[AUGenericView alloc] initWithAudioUnit: plugin.audioUnit];
  788. wrapper.setView (pluginView);
  789. if (pluginView != nil)
  790. {
  791. timerCallback();
  792. startTimer (70);
  793. }
  794. return pluginView != nil;
  795. }
  796. };
  797. #if JUCE_SUPPORT_CARBON
  798. //==============================================================================
  799. class AudioUnitPluginWindowCarbon : public AudioProcessorEditor
  800. {
  801. public:
  802. //==============================================================================
  803. AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& plugin_)
  804. : AudioProcessorEditor (&plugin_),
  805. plugin (plugin_),
  806. viewComponent (0)
  807. {
  808. addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this));
  809. setOpaque (true);
  810. setVisible (true);
  811. setSize (400, 300);
  812. ComponentDescription viewList [16];
  813. UInt32 viewListSize = sizeof (viewList);
  814. AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global,
  815. 0, &viewList, &viewListSize);
  816. componentRecord = FindNextComponent (0, &viewList[0]);
  817. }
  818. ~AudioUnitPluginWindowCarbon()
  819. {
  820. innerWrapper = 0;
  821. if (isValid())
  822. plugin.editorBeingDeleted (this);
  823. }
  824. bool isValid() const noexcept { return componentRecord != 0; }
  825. //==============================================================================
  826. void paint (Graphics& g)
  827. {
  828. g.fillAll (Colours::black);
  829. }
  830. void resized()
  831. {
  832. innerWrapper->setSize (getWidth(), getHeight());
  833. }
  834. //==============================================================================
  835. bool keyStateChanged (bool)
  836. {
  837. return false;
  838. }
  839. bool keyPressed (const KeyPress&)
  840. {
  841. return false;
  842. }
  843. //==============================================================================
  844. AudioUnit getAudioUnit() const { return plugin.audioUnit; }
  845. AudioUnitCarbonView getViewComponent()
  846. {
  847. if (viewComponent == 0 && componentRecord != 0)
  848. viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord);
  849. return viewComponent;
  850. }
  851. void closeViewComponent()
  852. {
  853. if (viewComponent != 0)
  854. {
  855. log ("Closing AU GUI: " + plugin.getName());
  856. CloseComponent (viewComponent);
  857. viewComponent = 0;
  858. }
  859. }
  860. private:
  861. //==============================================================================
  862. AudioUnitPluginInstance& plugin;
  863. ComponentRecord* componentRecord;
  864. AudioUnitCarbonView viewComponent;
  865. //==============================================================================
  866. class InnerWrapperComponent : public CarbonViewWrapperComponent
  867. {
  868. public:
  869. InnerWrapperComponent (AudioUnitPluginWindowCarbon* const owner_)
  870. : owner (owner_)
  871. {
  872. }
  873. ~InnerWrapperComponent()
  874. {
  875. deleteWindow();
  876. }
  877. HIViewRef attachView (WindowRef windowRef, HIViewRef rootView)
  878. {
  879. log ("Opening AU GUI: " + owner->plugin.getName());
  880. AudioUnitCarbonView viewComponent = owner->getViewComponent();
  881. if (viewComponent == 0)
  882. return 0;
  883. Float32Point pos = { 0, 0 };
  884. Float32Point size = { 250, 200 };
  885. HIViewRef pluginView = 0;
  886. AudioUnitCarbonViewCreate (viewComponent,
  887. owner->getAudioUnit(),
  888. windowRef,
  889. rootView,
  890. &pos,
  891. &size,
  892. (ControlRef*) &pluginView);
  893. return pluginView;
  894. }
  895. void removeView (HIViewRef)
  896. {
  897. owner->closeViewComponent();
  898. }
  899. private:
  900. AudioUnitPluginWindowCarbon* const owner;
  901. };
  902. friend class InnerWrapperComponent;
  903. ScopedPointer<InnerWrapperComponent> innerWrapper;
  904. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginWindowCarbon);
  905. };
  906. #endif
  907. //==============================================================================
  908. bool AudioUnitPluginInstance::hasEditor() const
  909. {
  910. return true;
  911. }
  912. AudioProcessorEditor* AudioUnitPluginInstance::createEditor()
  913. {
  914. ScopedPointer<AudioProcessorEditor> w (new AudioUnitPluginWindowCocoa (*this, false));
  915. if (! static_cast <AudioUnitPluginWindowCocoa*> (static_cast <AudioProcessorEditor*> (w))->isValid())
  916. w = 0;
  917. #if JUCE_SUPPORT_CARBON
  918. if (w == nullptr)
  919. {
  920. w = new AudioUnitPluginWindowCarbon (*this);
  921. if (! static_cast <AudioUnitPluginWindowCarbon*> (static_cast <AudioProcessorEditor*> (w))->isValid())
  922. w = 0;
  923. }
  924. #endif
  925. if (w == nullptr)
  926. w = new AudioUnitPluginWindowCocoa (*this, true); // use AUGenericView as a fallback
  927. return w.release();
  928. }
  929. //==============================================================================
  930. const String AudioUnitPluginInstance::getCategory() const
  931. {
  932. const char* result = nullptr;
  933. switch (componentDesc.componentType)
  934. {
  935. case kAudioUnitType_Effect:
  936. case kAudioUnitType_MusicEffect: result = "Effect"; break;
  937. case kAudioUnitType_MusicDevice: result = "Synth"; break;
  938. case kAudioUnitType_Generator: result = "Generator"; break;
  939. case kAudioUnitType_Panner: result = "Panner"; break;
  940. default: break;
  941. }
  942. return result;
  943. }
  944. //==============================================================================
  945. int AudioUnitPluginInstance::getNumParameters()
  946. {
  947. return parameterIds.size();
  948. }
  949. float AudioUnitPluginInstance::getParameter (int index)
  950. {
  951. const ScopedLock sl (lock);
  952. Float32 value = 0.0f;
  953. if (audioUnit != 0 && isPositiveAndBelow (index, parameterIds.size()))
  954. {
  955. AudioUnitGetParameter (audioUnit,
  956. (UInt32) parameterIds.getUnchecked (index),
  957. kAudioUnitScope_Global, 0,
  958. &value);
  959. }
  960. return value;
  961. }
  962. void AudioUnitPluginInstance::setParameter (int index, float newValue)
  963. {
  964. const ScopedLock sl (lock);
  965. if (audioUnit != 0 && isPositiveAndBelow (index, parameterIds.size()))
  966. {
  967. AudioUnitSetParameter (audioUnit,
  968. (UInt32) parameterIds.getUnchecked (index),
  969. kAudioUnitScope_Global, 0,
  970. newValue, 0);
  971. }
  972. }
  973. const String AudioUnitPluginInstance::getParameterName (int index)
  974. {
  975. AudioUnitParameterInfo info = { 0 };
  976. UInt32 sz = sizeof (info);
  977. String name;
  978. if (AudioUnitGetProperty (audioUnit,
  979. kAudioUnitProperty_ParameterInfo,
  980. kAudioUnitScope_Global,
  981. parameterIds [index], &info, &sz) == noErr)
  982. {
  983. if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0)
  984. name = PlatformUtilities::cfStringToJuceString (info.cfNameString);
  985. else
  986. name = String (info.name, sizeof (info.name));
  987. }
  988. return name;
  989. }
  990. const String AudioUnitPluginInstance::getParameterText (int index)
  991. {
  992. return String (getParameter (index));
  993. }
  994. bool AudioUnitPluginInstance::isParameterAutomatable (int index) const
  995. {
  996. AudioUnitParameterInfo info;
  997. UInt32 sz = sizeof (info);
  998. if (AudioUnitGetProperty (audioUnit,
  999. kAudioUnitProperty_ParameterInfo,
  1000. kAudioUnitScope_Global,
  1001. parameterIds [index], &info, &sz) == noErr)
  1002. {
  1003. return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
  1004. }
  1005. return true;
  1006. }
  1007. //==============================================================================
  1008. int AudioUnitPluginInstance::getNumPrograms()
  1009. {
  1010. CFArrayRef presets;
  1011. UInt32 sz = sizeof (CFArrayRef);
  1012. int num = 0;
  1013. if (AudioUnitGetProperty (audioUnit,
  1014. kAudioUnitProperty_FactoryPresets,
  1015. kAudioUnitScope_Global,
  1016. 0, &presets, &sz) == noErr)
  1017. {
  1018. num = (int) CFArrayGetCount (presets);
  1019. CFRelease (presets);
  1020. }
  1021. return num;
  1022. }
  1023. int AudioUnitPluginInstance::getCurrentProgram()
  1024. {
  1025. AUPreset current;
  1026. current.presetNumber = 0;
  1027. UInt32 sz = sizeof (AUPreset);
  1028. AudioUnitGetProperty (audioUnit,
  1029. kAudioUnitProperty_FactoryPresets,
  1030. kAudioUnitScope_Global,
  1031. 0, &current, &sz);
  1032. return current.presetNumber;
  1033. }
  1034. void AudioUnitPluginInstance::setCurrentProgram (int newIndex)
  1035. {
  1036. AUPreset current;
  1037. current.presetNumber = newIndex;
  1038. current.presetName = 0;
  1039. AudioUnitSetProperty (audioUnit,
  1040. kAudioUnitProperty_PresentPreset,
  1041. kAudioUnitScope_Global,
  1042. 0, &current, sizeof (AUPreset));
  1043. }
  1044. const String AudioUnitPluginInstance::getProgramName (int index)
  1045. {
  1046. String s;
  1047. CFArrayRef presets;
  1048. UInt32 sz = sizeof (CFArrayRef);
  1049. if (AudioUnitGetProperty (audioUnit,
  1050. kAudioUnitProperty_FactoryPresets,
  1051. kAudioUnitScope_Global,
  1052. 0, &presets, &sz) == noErr)
  1053. {
  1054. for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i)
  1055. {
  1056. const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i);
  1057. if (p != nullptr && p->presetNumber == index)
  1058. {
  1059. s = PlatformUtilities::cfStringToJuceString (p->presetName);
  1060. break;
  1061. }
  1062. }
  1063. CFRelease (presets);
  1064. }
  1065. return s;
  1066. }
  1067. void AudioUnitPluginInstance::changeProgramName (int index, const String& newName)
  1068. {
  1069. jassertfalse; // xxx not implemented!
  1070. }
  1071. //==============================================================================
  1072. const String AudioUnitPluginInstance::getInputChannelName (int index) const
  1073. {
  1074. if (isPositiveAndBelow (index, getNumInputChannels()))
  1075. return "Input " + String (index + 1);
  1076. return String::empty;
  1077. }
  1078. bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const
  1079. {
  1080. if (! isPositiveAndBelow (index, getNumInputChannels()))
  1081. return false;
  1082. return true;
  1083. }
  1084. const String AudioUnitPluginInstance::getOutputChannelName (int index) const
  1085. {
  1086. if (isPositiveAndBelow (index, getNumOutputChannels()))
  1087. return "Output " + String (index + 1);
  1088. return String::empty;
  1089. }
  1090. bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const
  1091. {
  1092. if (! isPositiveAndBelow (index, getNumOutputChannels()))
  1093. return false;
  1094. return true;
  1095. }
  1096. //==============================================================================
  1097. void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData)
  1098. {
  1099. getCurrentProgramStateInformation (destData);
  1100. }
  1101. void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData)
  1102. {
  1103. CFPropertyListRef propertyList = 0;
  1104. UInt32 sz = sizeof (CFPropertyListRef);
  1105. if (AudioUnitGetProperty (audioUnit,
  1106. kAudioUnitProperty_ClassInfo,
  1107. kAudioUnitScope_Global,
  1108. 0, &propertyList, &sz) == noErr)
  1109. {
  1110. CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault);
  1111. CFWriteStreamOpen (stream);
  1112. CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0);
  1113. CFWriteStreamClose (stream);
  1114. CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten);
  1115. destData.setSize (bytesWritten);
  1116. destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize());
  1117. CFRelease (data);
  1118. CFRelease (stream);
  1119. CFRelease (propertyList);
  1120. }
  1121. }
  1122. void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes)
  1123. {
  1124. setCurrentProgramStateInformation (data, sizeInBytes);
  1125. }
  1126. void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes)
  1127. {
  1128. CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault,
  1129. (const UInt8*) data,
  1130. sizeInBytes,
  1131. kCFAllocatorNull);
  1132. CFReadStreamOpen (stream);
  1133. CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0;
  1134. CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault,
  1135. stream,
  1136. 0,
  1137. kCFPropertyListImmutable,
  1138. &format,
  1139. 0);
  1140. CFRelease (stream);
  1141. if (propertyList != 0)
  1142. AudioUnitSetProperty (audioUnit,
  1143. kAudioUnitProperty_ClassInfo,
  1144. kAudioUnitScope_Global,
  1145. 0, &propertyList, sizeof (propertyList));
  1146. }
  1147. //==============================================================================
  1148. //==============================================================================
  1149. AudioUnitPluginFormat::AudioUnitPluginFormat()
  1150. {
  1151. }
  1152. AudioUnitPluginFormat::~AudioUnitPluginFormat()
  1153. {
  1154. }
  1155. void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& results,
  1156. const String& fileOrIdentifier)
  1157. {
  1158. if (! fileMightContainThisPluginType (fileOrIdentifier))
  1159. return;
  1160. PluginDescription desc;
  1161. desc.fileOrIdentifier = fileOrIdentifier;
  1162. desc.uid = 0;
  1163. try
  1164. {
  1165. ScopedPointer <AudioPluginInstance> createdInstance (createInstanceFromDescription (desc));
  1166. AudioUnitPluginInstance* const auInstance = dynamic_cast <AudioUnitPluginInstance*> ((AudioPluginInstance*) createdInstance);
  1167. if (auInstance != nullptr)
  1168. {
  1169. auInstance->fillInPluginDescription (desc);
  1170. results.add (new PluginDescription (desc));
  1171. }
  1172. }
  1173. catch (...)
  1174. {
  1175. // crashed while loading...
  1176. }
  1177. }
  1178. AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc)
  1179. {
  1180. if (fileMightContainThisPluginType (desc.fileOrIdentifier))
  1181. {
  1182. ScopedPointer <AudioUnitPluginInstance> result (new AudioUnitPluginInstance (desc.fileOrIdentifier));
  1183. if (result->audioUnit != 0)
  1184. {
  1185. result->initialise();
  1186. return result.release();
  1187. }
  1188. }
  1189. return nullptr;
  1190. }
  1191. const StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/,
  1192. const bool /*recursive*/)
  1193. {
  1194. StringArray result;
  1195. ComponentRecord* comp = 0;
  1196. for (;;)
  1197. {
  1198. ComponentDescription desc = { 0 };
  1199. comp = FindNextComponent (comp, &desc);
  1200. if (comp == 0)
  1201. break;
  1202. GetComponentInfo (comp, &desc, 0, 0, 0);
  1203. if (desc.componentType == kAudioUnitType_MusicDevice
  1204. || desc.componentType == kAudioUnitType_MusicEffect
  1205. || desc.componentType == kAudioUnitType_Effect
  1206. || desc.componentType == kAudioUnitType_Generator
  1207. || desc.componentType == kAudioUnitType_Panner)
  1208. {
  1209. const String s (AudioUnitFormatHelpers::createAUPluginIdentifier (desc));
  1210. DBG (s);
  1211. result.add (s);
  1212. }
  1213. }
  1214. return result;
  1215. }
  1216. bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
  1217. {
  1218. ComponentDescription desc;
  1219. String name, version, manufacturer;
  1220. if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer))
  1221. return FindNextComponent (0, &desc) != 0;
  1222. const File f (fileOrIdentifier);
  1223. return f.hasFileExtension (".component")
  1224. && f.isDirectory();
  1225. }
  1226. const String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier)
  1227. {
  1228. ComponentDescription desc;
  1229. String name, version, manufacturer;
  1230. AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer);
  1231. if (name.isEmpty())
  1232. name = fileOrIdentifier;
  1233. return name;
  1234. }
  1235. bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc)
  1236. {
  1237. if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix))
  1238. return fileMightContainThisPluginType (desc.fileOrIdentifier);
  1239. else
  1240. return File (desc.fileOrIdentifier).exists();
  1241. }
  1242. const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch()
  1243. {
  1244. return FileSearchPath ("/(Default AudioUnit locations)");
  1245. }
  1246. #endif
  1247. END_JUCE_NAMESPACE
  1248. #undef log
  1249. #endif