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.

540 lines
19KB

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