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.

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