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.

583 lines
21KB

  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. #include "AAX_Exports.cpp"
  31. #include "AAX_ICollection.h"
  32. #include "AAX_IComponentDescriptor.h"
  33. #include "AAX_IEffectDescriptor.h"
  34. #include "AAX_IPropertyMap.h"
  35. #include "AAX_CEffectParameters.h"
  36. #include "AAX_Errors.h"
  37. #include "AAX_CBinaryTaperDelegate.h"
  38. #include "AAX_CBinaryDisplayDelegate.h"
  39. #include "AAX_CEffectGUI.h"
  40. #include "AAX_IViewContainer.h"
  41. using juce::Component;
  42. //==============================================================================
  43. /** Somewhere in the codebase of your plugin, you need to implement this function
  44. and make it return a new instance of the filter subclass that you're building.
  45. */
  46. extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
  47. //==============================================================================
  48. struct AAXClasses
  49. {
  50. static void check (AAX_Result result)
  51. {
  52. jassert (result == AAX_SUCCESS);
  53. }
  54. struct FourCharConst
  55. {
  56. FourCharConst (const uint32 n) noexcept
  57. {
  58. asString[0] = (char) (n >> 24);
  59. asString[1] = (char) (n >> 16);
  60. asString[2] = (char) (n >> 8);
  61. asString[3] = (char) n;
  62. asString[4] = 0;
  63. }
  64. char asString[5];
  65. };
  66. static AAX_EStemFormat getFormatForChans (const int numChans) noexcept
  67. {
  68. switch (numChans)
  69. {
  70. case 0: return AAX_eStemFormat_None;
  71. case 1: return AAX_eStemFormat_Mono;
  72. case 2: return AAX_eStemFormat_Stereo;
  73. case 3: return AAX_eStemFormat_LCR;
  74. case 4: return AAX_eStemFormat_Quad;
  75. case 5: return AAX_eStemFormat_5_0;
  76. case 6: return AAX_eStemFormat_5_1;
  77. case 7: return AAX_eStemFormat_6_1;
  78. case 8: return AAX_eStemFormat_7_1_DTS;
  79. default: jassertfalse; break; // hmm - not a valid number of chans..
  80. }
  81. return AAX_eStemFormat_None;
  82. }
  83. static int getNumChannelsForStemFormat (AAX_EStemFormat format) noexcept
  84. {
  85. switch (format)
  86. {
  87. case AAX_eStemFormat_None: return 0;
  88. case AAX_eStemFormat_Mono: return 1;
  89. case AAX_eStemFormat_Stereo: return 2;
  90. case AAX_eStemFormat_LCR: return 3;
  91. case AAX_eStemFormat_Quad: return 4;
  92. case AAX_eStemFormat_5_0: return 5;
  93. case AAX_eStemFormat_5_1: return 6;
  94. case AAX_eStemFormat_6_1: return 7;
  95. case AAX_eStemFormat_7_1_DTS: return 8;
  96. default: jassertfalse; break; // hmm - not a valid number of chans..
  97. }
  98. return 0;
  99. }
  100. //==============================================================================
  101. struct JUCELibraryRefCount
  102. {
  103. JUCELibraryRefCount() { if (getCount()++ == 0) initialise(); }
  104. ~JUCELibraryRefCount() { if (--getCount() == 0) shutdown(); }
  105. private:
  106. static void initialise()
  107. {
  108. initialiseJuce_GUI();
  109. }
  110. static void shutdown()
  111. {
  112. shutdownJuce_GUI();
  113. }
  114. int& getCount() noexcept
  115. {
  116. static int count = 0;
  117. return count;
  118. }
  119. };
  120. //==============================================================================
  121. struct PluginInstanceInfo
  122. {
  123. PluginInstanceInfo (AudioProcessor* pluginInstance_)
  124. : pluginInstance (pluginInstance_)
  125. {
  126. }
  127. void process (const float* const* inputs, float* const* outputs, const int bufferSize)
  128. {
  129. const int numIns = pluginInstance->getNumInputChannels();
  130. const int numOuts = pluginInstance->getNumOutputChannels();
  131. if (numOuts >= numIns)
  132. {
  133. for (int i = 0; i < numIns; ++i)
  134. memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
  135. process (outputs, numOuts, bufferSize);
  136. }
  137. else
  138. {
  139. if (channelList.size() <= numIns)
  140. channelList.insertMultiple (-1, nullptr, 1 + numIns - channelList.size());
  141. float** channels = channelList.getRawDataPointer();
  142. for (int i = 0; i < numOuts; ++i)
  143. {
  144. memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
  145. channels[i] = outputs[i];
  146. }
  147. for (int i = numOuts; i < numIns; ++i)
  148. channels[i] = const_cast <float*> (inputs[i]);
  149. process (channels, numIns, bufferSize);
  150. }
  151. }
  152. void process (float* const* channels, const int numChans, const int bufferSize)
  153. {
  154. AudioSampleBuffer buffer (channels, numChans, bufferSize);
  155. // XXX need to do midi..
  156. midiBuffer.clear();
  157. {
  158. const ScopedLock sl (pluginInstance->getCallbackLock());
  159. pluginInstance->processBlock (buffer, midiBuffer);
  160. }
  161. }
  162. void bypass (float* const* inputs, float* const* outputs, int bufferSize)
  163. {
  164. const int numIns = pluginInstance->getNumInputChannels();
  165. const int numOuts = pluginInstance->getNumOutputChannels();
  166. for (int i = 0; i < numOuts; ++i)
  167. {
  168. if (i < numIns)
  169. memcpy (outputs[i], inputs[i], sizeof (float) * bufferSize);
  170. else
  171. zeromem (outputs[i], sizeof (float) * bufferSize);
  172. }
  173. }
  174. AudioProcessor* pluginInstance;
  175. MidiBuffer midiBuffer;
  176. Array<float*> channelList;
  177. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginInstanceInfo);
  178. };
  179. //==============================================================================
  180. struct JUCEAlgorithmContext
  181. {
  182. float** inputChannels;
  183. float** outputChannels;
  184. int32_t* bufferSize;
  185. int32_t* bypass;
  186. PluginInstanceInfo* pluginInstance;
  187. int32_t* isPrepared;
  188. };
  189. struct JUCEAlgorithmIDs
  190. {
  191. enum
  192. {
  193. inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
  194. outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
  195. bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
  196. bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
  197. pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
  198. preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared)
  199. };
  200. };
  201. //==============================================================================
  202. class JuceAAX_GUI : public AAX_CEffectGUI
  203. {
  204. public:
  205. JuceAAX_GUI() {}
  206. virtual ~JuceAAX_GUI() { DeleteViewContainer(); }
  207. static AAX_IEffectGUI* AAX_CALLBACK Create() { return new JuceAAX_GUI(); }
  208. void CreateViewContents()
  209. {
  210. if (component == nullptr)
  211. {
  212. JuceAAX_Parameters* params = dynamic_cast <JuceAAX_Parameters*> (GetEffectParameters());
  213. jassert (params != nullptr);
  214. if (params != nullptr)
  215. component = new ContentWrapperComponent (*this, params->getPluginInstance());
  216. }
  217. }
  218. void CreateViewContainer()
  219. {
  220. CreateViewContents();
  221. void* nativeViewToAttachTo = GetViewContainerPtr();
  222. if (nativeViewToAttachTo != nullptr)
  223. {
  224. #if JUCE_MAC
  225. if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
  226. #else
  227. if (GetViewContainerType() == AAX_eViewContainer_Type_HWND)
  228. #endif
  229. {
  230. component->setVisible (true);
  231. component->addToDesktop (0, nativeViewToAttachTo);
  232. }
  233. }
  234. }
  235. void DeleteViewContainer()
  236. {
  237. if (component != nullptr)
  238. {
  239. JUCE_AUTORELEASEPOOL
  240. component->removeFromDesktop();
  241. component = nullptr;
  242. }
  243. }
  244. virtual AAX_Result GetViewSize (AAX_Point* const viewSize) const
  245. {
  246. if (component != nullptr)
  247. {
  248. viewSize->horz = (float) component->getWidth();
  249. viewSize->vert = (float) component->getHeight();
  250. return AAX_SUCCESS;
  251. }
  252. return AAX_ERROR_NULL_OBJECT;
  253. }
  254. AAX_Result ParameterUpdated (AAX_CParamID iParameterID)
  255. {
  256. return AAX_SUCCESS;
  257. }
  258. AAX_Result SetControlHighlightInfo (AAX_CParamID iParameterID, AAX_CBoolean iIsHighlighted, AAX_EHighlightColor iColor)
  259. {
  260. return AAX_SUCCESS;
  261. }
  262. private:
  263. class ContentWrapperComponent : public juce::Component
  264. {
  265. public:
  266. ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor* plugin)
  267. : owner (gui)
  268. {
  269. setOpaque (true);
  270. addAndMakeVisible (pluginEditor = plugin->createEditorIfNeeded());
  271. setBounds (pluginEditor->getLocalBounds());
  272. setBroughtToFrontOnMouseClick (true);
  273. }
  274. ~ContentWrapperComponent()
  275. {
  276. if (pluginEditor != nullptr)
  277. {
  278. PopupMenu::dismissAllActiveMenus();
  279. pluginEditor->getAudioProcessor()->editorBeingDeleted (pluginEditor);
  280. }
  281. }
  282. void paint (Graphics& g)
  283. {
  284. g.fillAll (Colours::black);
  285. }
  286. void childBoundsChanged (Component*)
  287. {
  288. if (pluginEditor != nullptr)
  289. {
  290. const int w = pluginEditor->getWidth();
  291. const int h = pluginEditor->getHeight();
  292. setSize (w, h);
  293. AAX_Point newSize ((float) h, (float) w);
  294. owner.GetViewContainer()->SetViewSize (newSize);
  295. }
  296. }
  297. private:
  298. ScopedPointer<AudioProcessorEditor> pluginEditor;
  299. JuceAAX_GUI& owner;
  300. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent);
  301. };
  302. ScopedPointer<ContentWrapperComponent> component;
  303. JUCELibraryRefCount juceCount;
  304. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAAX_GUI);
  305. };
  306. //==============================================================================
  307. class JuceAAX_Parameters : public AAX_CEffectParameters
  308. {
  309. public:
  310. JuceAAX_Parameters()
  311. {
  312. pluginInstance = createPluginFilter();
  313. if (pluginInstance != nullptr)
  314. pluginInstance->wrapperType = AudioProcessor::wrapperType_AAX;
  315. }
  316. static AAX_CEffectParameters* AAX_CALLBACK Create() { return new JuceAAX_Parameters(); }
  317. AAX_Result EffectInit()
  318. {
  319. addBypassParameter();
  320. // add other params..
  321. preparePlugin();
  322. return AAX_SUCCESS;
  323. }
  324. AAX_Result ResetFieldData (AAX_CFieldIndex fieldIndex, void* data, uint32_t dataSize) const
  325. {
  326. switch (fieldIndex)
  327. {
  328. case JUCEAlgorithmIDs::pluginInstance:
  329. {
  330. const size_t numObjects = dataSize / sizeof (PluginInstanceInfo);
  331. PluginInstanceInfo* const objects = static_cast <PluginInstanceInfo*> (data);
  332. jassert (numObjects == 1); // not sure how to handle more than one..
  333. for (size_t i = 0; i < numObjects; ++i)
  334. new (objects + i) PluginInstanceInfo (pluginInstance);
  335. break;
  336. }
  337. case JUCEAlgorithmIDs::preparedFlag:
  338. {
  339. preparePlugin();
  340. const size_t numObjects = dataSize / sizeof (uint32_t);
  341. uint32_t* const objects = static_cast <uint32_t*> (data);
  342. for (size_t i = 0; i < numObjects; ++i)
  343. new (objects + i) uint32_t (1);
  344. break;
  345. }
  346. }
  347. return AAX_SUCCESS;
  348. //return AAX_ERROR_INVALID_FIELD_INDEX;
  349. }
  350. AudioProcessor* getPluginInstance() const noexcept { return pluginInstance; }
  351. private:
  352. void addBypassParameter()
  353. {
  354. AAX_CString bypassID;
  355. GetMasterBypassParameter (&bypassID);
  356. AAX_IParameter* masterBypass = new AAX_CParameter<bool> (bypassID.CString(),
  357. AAX_CString ("Master Bypass"),
  358. false,
  359. AAX_CBinaryTaperDelegate<bool>(),
  360. AAX_CBinaryDisplayDelegate<bool> ("bypass", "on"),
  361. true);
  362. masterBypass->SetNumberOfSteps (2);
  363. masterBypass->SetType (AAX_eParameterType_Discrete);
  364. mParameterManager.AddParameter (masterBypass);
  365. mPacketDispatcher.RegisterPacket (bypassID.CString(), JUCEAlgorithmIDs::bypass);
  366. }
  367. void preparePlugin() const
  368. {
  369. AAX_CSampleRate sampleRate;
  370. check (Controller()->GetSampleRate (&sampleRate));
  371. AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
  372. check (Controller()->GetInputStemFormat (&inputStemFormat));
  373. const int numberOfInputChannels = getNumChannelsForStemFormat (inputStemFormat);
  374. AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
  375. check (Controller()->GetOutputStemFormat (&outputStemFormat));
  376. const int numberOfOutputChannels = getNumChannelsForStemFormat (outputStemFormat);
  377. int32_t bufferSize = 0;
  378. check (Controller()->GetSignalLatency (&bufferSize));
  379. AudioProcessor* audioProcessor = getPluginInstance();
  380. audioProcessor->setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, bufferSize);
  381. audioProcessor->prepareToPlay (sampleRate, bufferSize);
  382. }
  383. JUCELibraryRefCount juceCount;
  384. ScopedPointer<AudioProcessor> pluginInstance;
  385. JUCE_DECLARE_NON_COPYABLE (JuceAAX_Parameters);
  386. };
  387. //==============================================================================
  388. static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[],
  389. const void* const instancesEnd)
  390. {
  391. for (JUCEAlgorithmContext* const* iter = instancesBegin; iter < instancesEnd; ++iter)
  392. {
  393. const JUCEAlgorithmContext& i = **iter;
  394. if (*(i.bypass) != 0)
  395. i.pluginInstance->bypass (i.inputChannels, i.outputChannels, *(i.bufferSize));
  396. else
  397. i.pluginInstance->process (i.inputChannels, i.outputChannels, *(i.bufferSize));
  398. }
  399. }
  400. //==============================================================================
  401. static void createDescriptor (AAX_IComponentDescriptor& desc, int numInputs, int numOutputs)
  402. {
  403. check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
  404. check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
  405. check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
  406. check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
  407. check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
  408. // Create a property map
  409. AAX_IPropertyMap* const properties = desc.NewPropertyMap();
  410. jassert (properties != nullptr);
  411. properties->AddProperty (AAX_eProperty_ManufacturerID, JucePlugin_AAXManufacturerCode);
  412. properties->AddProperty (AAX_eProperty_ProductID, JucePlugin_AAXProductId);
  413. #if JucePlugin_AAXDisableBypass
  414. properties->AddProperty (AAX_eProperty_CanBypass, false);
  415. #else
  416. properties->AddProperty (AAX_eProperty_CanBypass, true);
  417. #endif
  418. properties->AddProperty (AAX_eProperty_InputStemFormat, getFormatForChans (numInputs));
  419. properties->AddProperty (AAX_eProperty_OutputStemFormat, getFormatForChans (numOutputs));
  420. properties->AddProperty (AAX_eProperty_PlugInID_Native, JucePlugin_AAXPluginId + (numInputs + 256 * numOutputs));
  421. check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
  422. }
  423. static void getPlugInDescription (AAX_IEffectDescriptor& descriptor)
  424. {
  425. descriptor.AddName (JucePlugin_Desc);
  426. descriptor.AddName (JucePlugin_Name);
  427. descriptor.AddName (FourCharConst (JucePlugin_PluginCode).asString);
  428. descriptor.AddCategory (JucePlugin_AAXCategory);
  429. check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
  430. check (descriptor.AddProcPtr ((void*) JuceAAX_Parameters::Create, kAAX_ProcPtrID_Create_EffectParameters));
  431. const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  432. const int numConfigs = numElementsInArray (channelConfigs);
  433. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  434. // value in your JucePluginCharacteristics.h file..
  435. jassert (numConfigs > 0);
  436. for (int i = 0; i < numConfigs; ++i)
  437. {
  438. AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor();
  439. if (desc != nullptr)
  440. {
  441. createDescriptor (*desc,
  442. channelConfigs [i][0],
  443. channelConfigs [i][1]);
  444. check (descriptor.AddComponent (desc));
  445. }
  446. }
  447. }
  448. };
  449. //==============================================================================
  450. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
  451. {
  452. AAXClasses::JUCELibraryRefCount libraryRefCount;
  453. AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor();
  454. if (descriptor == nullptr)
  455. return AAX_ERROR_NULL_OBJECT;
  456. AAXClasses::getPlugInDescription (*descriptor);
  457. collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
  458. collection->SetManufacturerName (JucePlugin_Manufacturer);
  459. collection->AddPackageName (JucePlugin_Desc);
  460. collection->AddPackageName (JucePlugin_Name);
  461. collection->AddPackageName (AAXClasses::FourCharConst (JucePlugin_PluginCode).asString);
  462. collection->SetPackageVersion (JucePlugin_VersionCode);
  463. return AAX_SUCCESS;
  464. }
  465. #endif