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.

646 lines
23KB

  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. // Your project must contain an AppConfig.h file with your project-specific settings in it,
  19. // and your header search path must make it accessible to the module's files.
  20. #include "AppConfig.h"
  21. #include "../utility/juce_CheckSettingMacros.h"
  22. #if JucePlugin_Build_AAX && (JUCE_INCLUDED_AAX_IN_MM || defined (_WIN32) || defined (_WIN64))
  23. #ifdef _MSC_VER
  24. #include <windows.h>
  25. #else
  26. #include <Cocoa/Cocoa.h>
  27. #endif
  28. #include "../utility/juce_IncludeModuleHeaders.h"
  29. #undef Component
  30. #ifdef __clang__
  31. #pragma clang diagnostic push
  32. #pragma clang diagnostic ignored "-Wnon-virtual-dtor"
  33. #endif
  34. #include "AAX_Exports.cpp"
  35. #include "AAX_ICollection.h"
  36. #include "AAX_IComponentDescriptor.h"
  37. #include "AAX_IEffectDescriptor.h"
  38. #include "AAX_IPropertyMap.h"
  39. #include "AAX_CEffectParameters.h"
  40. #include "AAX_Errors.h"
  41. #include "AAX_CBinaryTaperDelegate.h"
  42. #include "AAX_CBinaryDisplayDelegate.h"
  43. #include "AAX_CEffectGUI.h"
  44. #include "AAX_IViewContainer.h"
  45. #include "AAX_ITransport.h"
  46. #ifdef __clang__
  47. #pragma clang diagnostic pop
  48. #endif
  49. using juce::Component;
  50. const int32_t juceChunkType = 'juce';
  51. //==============================================================================
  52. struct AAXClasses
  53. {
  54. static void check (AAX_Result result)
  55. {
  56. jassert (result == AAX_SUCCESS); (void) result;
  57. }
  58. static AAX_EStemFormat getFormatForChans (const int numChans) noexcept
  59. {
  60. switch (numChans)
  61. {
  62. case 0: return AAX_eStemFormat_None;
  63. case 1: return AAX_eStemFormat_Mono;
  64. case 2: return AAX_eStemFormat_Stereo;
  65. case 3: return AAX_eStemFormat_LCR;
  66. case 4: return AAX_eStemFormat_Quad;
  67. case 5: return AAX_eStemFormat_5_0;
  68. case 6: return AAX_eStemFormat_5_1;
  69. case 7: return AAX_eStemFormat_6_1;
  70. case 8: return AAX_eStemFormat_7_1_DTS;
  71. default: jassertfalse; break; // hmm - not a valid number of chans..
  72. }
  73. return AAX_eStemFormat_None;
  74. }
  75. static int getNumChannelsForStemFormat (AAX_EStemFormat format) noexcept
  76. {
  77. switch (format)
  78. {
  79. case AAX_eStemFormat_None: return 0;
  80. case AAX_eStemFormat_Mono: return 1;
  81. case AAX_eStemFormat_Stereo: return 2;
  82. case AAX_eStemFormat_LCR: return 3;
  83. case AAX_eStemFormat_Quad: return 4;
  84. case AAX_eStemFormat_5_0: return 5;
  85. case AAX_eStemFormat_5_1: return 6;
  86. case AAX_eStemFormat_6_1: return 7;
  87. case AAX_eStemFormat_7_1_DTS: return 8;
  88. default: jassertfalse; break; // hmm - not a valid number of chans..
  89. }
  90. return 0;
  91. }
  92. //==============================================================================
  93. struct JUCELibraryRefCount
  94. {
  95. JUCELibraryRefCount() { if (getCount()++ == 0) initialise(); }
  96. ~JUCELibraryRefCount() { if (--getCount() == 0) shutdown(); }
  97. private:
  98. static void initialise()
  99. {
  100. initialiseJuce_GUI();
  101. }
  102. static void shutdown()
  103. {
  104. shutdownJuce_GUI();
  105. }
  106. int& getCount() noexcept
  107. {
  108. static int count = 0;
  109. return count;
  110. }
  111. };
  112. //==============================================================================
  113. struct PluginInstanceInfo
  114. {
  115. PluginInstanceInfo (AudioProcessor& p) : pluginInstance (p) {}
  116. void process (const float* const* inputs, float* const* outputs, const int bufferSize, const bool bypass)
  117. {
  118. const int numIns = pluginInstance.getNumInputChannels();
  119. const int numOuts = pluginInstance.getNumOutputChannels();
  120. if (numOuts >= numIns)
  121. {
  122. for (int i = 0; i < numIns; ++i)
  123. memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
  124. process (outputs, numOuts, bufferSize, bypass);
  125. }
  126. else
  127. {
  128. if (channelList.size() <= numIns)
  129. channelList.insertMultiple (-1, nullptr, 1 + numIns - channelList.size());
  130. float** channels = channelList.getRawDataPointer();
  131. for (int i = 0; i < numOuts; ++i)
  132. {
  133. memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
  134. channels[i] = outputs[i];
  135. }
  136. for (int i = numOuts; i < numIns; ++i)
  137. channels[i] = const_cast <float*> (inputs[i]);
  138. process (channels, numIns, bufferSize, bypass);
  139. }
  140. }
  141. void process (float* const* channels, const int numChans, const int bufferSize, const bool bypass)
  142. {
  143. AudioSampleBuffer buffer (channels, numChans, bufferSize);
  144. // XXX need to do midi..
  145. midiBuffer.clear();
  146. {
  147. const ScopedLock sl (pluginInstance.getCallbackLock());
  148. if (bypass)
  149. pluginInstance.processBlockBypassed (buffer, midiBuffer);
  150. else
  151. pluginInstance.processBlock (buffer, midiBuffer);
  152. }
  153. }
  154. AudioProcessor& pluginInstance;
  155. MidiBuffer midiBuffer;
  156. Array<float*> channelList;
  157. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginInstanceInfo)
  158. };
  159. //==============================================================================
  160. struct JUCEAlgorithmContext
  161. {
  162. float** inputChannels;
  163. float** outputChannels;
  164. int32_t* bufferSize;
  165. int32_t* bypass;
  166. PluginInstanceInfo* pluginInstance;
  167. int32_t* isPrepared;
  168. };
  169. struct JUCEAlgorithmIDs
  170. {
  171. enum
  172. {
  173. inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
  174. outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
  175. bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
  176. bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
  177. pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
  178. preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared)
  179. };
  180. };
  181. //==============================================================================
  182. class JuceAAX_GUI : public AAX_CEffectGUI
  183. {
  184. public:
  185. JuceAAX_GUI() {}
  186. virtual ~JuceAAX_GUI() { DeleteViewContainer(); }
  187. static AAX_IEffectGUI* AAX_CALLBACK Create() { return new JuceAAX_GUI(); }
  188. void CreateViewContents()
  189. {
  190. if (component == nullptr)
  191. {
  192. if (JuceAAX_Parameters* params = dynamic_cast <JuceAAX_Parameters*> (GetEffectParameters()))
  193. component = new ContentWrapperComponent (*this, params->getPluginInstance());
  194. else
  195. jassertfalse;
  196. }
  197. }
  198. void CreateViewContainer()
  199. {
  200. CreateViewContents();
  201. if (void* nativeViewToAttachTo = GetViewContainerPtr())
  202. {
  203. #if JUCE_MAC
  204. if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
  205. #else
  206. if (GetViewContainerType() == AAX_eViewContainer_Type_HWND)
  207. #endif
  208. {
  209. component->setVisible (true);
  210. component->addToDesktop (0, nativeViewToAttachTo);
  211. }
  212. }
  213. }
  214. void DeleteViewContainer()
  215. {
  216. if (component != nullptr)
  217. {
  218. JUCE_AUTORELEASEPOOL
  219. component->removeFromDesktop();
  220. component = nullptr;
  221. }
  222. }
  223. virtual AAX_Result GetViewSize (AAX_Point* const viewSize) const
  224. {
  225. if (component != nullptr)
  226. {
  227. viewSize->horz = (float) component->getWidth();
  228. viewSize->vert = (float) component->getHeight();
  229. return AAX_SUCCESS;
  230. }
  231. return AAX_ERROR_NULL_OBJECT;
  232. }
  233. AAX_Result ParameterUpdated (AAX_CParamID /*paramID*/)
  234. {
  235. return AAX_SUCCESS;
  236. }
  237. AAX_Result SetControlHighlightInfo (AAX_CParamID /*paramID*/, AAX_CBoolean /*isHighlighted*/, AAX_EHighlightColor)
  238. {
  239. return AAX_SUCCESS;
  240. }
  241. private:
  242. class ContentWrapperComponent : public juce::Component
  243. {
  244. public:
  245. ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin)
  246. : owner (gui)
  247. {
  248. setOpaque (true);
  249. addAndMakeVisible (pluginEditor = plugin.createEditorIfNeeded());
  250. setBounds (pluginEditor->getLocalBounds());
  251. setBroughtToFrontOnMouseClick (true);
  252. }
  253. ~ContentWrapperComponent()
  254. {
  255. if (pluginEditor != nullptr)
  256. {
  257. PopupMenu::dismissAllActiveMenus();
  258. pluginEditor->getAudioProcessor()->editorBeingDeleted (pluginEditor);
  259. }
  260. }
  261. void paint (Graphics& g)
  262. {
  263. g.fillAll (Colours::black);
  264. }
  265. void childBoundsChanged (Component*)
  266. {
  267. if (pluginEditor != nullptr)
  268. {
  269. const int w = pluginEditor->getWidth();
  270. const int h = pluginEditor->getHeight();
  271. setSize (w, h);
  272. AAX_Point newSize ((float) h, (float) w);
  273. owner.GetViewContainer()->SetViewSize (newSize);
  274. }
  275. }
  276. private:
  277. ScopedPointer<AudioProcessorEditor> pluginEditor;
  278. JuceAAX_GUI& owner;
  279. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent)
  280. };
  281. ScopedPointer<ContentWrapperComponent> component;
  282. JUCELibraryRefCount juceCount;
  283. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAAX_GUI)
  284. };
  285. //==============================================================================
  286. class JuceAAX_Parameters : public AAX_CEffectParameters,
  287. public juce::AudioPlayHead
  288. {
  289. public:
  290. JuceAAX_Parameters()
  291. {
  292. pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_AAX);
  293. pluginInstance->setPlayHead (this);
  294. AAX_CEffectParameters::GetNumberOfChunks (&juceChunkIndex);
  295. }
  296. static AAX_CEffectParameters* AAX_CALLBACK Create() { return new JuceAAX_Parameters(); }
  297. AAX_Result EffectInit()
  298. {
  299. addBypassParameter();
  300. preparePlugin();
  301. return AAX_SUCCESS;
  302. }
  303. AAX_Result GetNumberOfChunks (int32_t* numChunks) const
  304. {
  305. // The juceChunk is the last chunk.
  306. *numChunks = juceChunkIndex + 1;
  307. return AAX_SUCCESS;
  308. }
  309. AAX_Result GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const
  310. {
  311. if (index != juceChunkIndex)
  312. return AAX_CEffectParameters::GetChunkIDFromIndex (index, chunkID);
  313. *chunkID = juceChunkType;
  314. return AAX_SUCCESS;
  315. }
  316. AAX_Result GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const
  317. {
  318. if (chunkID != juceChunkType)
  319. return AAX_CEffectParameters::GetChunkSize (chunkID, oSize);
  320. tempFilterData.setSize (0);
  321. pluginInstance->getStateInformation (tempFilterData);
  322. *oSize = (uint32_t) tempFilterData.getSize();
  323. return AAX_SUCCESS;
  324. }
  325. AAX_Result GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const
  326. {
  327. if (chunkID != juceChunkType)
  328. return AAX_CEffectParameters::GetChunk (chunkID, oChunk);
  329. if (tempFilterData.getSize() == 0)
  330. pluginInstance->getStateInformation (tempFilterData);
  331. oChunk->fSize = (uint32_t) tempFilterData.getSize();
  332. tempFilterData.copyTo (oChunk->fData, 0, tempFilterData.getSize());
  333. tempFilterData.setSize (0);
  334. return AAX_SUCCESS;
  335. }
  336. AAX_Result SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* chunk)
  337. {
  338. if (chunkID != juceChunkType)
  339. return AAX_CEffectParameters::SetChunk (chunkID, chunk);
  340. pluginInstance->setStateInformation ((void*) chunk->fData, chunk->fSize);
  341. return AAX_SUCCESS;
  342. }
  343. AAX_Result ResetFieldData (AAX_CFieldIndex fieldIndex, void* data, uint32_t dataSize) const
  344. {
  345. switch (fieldIndex)
  346. {
  347. case JUCEAlgorithmIDs::pluginInstance:
  348. {
  349. const size_t numObjects = dataSize / sizeof (PluginInstanceInfo);
  350. PluginInstanceInfo* const objects = static_cast <PluginInstanceInfo*> (data);
  351. jassert (numObjects == 1); // not sure how to handle more than one..
  352. for (size_t i = 0; i < numObjects; ++i)
  353. new (objects + i) PluginInstanceInfo (*pluginInstance);
  354. break;
  355. }
  356. case JUCEAlgorithmIDs::preparedFlag:
  357. {
  358. preparePlugin();
  359. const size_t numObjects = dataSize / sizeof (uint32_t);
  360. uint32_t* const objects = static_cast <uint32_t*> (data);
  361. for (size_t i = 0; i < numObjects; ++i)
  362. new (objects + i) uint32_t (1);
  363. break;
  364. }
  365. }
  366. return AAX_SUCCESS;
  367. //return AAX_ERROR_INVALID_FIELD_INDEX;
  368. }
  369. AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
  370. bool getCurrentPosition (juce::AudioPlayHead::CurrentPositionInfo& info)
  371. {
  372. const AAX_ITransport& transport = *Transport();
  373. check (transport.GetCurrentTempo (&info.bpm));
  374. int32_t num, denom;
  375. transport.GetCurrentMeter (&num, &denom);
  376. info.timeSigNumerator = num;
  377. info.timeSigDenominator = denom;
  378. check (transport.GetCurrentNativeSampleLocation (&info.timeInSamples));
  379. info.timeInSeconds = info.timeInSamples / getSampleRate();
  380. int64_t ticks;
  381. check (transport.GetCurrentTickPosition (&ticks));
  382. info.ppqPosition = ticks / 960000.0;
  383. int64_t loopStartTick, loopEndTick;
  384. check (transport.GetCurrentLoopPosition (&info.isLooping, &loopStartTick, &loopEndTick));
  385. info.ppqLoopStart = loopStartTick / 960000.0;
  386. info.ppqLoopEnd = loopEndTick / 960000.0;
  387. // No way to get these: (?)
  388. info.isRecording = false;
  389. info.ppqPositionOfLastBarStart = 0;
  390. info.editOriginTime = 0;
  391. return true;
  392. }
  393. private:
  394. void addBypassParameter()
  395. {
  396. AAX_CString bypassID;
  397. GetMasterBypassParameter (&bypassID);
  398. AAX_IParameter* masterBypass = new AAX_CParameter<bool> (bypassID.CString(),
  399. AAX_CString ("Master Bypass"),
  400. false,
  401. AAX_CBinaryTaperDelegate<bool>(),
  402. AAX_CBinaryDisplayDelegate<bool> ("bypass", "on"),
  403. true);
  404. masterBypass->SetNumberOfSteps (2);
  405. masterBypass->SetType (AAX_eParameterType_Discrete);
  406. mParameterManager.AddParameter (masterBypass);
  407. mPacketDispatcher.RegisterPacket (bypassID.CString(), JUCEAlgorithmIDs::bypass);
  408. }
  409. void preparePlugin() const
  410. {
  411. AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
  412. check (Controller()->GetInputStemFormat (&inputStemFormat));
  413. const int numberOfInputChannels = getNumChannelsForStemFormat (inputStemFormat);
  414. AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
  415. check (Controller()->GetOutputStemFormat (&outputStemFormat));
  416. const int numberOfOutputChannels = getNumChannelsForStemFormat (outputStemFormat);
  417. int32_t bufferSize = 0;
  418. check (Controller()->GetSignalLatency (&bufferSize));
  419. const AAX_CSampleRate sampleRate = getSampleRate();
  420. AudioProcessor& audioProcessor = getPluginInstance();
  421. audioProcessor.setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, bufferSize);
  422. audioProcessor.prepareToPlay (sampleRate, bufferSize);
  423. }
  424. AAX_CSampleRate getSampleRate() const
  425. {
  426. AAX_CSampleRate sampleRate;
  427. check (Controller()->GetSampleRate (&sampleRate));
  428. return sampleRate;
  429. }
  430. JUCELibraryRefCount juceCount;
  431. ScopedPointer<AudioProcessor> pluginInstance;
  432. int32_t juceChunkIndex;
  433. // tempFilterData is initialized in GetChunkSize.
  434. // To avoid generating it again in GetChunk, we keep it as a member.
  435. mutable juce::MemoryBlock tempFilterData;
  436. JUCE_DECLARE_NON_COPYABLE (JuceAAX_Parameters)
  437. };
  438. //==============================================================================
  439. static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[],
  440. const void* const instancesEnd)
  441. {
  442. for (JUCEAlgorithmContext* const* iter = instancesBegin; iter < instancesEnd; ++iter)
  443. {
  444. const JUCEAlgorithmContext& i = **iter;
  445. i.pluginInstance->process (i.inputChannels, i.outputChannels,
  446. *(i.bufferSize), *(i.bypass) != 0);
  447. }
  448. }
  449. //==============================================================================
  450. static void createDescriptor (AAX_IComponentDescriptor& desc, int numInputs, int numOutputs)
  451. {
  452. check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
  453. check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
  454. check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
  455. check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
  456. check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
  457. // Create a property map
  458. AAX_IPropertyMap* const properties = desc.NewPropertyMap();
  459. jassert (properties != nullptr);
  460. properties->AddProperty (AAX_eProperty_ManufacturerID, JucePlugin_AAXManufacturerCode);
  461. properties->AddProperty (AAX_eProperty_ProductID, JucePlugin_AAXProductId);
  462. #if JucePlugin_AAXDisableBypass
  463. properties->AddProperty (AAX_eProperty_CanBypass, false);
  464. #else
  465. properties->AddProperty (AAX_eProperty_CanBypass, true);
  466. #endif
  467. properties->AddProperty (AAX_eProperty_InputStemFormat, getFormatForChans (numInputs));
  468. properties->AddProperty (AAX_eProperty_OutputStemFormat, getFormatForChans (numOutputs));
  469. properties->AddProperty (AAX_eProperty_PlugInID_Native, JucePlugin_AAXPluginId + (numInputs + 256 * numOutputs));
  470. check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
  471. }
  472. static void getPlugInDescription (AAX_IEffectDescriptor& descriptor)
  473. {
  474. descriptor.AddName (JucePlugin_Desc);
  475. descriptor.AddName (JucePlugin_Name);
  476. descriptor.AddCategory (JucePlugin_AAXCategory);
  477. check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
  478. check (descriptor.AddProcPtr ((void*) JuceAAX_Parameters::Create, kAAX_ProcPtrID_Create_EffectParameters));
  479. const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  480. const int numConfigs = numElementsInArray (channelConfigs);
  481. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  482. // value in your JucePluginCharacteristics.h file..
  483. jassert (numConfigs > 0);
  484. for (int i = 0; i < numConfigs; ++i)
  485. {
  486. if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
  487. {
  488. createDescriptor (*desc,
  489. channelConfigs [i][0],
  490. channelConfigs [i][1]);
  491. check (descriptor.AddComponent (desc));
  492. }
  493. }
  494. }
  495. };
  496. //==============================================================================
  497. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection*);
  498. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
  499. {
  500. AAXClasses::JUCELibraryRefCount libraryRefCount;
  501. if (AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor())
  502. {
  503. AAXClasses::getPlugInDescription (*descriptor);
  504. collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
  505. collection->SetManufacturerName (JucePlugin_Manufacturer);
  506. collection->AddPackageName (JucePlugin_Desc);
  507. collection->AddPackageName (JucePlugin_Name);
  508. collection->SetPackageVersion (JucePlugin_VersionCode);
  509. return AAX_SUCCESS;
  510. }
  511. return AAX_ERROR_NULL_OBJECT;
  512. }
  513. #endif