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.

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