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.

574 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. }
  273. ~ContentWrapperComponent()
  274. {
  275. if (pluginEditor != nullptr)
  276. {
  277. PopupMenu::dismissAllActiveMenus();
  278. pluginEditor->getAudioProcessor()->editorBeingDeleted (pluginEditor);
  279. }
  280. }
  281. void paint (Graphics& g)
  282. {
  283. g.fillAll (Colours::black);
  284. }
  285. void childBoundsChanged (Component*)
  286. {
  287. if (pluginEditor != nullptr)
  288. {
  289. const int w = pluginEditor->getWidth();
  290. const int h = pluginEditor->getHeight();
  291. setSize (w, h);
  292. AAX_Point newSize ((float) h, (float) w);
  293. owner.GetViewContainer()->SetViewSize (newSize);
  294. }
  295. }
  296. private:
  297. ScopedPointer<AudioProcessorEditor> pluginEditor;
  298. JuceAAX_GUI& owner;
  299. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent);
  300. };
  301. ScopedPointer<ContentWrapperComponent> component;
  302. JUCELibraryRefCount juceCount;
  303. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAAX_GUI);
  304. };
  305. //==============================================================================
  306. class JuceAAX_Parameters : public AAX_CEffectParameters
  307. {
  308. public:
  309. JuceAAX_Parameters()
  310. {
  311. pluginInstance = createPluginFilter();
  312. }
  313. static AAX_CEffectParameters* AAX_CALLBACK Create() { return new JuceAAX_Parameters(); }
  314. AAX_Result EffectInit()
  315. {
  316. addBypassParameter();
  317. // add other params..
  318. preparePlugin();
  319. return AAX_SUCCESS;
  320. }
  321. AAX_Result ResetFieldData (AAX_CFieldIndex fieldIndex, void* data, uint32_t dataSize) const
  322. {
  323. switch (fieldIndex)
  324. {
  325. case JUCEAlgorithmIDs::pluginInstance:
  326. {
  327. const size_t numObjects = dataSize / sizeof (PluginInstanceInfo);
  328. PluginInstanceInfo* const objects = static_cast <PluginInstanceInfo*> (data);
  329. jassert (numObjects == 1); // not sure how to handle more than one..
  330. for (size_t i = 0; i < numObjects; ++i)
  331. new (objects + i) PluginInstanceInfo (pluginInstance);
  332. break;
  333. }
  334. case JUCEAlgorithmIDs::preparedFlag:
  335. {
  336. preparePlugin();
  337. const size_t numObjects = dataSize / sizeof (uint32_t);
  338. uint32_t* const objects = static_cast <uint32_t*> (data);
  339. for (size_t i = 0; i < numObjects; ++i)
  340. new (objects + i) uint32_t (1);
  341. break;
  342. }
  343. }
  344. return AAX_SUCCESS;
  345. //return AAX_ERROR_INVALID_FIELD_INDEX;
  346. }
  347. AudioProcessor* getPluginInstance() const noexcept { return pluginInstance; }
  348. private:
  349. void addBypassParameter()
  350. {
  351. AAX_CString bypassID;
  352. GetMasterBypassParameter (&bypassID);
  353. AAX_IParameter* masterBypass = new AAX_CParameter<bool> (bypassID.CString(),
  354. AAX_CString ("Master Bypass"),
  355. false,
  356. AAX_CBinaryTaperDelegate<bool>(),
  357. AAX_CBinaryDisplayDelegate<bool> ("bypass", "on"),
  358. true);
  359. masterBypass->SetNumberOfSteps (2);
  360. masterBypass->SetType (AAX_eParameterType_Discrete);
  361. mParameterManager.AddParameter (masterBypass);
  362. mPacketDispatcher.RegisterPacket (bypassID.CString(), JUCEAlgorithmIDs::bypass);
  363. }
  364. void preparePlugin() const
  365. {
  366. AAX_CSampleRate sampleRate;
  367. check (Controller()->GetSampleRate (&sampleRate));
  368. AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
  369. check (Controller()->GetInputStemFormat (&inputStemFormat));
  370. const int numberOfInputChannels = getNumChannelsForStemFormat (inputStemFormat);
  371. AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
  372. check (Controller()->GetOutputStemFormat (&outputStemFormat));
  373. const int numberOfOutputChannels = getNumChannelsForStemFormat (outputStemFormat);
  374. int32_t bufferSize = 0;
  375. check (Controller()->GetSignalLatency (&bufferSize));
  376. AudioProcessor* audioProcessor = getPluginInstance();
  377. audioProcessor->setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, bufferSize);
  378. audioProcessor->prepareToPlay (sampleRate, bufferSize);
  379. }
  380. JUCELibraryRefCount juceCount;
  381. ScopedPointer<AudioProcessor> pluginInstance;
  382. JUCE_DECLARE_NON_COPYABLE (JuceAAX_Parameters);
  383. };
  384. //==============================================================================
  385. static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[],
  386. const void* const instancesEnd)
  387. {
  388. for (JUCEAlgorithmContext* const* iter = instancesBegin; iter < instancesEnd; ++iter)
  389. {
  390. const JUCEAlgorithmContext& i = **iter;
  391. if (*(i.bypass) != 0)
  392. i.pluginInstance->bypass (i.inputChannels, i.outputChannels, *(i.bufferSize));
  393. else
  394. i.pluginInstance->process (i.inputChannels, i.outputChannels, *(i.bufferSize));
  395. }
  396. }
  397. //==============================================================================
  398. static void createDescriptor (AAX_IComponentDescriptor& desc, int numInputs, int numOutputs)
  399. {
  400. check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
  401. check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
  402. check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
  403. check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
  404. check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
  405. // Create a property map
  406. AAX_IPropertyMap* const properties = desc.NewPropertyMap();
  407. jassert (properties != nullptr);
  408. properties->AddProperty (AAX_eProperty_ManufacturerID, JucePlugin_AAXManufacturerCode);
  409. properties->AddProperty (AAX_eProperty_ProductID, JucePlugin_AAXProductId);
  410. properties->AddProperty (AAX_eProperty_CanBypass, true);
  411. properties->AddProperty (AAX_eProperty_InputStemFormat, getFormatForChans (numInputs));
  412. properties->AddProperty (AAX_eProperty_OutputStemFormat, getFormatForChans (numOutputs));
  413. properties->AddProperty (AAX_eProperty_PlugInID_Native, JucePlugin_AAXPluginId + (numInputs + 256 * numOutputs));
  414. check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
  415. }
  416. static void getPlugInDescription (AAX_IEffectDescriptor& descriptor)
  417. {
  418. descriptor.AddName (JucePlugin_Desc);
  419. descriptor.AddName (JucePlugin_Name);
  420. descriptor.AddName (FourCharConst (JucePlugin_PluginCode).asString);
  421. descriptor.AddCategory (JucePlugin_AAXCategory);
  422. check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
  423. check (descriptor.AddProcPtr ((void*) JuceAAX_Parameters::Create, kAAX_ProcPtrID_Create_EffectParameters));
  424. const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  425. const int numConfigs = numElementsInArray (channelConfigs);
  426. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  427. // value in your JucePluginCharacteristics.h file..
  428. jassert (numConfigs > 0);
  429. for (int i = 0; i < numConfigs; ++i)
  430. {
  431. AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor();
  432. if (desc != nullptr)
  433. {
  434. createDescriptor (*desc,
  435. channelConfigs [i][0],
  436. channelConfigs [i][1]);
  437. check (descriptor.AddComponent (desc));
  438. }
  439. }
  440. }
  441. };
  442. //==============================================================================
  443. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
  444. {
  445. AAXClasses::JUCELibraryRefCount libraryRefCount;
  446. AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor();
  447. if (descriptor == nullptr)
  448. return AAX_ERROR_NULL_OBJECT;
  449. AAXClasses::getPlugInDescription (*descriptor);
  450. collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
  451. collection->SetManufacturerName (JucePlugin_Manufacturer);
  452. collection->AddPackageName (JucePlugin_Desc);
  453. collection->AddPackageName (JucePlugin_Name);
  454. collection->AddPackageName (AAXClasses::FourCharConst (JucePlugin_PluginCode).asString);
  455. collection->SetPackageVersion (JucePlugin_VersionCode);
  456. return AAX_SUCCESS;
  457. }
  458. #endif