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.

564 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 || JUCE_WINDOWS)
  23. #if JUCE_MAC
  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. #if JUCE_WINDOWS
  39. static HINSTANCE moduleInstance = 0;
  40. extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD selector, LPVOID)
  41. {
  42. if (selector == DLL_PROCESS_ATTACH)
  43. moduleInstance = instance;
  44. return true;
  45. }
  46. #endif
  47. //==============================================================================
  48. /** Somewhere in the codebase of your plugin, you need to implement this function
  49. and make it return a new instance of the filter subclass that you're building.
  50. */
  51. extern AudioProcessor* JUCE_CALLTYPE createPluginFilter();
  52. //==============================================================================
  53. struct AAXClasses
  54. {
  55. static void check (AAX_Result result)
  56. {
  57. jassert (result == AAX_SUCCESS);
  58. }
  59. struct FourCharConst
  60. {
  61. FourCharConst (const uint32 n) noexcept
  62. {
  63. asString[0] = (char) (n >> 24);
  64. asString[1] = (char) (n >> 16);
  65. asString[2] = (char) (n >> 8);
  66. asString[3] = (char) n;
  67. asString[4] = 0;
  68. }
  69. char asString[5];
  70. };
  71. static AAX_EStemFormat getFormatForChans (const int numChans) noexcept
  72. {
  73. switch (numChans)
  74. {
  75. case 0: return AAX_eStemFormat_None;
  76. case 1: return AAX_eStemFormat_Mono;
  77. case 2: return AAX_eStemFormat_Stereo;
  78. case 3: return AAX_eStemFormat_LCR;
  79. case 4: return AAX_eStemFormat_Quad;
  80. case 5: return AAX_eStemFormat_5_0;
  81. case 6: return AAX_eStemFormat_5_1;
  82. case 7: return AAX_eStemFormat_6_1;
  83. case 8: return AAX_eStemFormat_7_1_DTS;
  84. default: jassertfalse; break; // hmm - not a valid number of chans..
  85. }
  86. return AAX_eStemFormat_None;
  87. }
  88. static int getNumChannelsForStemFormat (AAX_EStemFormat format) noexcept
  89. {
  90. switch (format)
  91. {
  92. case AAX_eStemFormat_None: return 0;
  93. case AAX_eStemFormat_Mono: return 1;
  94. case AAX_eStemFormat_Stereo: return 2;
  95. case AAX_eStemFormat_LCR: return 3;
  96. case AAX_eStemFormat_Quad: return 4;
  97. case AAX_eStemFormat_5_0: return 5;
  98. case AAX_eStemFormat_5_1: return 6;
  99. case AAX_eStemFormat_6_1: return 7;
  100. case AAX_eStemFormat_7_1_DTS: return 8;
  101. default: jassertfalse; break; // hmm - not a valid number of chans..
  102. }
  103. return 0;
  104. }
  105. //==============================================================================
  106. struct JUCELibraryRefCount
  107. {
  108. JUCELibraryRefCount() { if (getCount()++ == 0) initialise(); }
  109. ~JUCELibraryRefCount() { if (--getCount() == 0) shutdown(); }
  110. private:
  111. static void initialise()
  112. {
  113. #if JUCE_WINDOWS
  114. PlatformUtilities::setCurrentModuleInstanceHandle (moduleInstance);
  115. #endif
  116. initialiseJuce_GUI();
  117. }
  118. static void shutdown()
  119. {
  120. shutdownJuce_GUI();
  121. }
  122. int& getCount() noexcept
  123. {
  124. static int count = 0;
  125. return count;
  126. }
  127. };
  128. //==============================================================================
  129. struct PluginInstanceInfo
  130. {
  131. PluginInstanceInfo (AudioProcessor* pluginInstance_)
  132. : pluginInstance (pluginInstance_)
  133. {
  134. }
  135. void process (float* const* inputs, float* const* outputs, const int bufferSize)
  136. {
  137. const int numIns = pluginInstance->getNumInputChannels();
  138. const int numOuts = pluginInstance->getNumOutputChannels();
  139. const int bufferBytes = bufferSize * sizeof (float);
  140. float* const* renderingChannels = inputs;
  141. int numChans = numIns;
  142. if (numOuts > numIns)
  143. {
  144. for (int i = 0; i < numIns; ++i)
  145. memcpy (outputs[i], inputs[i], bufferBytes);
  146. renderingChannels = outputs;
  147. numChans = numOuts;
  148. }
  149. AudioSampleBuffer buffer (renderingChannels, jmax (1, numChans), bufferSize);
  150. // XXX need to do midi..
  151. midiBuffer.clear();
  152. {
  153. const ScopedLock sl (pluginInstance->getCallbackLock());
  154. pluginInstance->processBlock (buffer, midiBuffer);
  155. }
  156. if (numOuts <= numIns)
  157. {
  158. for (int i = 0; i < numOuts; ++i)
  159. memcpy (outputs[i], inputs[i], bufferBytes);
  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. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginInstanceInfo);
  177. };
  178. //==============================================================================
  179. struct JUCEAlgorithmContext
  180. {
  181. float* const* inputChannels;
  182. float* const* outputChannels;
  183. int32_t* bufferSize;
  184. int32_t* bypass;
  185. PluginInstanceInfo* pluginInstance;
  186. int32_t* isPrepared;
  187. };
  188. struct JUCEAlgorithmIDs
  189. {
  190. enum
  191. {
  192. inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
  193. outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
  194. bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
  195. bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
  196. pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
  197. preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared)
  198. };
  199. };
  200. //==============================================================================
  201. class JuceAAX_GUI : public AAX_CEffectGUI
  202. {
  203. public:
  204. JuceAAX_GUI() {}
  205. virtual ~JuceAAX_GUI() { DeleteViewContainer(); }
  206. static AAX_IEffectGUI* AAX_CALLBACK Create() { return new JuceAAX_GUI(); }
  207. void CreateViewContents()
  208. {
  209. if (component == nullptr)
  210. {
  211. JuceAAX_Parameters* params = dynamic_cast <JuceAAX_Parameters*> (GetEffectParameters());
  212. jassert (params != nullptr);
  213. if (params != nullptr)
  214. component = new ContentWrapperComponent (params->getPluginInstance());
  215. }
  216. }
  217. void CreateViewContainer()
  218. {
  219. CreateViewContents();
  220. void* nativeViewToAttachTo = GetViewContainerPtr();
  221. if (nativeViewToAttachTo != nullptr)
  222. {
  223. #if JUCE_MAC
  224. if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
  225. #else
  226. if (GetViewContainerType() == AAX_eViewContainer_Type_HWND)
  227. #endif
  228. {
  229. component->setVisible (true);
  230. component->addToDesktop (0, nativeViewToAttachTo);
  231. }
  232. }
  233. }
  234. void DeleteViewContainer()
  235. {
  236. if (component != nullptr)
  237. {
  238. JUCE_AUTORELEASEPOOL
  239. component->removeFromDesktop();
  240. component = nullptr;
  241. }
  242. }
  243. virtual AAX_Result GetViewSize (AAX_Point* const viewSize) const
  244. {
  245. if (component != nullptr)
  246. {
  247. viewSize->horz = (float) component->getWidth();
  248. viewSize->vert = (float) component->getHeight();
  249. return AAX_SUCCESS;
  250. }
  251. return AAX_ERROR_NULL_OBJECT;
  252. }
  253. AAX_Result ParameterUpdated (AAX_CParamID iParameterID)
  254. {
  255. return AAX_SUCCESS;
  256. }
  257. AAX_Result SetControlHighlightInfo (AAX_CParamID iParameterID, AAX_CBoolean iIsHighlighted, AAX_EHighlightColor iColor)
  258. {
  259. return AAX_SUCCESS;
  260. }
  261. private:
  262. class ContentWrapperComponent : public Component
  263. {
  264. public:
  265. ContentWrapperComponent (AudioProcessor* plugin)
  266. {
  267. setOpaque (true);
  268. addAndMakeVisible (pluginEditor = plugin->createEditor());
  269. }
  270. ~ContentWrapperComponent()
  271. {
  272. if (pluginEditor != nullptr)
  273. {
  274. PopupMenu::dismissAllActiveMenus();
  275. pluginEditor->getAudioProcessor()->editorBeingDeleted (pluginEditor);
  276. }
  277. }
  278. void paint (Graphics& g)
  279. {
  280. g.fillAll (Colours::pink);
  281. }
  282. void resized()
  283. {
  284. if (pluginEditor != nullptr)
  285. pluginEditor->setBounds (getLocalBounds());
  286. }
  287. private:
  288. ScopedPointer<AudioProcessorEditor> pluginEditor;
  289. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent);
  290. };
  291. ScopedPointer<ContentWrapperComponent> component;
  292. JUCELibraryRefCount juceCount;
  293. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAAX_GUI);
  294. };
  295. //==============================================================================
  296. class JuceAAX_Parameters : public AAX_CEffectParameters
  297. {
  298. public:
  299. JuceAAX_Parameters()
  300. {
  301. pluginInstance = createPluginFilter();
  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. if (*(i.bypass) != 0)
  382. i.pluginInstance->bypass (i.inputChannels, i.outputChannels, *(i.bufferSize));
  383. else
  384. i.pluginInstance->process (i.inputChannels, i.outputChannels, *(i.bufferSize));
  385. }
  386. }
  387. //==============================================================================
  388. static void createDescriptor (AAX_IComponentDescriptor& desc, int numInputs, int numOutputs)
  389. {
  390. check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
  391. check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
  392. check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
  393. check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
  394. check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
  395. // Create a property map
  396. AAX_IPropertyMap* const properties = desc.NewPropertyMap();
  397. jassert (properties != nullptr);
  398. properties->AddProperty (AAX_eProperty_ManufacturerID, JucePlugin_AAXManufacturerCode);
  399. properties->AddProperty (AAX_eProperty_ProductID, JucePlugin_AAXProductId);
  400. properties->AddProperty (AAX_eProperty_CanBypass, true);
  401. properties->AddProperty (AAX_eProperty_InputStemFormat, getFormatForChans (numInputs));
  402. properties->AddProperty (AAX_eProperty_OutputStemFormat, getFormatForChans (numOutputs));
  403. properties->AddProperty (AAX_eProperty_PlugInID_Native, JucePlugin_AAXPluginId + (numInputs + 256 * numOutputs));
  404. check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
  405. }
  406. static void getPlugInDescription (AAX_IEffectDescriptor& descriptor)
  407. {
  408. descriptor.AddName (JucePlugin_Desc);
  409. descriptor.AddName (JucePlugin_Name);
  410. descriptor.AddName (FourCharConst (JucePlugin_PluginCode).asString);
  411. descriptor.AddCategory (JucePlugin_AAXCategory);
  412. check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
  413. check (descriptor.AddProcPtr ((void*) JuceAAX_Parameters::Create, kAAX_ProcPtrID_Create_EffectParameters));
  414. const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
  415. const int numConfigs = numElementsInArray (channelConfigs);
  416. // You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
  417. // value in your JucePluginCharacteristics.h file..
  418. jassert (numConfigs > 0);
  419. for (int i = 0; i < numConfigs; ++i)
  420. {
  421. AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor();
  422. if (desc != nullptr)
  423. {
  424. createDescriptor (*desc,
  425. channelConfigs [i][0],
  426. channelConfigs [i][1]);
  427. check (descriptor.AddComponent (desc));
  428. }
  429. }
  430. }
  431. };
  432. //==============================================================================
  433. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* const collection)
  434. {
  435. AAXClasses::JUCELibraryRefCount libraryRefCount;
  436. AAX_IEffectDescriptor* const descriptor = collection->NewDescriptor();
  437. if (descriptor == nullptr)
  438. return AAX_ERROR_NULL_OBJECT;
  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. #endif