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.

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