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.

565 lines
20KB

  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); (void) result;
  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& p) : pluginInstance (p) {}
  124. void process (const float* const* inputs, float* const* outputs, const int bufferSize, const bool bypass)
  125. {
  126. const int numIns = pluginInstance.getNumInputChannels();
  127. const int numOuts = pluginInstance.getNumOutputChannels();
  128. if (numOuts >= numIns)
  129. {
  130. for (int i = 0; i < numIns; ++i)
  131. memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
  132. process (outputs, numOuts, bufferSize, bypass);
  133. }
  134. else
  135. {
  136. if (channelList.size() <= numIns)
  137. channelList.insertMultiple (-1, nullptr, 1 + numIns - channelList.size());
  138. float** channels = channelList.getRawDataPointer();
  139. for (int i = 0; i < numOuts; ++i)
  140. {
  141. memcpy (outputs[i], inputs[i], bufferSize * sizeof (float));
  142. channels[i] = outputs[i];
  143. }
  144. for (int i = numOuts; i < numIns; ++i)
  145. channels[i] = const_cast <float*> (inputs[i]);
  146. process (channels, numIns, bufferSize, bypass);
  147. }
  148. }
  149. void process (float* const* channels, const int numChans, const int bufferSize, const bool bypass)
  150. {
  151. AudioSampleBuffer buffer (channels, numChans, bufferSize);
  152. // XXX need to do midi..
  153. midiBuffer.clear();
  154. {
  155. const ScopedLock sl (pluginInstance.getCallbackLock());
  156. if (bypass)
  157. pluginInstance.processBlockBypassed (buffer, midiBuffer);
  158. else
  159. pluginInstance.processBlock (buffer, midiBuffer);
  160. }
  161. }
  162. AudioProcessor& pluginInstance;
  163. MidiBuffer midiBuffer;
  164. Array<float*> channelList;
  165. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginInstanceInfo);
  166. };
  167. //==============================================================================
  168. struct JUCEAlgorithmContext
  169. {
  170. float** inputChannels;
  171. float** outputChannels;
  172. int32_t* bufferSize;
  173. int32_t* bypass;
  174. PluginInstanceInfo* pluginInstance;
  175. int32_t* isPrepared;
  176. };
  177. struct JUCEAlgorithmIDs
  178. {
  179. enum
  180. {
  181. inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
  182. outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
  183. bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
  184. bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
  185. pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
  186. preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared)
  187. };
  188. };
  189. //==============================================================================
  190. class JuceAAX_GUI : public AAX_CEffectGUI
  191. {
  192. public:
  193. JuceAAX_GUI() {}
  194. virtual ~JuceAAX_GUI() { DeleteViewContainer(); }
  195. static AAX_IEffectGUI* AAX_CALLBACK Create() { return new JuceAAX_GUI(); }
  196. void CreateViewContents()
  197. {
  198. if (component == nullptr)
  199. {
  200. if (JuceAAX_Parameters* params = dynamic_cast <JuceAAX_Parameters*> (GetEffectParameters()))
  201. component = new ContentWrapperComponent (*this, params->getPluginInstance());
  202. else
  203. jassertfalse;
  204. }
  205. }
  206. void CreateViewContainer()
  207. {
  208. CreateViewContents();
  209. if (void* nativeViewToAttachTo = GetViewContainerPtr())
  210. {
  211. #if JUCE_MAC
  212. if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
  213. #else
  214. if (GetViewContainerType() == AAX_eViewContainer_Type_HWND)
  215. #endif
  216. {
  217. component->setVisible (true);
  218. component->addToDesktop (0, nativeViewToAttachTo);
  219. }
  220. }
  221. }
  222. void DeleteViewContainer()
  223. {
  224. if (component != nullptr)
  225. {
  226. JUCE_AUTORELEASEPOOL
  227. component->removeFromDesktop();
  228. component = nullptr;
  229. }
  230. }
  231. virtual AAX_Result GetViewSize (AAX_Point* const viewSize) const
  232. {
  233. if (component != nullptr)
  234. {
  235. viewSize->horz = (float) component->getWidth();
  236. viewSize->vert = (float) component->getHeight();
  237. return AAX_SUCCESS;
  238. }
  239. return AAX_ERROR_NULL_OBJECT;
  240. }
  241. AAX_Result ParameterUpdated (AAX_CParamID iParameterID)
  242. {
  243. return AAX_SUCCESS;
  244. }
  245. AAX_Result SetControlHighlightInfo (AAX_CParamID iParameterID, AAX_CBoolean iIsHighlighted, AAX_EHighlightColor iColor)
  246. {
  247. return AAX_SUCCESS;
  248. }
  249. private:
  250. class ContentWrapperComponent : public juce::Component
  251. {
  252. public:
  253. ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin)
  254. : owner (gui)
  255. {
  256. setOpaque (true);
  257. addAndMakeVisible (pluginEditor = plugin.createEditorIfNeeded());
  258. setBounds (pluginEditor->getLocalBounds());
  259. setBroughtToFrontOnMouseClick (true);
  260. }
  261. ~ContentWrapperComponent()
  262. {
  263. if (pluginEditor != nullptr)
  264. {
  265. PopupMenu::dismissAllActiveMenus();
  266. pluginEditor->getAudioProcessor()->editorBeingDeleted (pluginEditor);
  267. }
  268. }
  269. void paint (Graphics& g)
  270. {
  271. g.fillAll (Colours::black);
  272. }
  273. void childBoundsChanged (Component*)
  274. {
  275. if (pluginEditor != nullptr)
  276. {
  277. const int w = pluginEditor->getWidth();
  278. const int h = pluginEditor->getHeight();
  279. setSize (w, h);
  280. AAX_Point newSize ((float) h, (float) w);
  281. owner.GetViewContainer()->SetViewSize (newSize);
  282. }
  283. }
  284. private:
  285. ScopedPointer<AudioProcessorEditor> pluginEditor;
  286. JuceAAX_GUI& owner;
  287. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent);
  288. };
  289. ScopedPointer<ContentWrapperComponent> component;
  290. JUCELibraryRefCount juceCount;
  291. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAAX_GUI);
  292. };
  293. //==============================================================================
  294. class JuceAAX_Parameters : public AAX_CEffectParameters
  295. {
  296. public:
  297. JuceAAX_Parameters()
  298. {
  299. pluginInstance = createPluginFilter();
  300. jassert (pluginInstance != nullptr); // your createPluginFilter() method must return an object!
  301. pluginInstance->wrapperType = AudioProcessor::wrapperType_AAX;
  302. }
  303. static AAX_CEffectParameters* AAX_CALLBACK Create() { return new JuceAAX_Parameters(); }
  304. AAX_Result EffectInit()
  305. {
  306. addBypassParameter();
  307. // add other params..
  308. preparePlugin();
  309. return AAX_SUCCESS;
  310. }
  311. AAX_Result ResetFieldData (AAX_CFieldIndex fieldIndex, void* data, uint32_t dataSize) const
  312. {
  313. switch (fieldIndex)
  314. {
  315. case JUCEAlgorithmIDs::pluginInstance:
  316. {
  317. const size_t numObjects = dataSize / sizeof (PluginInstanceInfo);
  318. PluginInstanceInfo* const objects = static_cast <PluginInstanceInfo*> (data);
  319. jassert (numObjects == 1); // not sure how to handle more than one..
  320. for (size_t i = 0; i < numObjects; ++i)
  321. new (objects + i) PluginInstanceInfo (*pluginInstance);
  322. break;
  323. }
  324. case JUCEAlgorithmIDs::preparedFlag:
  325. {
  326. preparePlugin();
  327. const size_t numObjects = dataSize / sizeof (uint32_t);
  328. uint32_t* const objects = static_cast <uint32_t*> (data);
  329. for (size_t i = 0; i < numObjects; ++i)
  330. new (objects + i) uint32_t (1);
  331. break;
  332. }
  333. }
  334. return AAX_SUCCESS;
  335. //return AAX_ERROR_INVALID_FIELD_INDEX;
  336. }
  337. AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
  338. private:
  339. void addBypassParameter()
  340. {
  341. AAX_CString bypassID;
  342. GetMasterBypassParameter (&bypassID);
  343. AAX_IParameter* masterBypass = new AAX_CParameter<bool> (bypassID.CString(),
  344. AAX_CString ("Master Bypass"),
  345. false,
  346. AAX_CBinaryTaperDelegate<bool>(),
  347. AAX_CBinaryDisplayDelegate<bool> ("bypass", "on"),
  348. true);
  349. masterBypass->SetNumberOfSteps (2);
  350. masterBypass->SetType (AAX_eParameterType_Discrete);
  351. mParameterManager.AddParameter (masterBypass);
  352. mPacketDispatcher.RegisterPacket (bypassID.CString(), JUCEAlgorithmIDs::bypass);
  353. }
  354. void preparePlugin() const
  355. {
  356. AAX_CSampleRate sampleRate;
  357. check (Controller()->GetSampleRate (&sampleRate));
  358. AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
  359. check (Controller()->GetInputStemFormat (&inputStemFormat));
  360. const int numberOfInputChannels = getNumChannelsForStemFormat (inputStemFormat);
  361. AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
  362. check (Controller()->GetOutputStemFormat (&outputStemFormat));
  363. const int numberOfOutputChannels = getNumChannelsForStemFormat (outputStemFormat);
  364. int32_t bufferSize = 0;
  365. check (Controller()->GetSignalLatency (&bufferSize));
  366. AudioProcessor& audioProcessor = getPluginInstance();
  367. audioProcessor.setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, bufferSize);
  368. audioProcessor.prepareToPlay (sampleRate, bufferSize);
  369. }
  370. JUCELibraryRefCount juceCount;
  371. ScopedPointer<AudioProcessor> pluginInstance;
  372. JUCE_DECLARE_NON_COPYABLE (JuceAAX_Parameters);
  373. };
  374. //==============================================================================
  375. static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[],
  376. const void* const instancesEnd)
  377. {
  378. for (JUCEAlgorithmContext* const* iter = instancesBegin; iter < instancesEnd; ++iter)
  379. {
  380. const JUCEAlgorithmContext& i = **iter;
  381. i.pluginInstance->process (i.inputChannels, i.outputChannels,
  382. *(i.bufferSize), *(i.bypass) != 0);
  383. }
  384. }
  385. //==============================================================================
  386. static void createDescriptor (AAX_IComponentDescriptor& desc, int numInputs, int numOutputs)
  387. {
  388. check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
  389. check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
  390. check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
  391. check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
  392. check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
  393. // Create a property map
  394. AAX_IPropertyMap* const properties = desc.NewPropertyMap();
  395. jassert (properties != nullptr);
  396. properties->AddProperty (AAX_eProperty_ManufacturerID, JucePlugin_AAXManufacturerCode);
  397. properties->AddProperty (AAX_eProperty_ProductID, JucePlugin_AAXProductId);
  398. #if JucePlugin_AAXDisableBypass
  399. properties->AddProperty (AAX_eProperty_CanBypass, false);
  400. #else
  401. properties->AddProperty (AAX_eProperty_CanBypass, true);
  402. #endif
  403. properties->AddProperty (AAX_eProperty_InputStemFormat, getFormatForChans (numInputs));
  404. properties->AddProperty (AAX_eProperty_OutputStemFormat, getFormatForChans (numOutputs));
  405. properties->AddProperty (AAX_eProperty_PlugInID_Native, JucePlugin_AAXPluginId + (numInputs + 256 * numOutputs));
  406. check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
  407. }
  408. static void getPlugInDescription (AAX_IEffectDescriptor& descriptor)
  409. {
  410. descriptor.AddName (JucePlugin_Desc);
  411. descriptor.AddName (JucePlugin_Name);
  412. descriptor.AddName (FourCharConst (JucePlugin_PluginCode).asString);
  413. descriptor.AddCategory (JucePlugin_AAXCategory);
  414. check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
  415. check (descriptor.AddProcPtr ((void*) JuceAAX_Parameters::Create, kAAX_ProcPtrID_Create_EffectParameters));
  416. const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  417. const int numConfigs = numElementsInArray (channelConfigs);
  418. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  419. // value in your JucePluginCharacteristics.h file..
  420. jassert (numConfigs > 0);
  421. for (int i = 0; i < numConfigs; ++i)
  422. {
  423. if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
  424. {
  425. createDescriptor (*desc,
  426. channelConfigs [i][0],
  427. channelConfigs [i][1]);
  428. check (descriptor.AddComponent (desc));
  429. }
  430. }
  431. }
  432. };
  433. //==============================================================================
  434. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
  435. {
  436. AAXClasses::JUCELibraryRefCount libraryRefCount;
  437. if (AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor())
  438. {
  439. AAXClasses::getPlugInDescription (*descriptor);
  440. collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
  441. collection->SetManufacturerName (JucePlugin_Manufacturer);
  442. collection->AddPackageName (JucePlugin_Desc);
  443. collection->AddPackageName (JucePlugin_Name);
  444. collection->AddPackageName (AAXClasses::FourCharConst (JucePlugin_PluginCode).asString);
  445. collection->SetPackageVersion (JucePlugin_VersionCode);
  446. return AAX_SUCCESS;
  447. }
  448. return AAX_ERROR_NULL_OBJECT;
  449. }
  450. #endif