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.

2463 lines
103KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For the technical preview this file cannot be licensed commercially.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. #include <juce_core/system/juce_TargetPlatform.h>
  14. #include "../utility/juce_CheckSettingMacros.h"
  15. #if JucePlugin_Build_AAX && (JUCE_INCLUDED_AAX_IN_MM || defined (_WIN32) || defined (_WIN64))
  16. #include "../utility/juce_IncludeSystemHeaders.h"
  17. #include "../utility/juce_IncludeModuleHeaders.h"
  18. #include "../utility/juce_WindowsHooks.h"
  19. #include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
  20. JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4127 4512 4996)
  21. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnon-virtual-dtor",
  22. "-Wsign-conversion",
  23. "-Wextra-semi",
  24. "-Wshift-sign-overflow",
  25. "-Wpragma-pack",
  26. "-Wzero-as-null-pointer-constant",
  27. "-Winconsistent-missing-destructor-override",
  28. "-Wfour-char-constants",
  29. "-Wtautological-overlap-compare")
  30. #include <AAX_Version.h>
  31. static_assert (AAX_SDK_CURRENT_REVISION >= AAX_SDK_2p3p0_REVISION, "JUCE requires AAX SDK version 2.3.0 or higher");
  32. #include <AAX_Exports.cpp>
  33. #include <AAX_ICollection.h>
  34. #include <AAX_IComponentDescriptor.h>
  35. #include <AAX_IEffectDescriptor.h>
  36. #include <AAX_IPropertyMap.h>
  37. #include <AAX_CEffectParameters.h>
  38. #include <AAX_Errors.h>
  39. #include <AAX_CBinaryTaperDelegate.h>
  40. #include <AAX_CBinaryDisplayDelegate.h>
  41. #include <AAX_CLinearTaperDelegate.h>
  42. #include <AAX_CNumberDisplayDelegate.h>
  43. #include <AAX_CEffectGUI.h>
  44. #include <AAX_IViewContainer.h>
  45. #include <AAX_ITransport.h>
  46. #include <AAX_IMIDINode.h>
  47. #include <AAX_UtilsNative.h>
  48. #include <AAX_Enums.h>
  49. #include <AAX_IDescriptionHost.h>
  50. #include <AAX_IFeatureInfo.h>
  51. #include <AAX_UIDs.h>
  52. #ifdef AAX_SDK_2p3p1_REVISION
  53. #if AAX_SDK_CURRENT_REVISION >= AAX_SDK_2p3p1_REVISION
  54. #include <AAX_Exception.h>
  55. #include <AAX_Assert.h>
  56. #endif
  57. #endif
  58. JUCE_END_IGNORE_WARNINGS_MSVC
  59. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  60. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfour-char-constants")
  61. #if JUCE_WINDOWS
  62. #ifndef JucePlugin_AAXLibs_path
  63. #error "You need to define the JucePlugin_AAXLibs_path macro. (This is best done via the Projucer)"
  64. #endif
  65. #if JUCE_64BIT
  66. #define JUCE_AAX_LIB "AAXLibrary_x64"
  67. #else
  68. #define JUCE_AAX_LIB "AAXLibrary"
  69. #endif
  70. #if JUCE_DEBUG
  71. #define JUCE_AAX_LIB_PATH "\\Debug\\"
  72. #define JUCE_AAX_LIB_SUFFIX "_D"
  73. #else
  74. #define JUCE_AAX_LIB_PATH "\\Release\\"
  75. #define JUCE_AAX_LIB_SUFFIX ""
  76. #endif
  77. #pragma comment(lib, JucePlugin_AAXLibs_path JUCE_AAX_LIB_PATH JUCE_AAX_LIB JUCE_AAX_LIB_SUFFIX ".lib")
  78. #endif
  79. #undef check
  80. #include "juce_AAX_Modifier_Injector.h"
  81. using namespace juce;
  82. #ifndef JucePlugin_AAX_Chunk_Identifier
  83. #define JucePlugin_AAX_Chunk_Identifier 'juce'
  84. #endif
  85. const int32_t juceChunkType = JucePlugin_AAX_Chunk_Identifier;
  86. //==============================================================================
  87. namespace AAXClasses
  88. {
  89. static int32 getAAXParamHash (AAX_CParamID paramID) noexcept
  90. {
  91. int32 result = 0;
  92. while (*paramID != 0)
  93. result = (31 * result) + (*paramID++);
  94. return result;
  95. }
  96. static void check (AAX_Result result)
  97. {
  98. jassertquiet (result == AAX_SUCCESS);
  99. }
  100. // maps a channel index of an AAX format to an index of a juce format
  101. struct AAXChannelStreamOrder
  102. {
  103. AAX_EStemFormat aaxStemFormat;
  104. AudioChannelSet::ChannelType speakerOrder[10];
  105. };
  106. static AAX_EStemFormat stemFormatForAmbisonicOrder (int order)
  107. {
  108. switch (order)
  109. {
  110. case 1: return AAX_eStemFormat_Ambi_1_ACN;
  111. case 2: return AAX_eStemFormat_Ambi_2_ACN;
  112. case 3: return AAX_eStemFormat_Ambi_3_ACN;
  113. default: break;
  114. }
  115. return AAX_eStemFormat_INT32_MAX;
  116. }
  117. static AAXChannelStreamOrder aaxChannelOrder[] =
  118. {
  119. { AAX_eStemFormat_Mono, { AudioChannelSet::centre, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown,
  120. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  121. { AAX_eStemFormat_Stereo, { AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown,
  122. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  123. { AAX_eStemFormat_LCR, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::unknown, AudioChannelSet::unknown,
  124. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  125. { AAX_eStemFormat_LCRS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::centreSurround, AudioChannelSet::unknown,
  126. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  127. { AAX_eStemFormat_Quad, { AudioChannelSet::left, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown,
  128. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  129. { AAX_eStemFormat_5_0, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround,
  130. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  131. { AAX_eStemFormat_5_1, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::rightSurround,
  132. AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  133. { AAX_eStemFormat_6_0, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::centreSurround,
  134. AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  135. { AAX_eStemFormat_6_1, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurround, AudioChannelSet::centreSurround,
  136. AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  137. { AAX_eStemFormat_7_0_SDDS, { AudioChannelSet::left, AudioChannelSet::leftCentre, AudioChannelSet::centre, AudioChannelSet::rightCentre, AudioChannelSet::right,
  138. AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  139. { AAX_eStemFormat_7_0_DTS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
  140. AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  141. { AAX_eStemFormat_7_1_SDDS, { AudioChannelSet::left, AudioChannelSet::leftCentre, AudioChannelSet::centre, AudioChannelSet::rightCentre, AudioChannelSet::right,
  142. AudioChannelSet::leftSurround, AudioChannelSet::rightSurround, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  143. { AAX_eStemFormat_7_1_DTS, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
  144. AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::LFE, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  145. { AAX_eStemFormat_7_0_2, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
  146. AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::topSideLeft, AudioChannelSet::topSideRight, AudioChannelSet::unknown } },
  147. { AAX_eStemFormat_7_1_2, { AudioChannelSet::left, AudioChannelSet::centre, AudioChannelSet::right, AudioChannelSet::leftSurroundSide, AudioChannelSet::rightSurroundSide,
  148. AudioChannelSet::leftSurroundRear, AudioChannelSet::rightSurroundRear, AudioChannelSet::LFE, AudioChannelSet::topSideLeft, AudioChannelSet::topSideRight } },
  149. { AAX_eStemFormat_None, { AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown,
  150. AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown, AudioChannelSet::unknown } },
  151. };
  152. static AAX_EStemFormat aaxFormats[] =
  153. {
  154. AAX_eStemFormat_Mono,
  155. AAX_eStemFormat_Stereo,
  156. AAX_eStemFormat_LCR,
  157. AAX_eStemFormat_LCRS,
  158. AAX_eStemFormat_Quad,
  159. AAX_eStemFormat_5_0,
  160. AAX_eStemFormat_5_1,
  161. AAX_eStemFormat_6_0,
  162. AAX_eStemFormat_6_1,
  163. AAX_eStemFormat_7_0_SDDS,
  164. AAX_eStemFormat_7_1_SDDS,
  165. AAX_eStemFormat_7_0_DTS,
  166. AAX_eStemFormat_7_1_DTS,
  167. AAX_eStemFormat_7_0_2,
  168. AAX_eStemFormat_7_1_2,
  169. AAX_eStemFormat_Ambi_1_ACN,
  170. AAX_eStemFormat_Ambi_2_ACN,
  171. AAX_eStemFormat_Ambi_3_ACN
  172. };
  173. static AAX_EStemFormat getFormatForAudioChannelSet (const AudioChannelSet& set, bool ignoreLayout) noexcept
  174. {
  175. // if the plug-in ignores layout, it is ok to convert between formats only by their numchannnels
  176. if (ignoreLayout)
  177. {
  178. auto numChannels = set.size();
  179. switch (numChannels)
  180. {
  181. case 0: return AAX_eStemFormat_None;
  182. case 1: return AAX_eStemFormat_Mono;
  183. case 2: return AAX_eStemFormat_Stereo;
  184. case 3: return AAX_eStemFormat_LCR;
  185. case 4: return AAX_eStemFormat_Quad;
  186. case 5: return AAX_eStemFormat_5_0;
  187. case 6: return AAX_eStemFormat_5_1;
  188. case 7: return AAX_eStemFormat_7_0_DTS;
  189. case 8: return AAX_eStemFormat_7_1_DTS;
  190. case 9: return AAX_eStemFormat_7_0_2;
  191. case 10: return AAX_eStemFormat_7_1_2;
  192. default: break;
  193. }
  194. // check for ambisonics support
  195. auto sqrtMinusOne = std::sqrt (static_cast<float> (numChannels)) - 1.0f;
  196. auto ambisonicOrder = jmax (0, static_cast<int> (std::floor (sqrtMinusOne)));
  197. if (static_cast<float> (ambisonicOrder) == sqrtMinusOne)
  198. return stemFormatForAmbisonicOrder (ambisonicOrder);
  199. return AAX_eStemFormat_INT32_MAX;
  200. }
  201. if (set == AudioChannelSet::disabled()) return AAX_eStemFormat_None;
  202. if (set == AudioChannelSet::mono()) return AAX_eStemFormat_Mono;
  203. if (set == AudioChannelSet::stereo()) return AAX_eStemFormat_Stereo;
  204. if (set == AudioChannelSet::createLCR()) return AAX_eStemFormat_LCR;
  205. if (set == AudioChannelSet::createLCRS()) return AAX_eStemFormat_LCRS;
  206. if (set == AudioChannelSet::quadraphonic()) return AAX_eStemFormat_Quad;
  207. if (set == AudioChannelSet::create5point0()) return AAX_eStemFormat_5_0;
  208. if (set == AudioChannelSet::create5point1()) return AAX_eStemFormat_5_1;
  209. if (set == AudioChannelSet::create6point0()) return AAX_eStemFormat_6_0;
  210. if (set == AudioChannelSet::create6point1()) return AAX_eStemFormat_6_1;
  211. if (set == AudioChannelSet::create7point0()) return AAX_eStemFormat_7_0_DTS;
  212. if (set == AudioChannelSet::create7point1()) return AAX_eStemFormat_7_1_DTS;
  213. if (set == AudioChannelSet::create7point0SDDS()) return AAX_eStemFormat_7_0_SDDS;
  214. if (set == AudioChannelSet::create7point1SDDS()) return AAX_eStemFormat_7_1_SDDS;
  215. if (set == AudioChannelSet::create7point0point2()) return AAX_eStemFormat_7_0_2;
  216. if (set == AudioChannelSet::create7point1point2()) return AAX_eStemFormat_7_1_2;
  217. auto order = set.getAmbisonicOrder();
  218. if (order >= 0)
  219. return stemFormatForAmbisonicOrder (order);
  220. return AAX_eStemFormat_INT32_MAX;
  221. }
  222. static AudioChannelSet channelSetFromStemFormat (AAX_EStemFormat format, bool ignoreLayout) noexcept
  223. {
  224. if (! ignoreLayout)
  225. {
  226. switch (format)
  227. {
  228. case AAX_eStemFormat_None: return AudioChannelSet::disabled();
  229. case AAX_eStemFormat_Mono: return AudioChannelSet::mono();
  230. case AAX_eStemFormat_Stereo: return AudioChannelSet::stereo();
  231. case AAX_eStemFormat_LCR: return AudioChannelSet::createLCR();
  232. case AAX_eStemFormat_LCRS: return AudioChannelSet::createLCRS();
  233. case AAX_eStemFormat_Quad: return AudioChannelSet::quadraphonic();
  234. case AAX_eStemFormat_5_0: return AudioChannelSet::create5point0();
  235. case AAX_eStemFormat_5_1: return AudioChannelSet::create5point1();
  236. case AAX_eStemFormat_6_0: return AudioChannelSet::create6point0();
  237. case AAX_eStemFormat_6_1: return AudioChannelSet::create6point1();
  238. case AAX_eStemFormat_7_0_SDDS: return AudioChannelSet::create7point0SDDS();
  239. case AAX_eStemFormat_7_0_DTS: return AudioChannelSet::create7point0();
  240. case AAX_eStemFormat_7_1_SDDS: return AudioChannelSet::create7point1SDDS();
  241. case AAX_eStemFormat_7_1_DTS: return AudioChannelSet::create7point1();
  242. case AAX_eStemFormat_7_0_2: return AudioChannelSet::create7point0point2();
  243. case AAX_eStemFormat_7_1_2: return AudioChannelSet::create7point1point2();
  244. case AAX_eStemFormat_Ambi_1_ACN: return AudioChannelSet::ambisonic (1);
  245. case AAX_eStemFormat_Ambi_2_ACN: return AudioChannelSet::ambisonic (2);
  246. case AAX_eStemFormat_Ambi_3_ACN: return AudioChannelSet::ambisonic (3);
  247. case AAX_eStemFormat_Reserved_1:
  248. case AAX_eStemFormat_Reserved_2:
  249. case AAX_eStemFormat_Reserved_3:
  250. case AAX_eStemFormatNum:
  251. case AAX_eStemFormat_Any:
  252. case AAX_eStemFormat_INT32_MAX:
  253. default: return AudioChannelSet::disabled();
  254. }
  255. }
  256. return AudioChannelSet::discreteChannels (jmax (0, static_cast<int> (AAX_STEM_FORMAT_CHANNEL_COUNT (format))));
  257. }
  258. static AAX_EMeterType getMeterTypeForCategory (AudioProcessorParameter::Category category)
  259. {
  260. switch (category)
  261. {
  262. case AudioProcessorParameter::inputMeter: return AAX_eMeterType_Input;
  263. case AudioProcessorParameter::outputMeter: return AAX_eMeterType_Output;
  264. case AudioProcessorParameter::compressorLimiterGainReductionMeter: return AAX_eMeterType_CLGain;
  265. case AudioProcessorParameter::expanderGateGainReductionMeter: return AAX_eMeterType_EGGain;
  266. case AudioProcessorParameter::analysisMeter: return AAX_eMeterType_Analysis;
  267. case AudioProcessorParameter::genericParameter:
  268. case AudioProcessorParameter::inputGain:
  269. case AudioProcessorParameter::outputGain:
  270. case AudioProcessorParameter::otherMeter:
  271. default: return AAX_eMeterType_Other;
  272. }
  273. }
  274. static Colour getColourFromHighlightEnum (AAX_EHighlightColor colour) noexcept
  275. {
  276. switch (colour)
  277. {
  278. case AAX_eHighlightColor_Red: return Colours::red;
  279. case AAX_eHighlightColor_Blue: return Colours::blue;
  280. case AAX_eHighlightColor_Green: return Colours::green;
  281. case AAX_eHighlightColor_Yellow: return Colours::yellow;
  282. case AAX_eHighlightColor_Num:
  283. default: jassertfalse; break;
  284. }
  285. return Colours::black;
  286. }
  287. static int juceChannelIndexToAax (int juceIndex, const AudioChannelSet& channelSet)
  288. {
  289. auto isAmbisonic = (channelSet.getAmbisonicOrder() >= 0);
  290. auto currentLayout = getFormatForAudioChannelSet (channelSet, false);
  291. int layoutIndex;
  292. if (isAmbisonic && currentLayout != AAX_eStemFormat_INT32_MAX)
  293. return juceIndex;
  294. for (layoutIndex = 0; aaxChannelOrder[layoutIndex].aaxStemFormat != currentLayout; ++layoutIndex)
  295. if (aaxChannelOrder[layoutIndex].aaxStemFormat == 0) return juceIndex;
  296. auto& channelOrder = aaxChannelOrder[layoutIndex];
  297. auto channelType = channelSet.getTypeOfChannel (static_cast<int> (juceIndex));
  298. auto numSpeakers = numElementsInArray (channelOrder.speakerOrder);
  299. for (int i = 0; i < numSpeakers && channelOrder.speakerOrder[i] != 0; ++i)
  300. if (channelOrder.speakerOrder[i] == channelType)
  301. return i;
  302. return juceIndex;
  303. }
  304. //==============================================================================
  305. class JuceAAX_Processor;
  306. struct PluginInstanceInfo
  307. {
  308. PluginInstanceInfo (JuceAAX_Processor& p) : parameters (p) {}
  309. JuceAAX_Processor& parameters;
  310. JUCE_DECLARE_NON_COPYABLE (PluginInstanceInfo)
  311. };
  312. //==============================================================================
  313. struct JUCEAlgorithmContext
  314. {
  315. float** inputChannels;
  316. float** outputChannels;
  317. int32_t* bufferSize;
  318. int32_t* bypass;
  319. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  320. AAX_IMIDINode* midiNodeIn;
  321. #endif
  322. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
  323. AAX_IMIDINode* midiNodeOut;
  324. #endif
  325. PluginInstanceInfo* pluginInstance;
  326. int32_t* isPrepared;
  327. float* const* meterTapBuffers;
  328. int32_t* sideChainBuffers;
  329. };
  330. struct JUCEAlgorithmIDs
  331. {
  332. enum
  333. {
  334. inputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, inputChannels),
  335. outputChannels = AAX_FIELD_INDEX (JUCEAlgorithmContext, outputChannels),
  336. bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
  337. bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
  338. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  339. midiNodeIn = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeIn),
  340. #endif
  341. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
  342. midiNodeOut = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeOut),
  343. #endif
  344. pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
  345. preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared),
  346. meterTapBuffers = AAX_FIELD_INDEX (JUCEAlgorithmContext, meterTapBuffers),
  347. sideChainBuffers = AAX_FIELD_INDEX (JUCEAlgorithmContext, sideChainBuffers)
  348. };
  349. };
  350. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  351. static AAX_IMIDINode* getMidiNodeIn (const JUCEAlgorithmContext& c) noexcept { return c.midiNodeIn; }
  352. #else
  353. static AAX_IMIDINode* getMidiNodeIn (const JUCEAlgorithmContext&) noexcept { return nullptr; }
  354. #endif
  355. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
  356. AAX_IMIDINode* midiNodeOut;
  357. static AAX_IMIDINode* getMidiNodeOut (const JUCEAlgorithmContext& c) noexcept { return c.midiNodeOut; }
  358. #else
  359. static AAX_IMIDINode* getMidiNodeOut (const JUCEAlgorithmContext&) noexcept { return nullptr; }
  360. #endif
  361. //==============================================================================
  362. class JuceAAX_Processor;
  363. class JuceAAX_GUI : public AAX_CEffectGUI,
  364. public ModifierKeyProvider
  365. {
  366. public:
  367. JuceAAX_GUI() = default;
  368. ~JuceAAX_GUI() override { DeleteViewContainer(); }
  369. static AAX_IEffectGUI* AAX_CALLBACK Create() { return new JuceAAX_GUI(); }
  370. void CreateViewContents() override;
  371. void CreateViewContainer() override
  372. {
  373. CreateViewContents();
  374. if (void* nativeViewToAttachTo = GetViewContainerPtr())
  375. {
  376. #if JUCE_MAC
  377. if (GetViewContainerType() == AAX_eViewContainer_Type_NSView)
  378. #else
  379. if (GetViewContainerType() == AAX_eViewContainer_Type_HWND)
  380. #endif
  381. {
  382. component->setVisible (true);
  383. component->addToDesktop (0, nativeViewToAttachTo);
  384. if (ModifierKeyReceiver* modReceiver = dynamic_cast<ModifierKeyReceiver*> (component->getPeer()))
  385. modReceiver->setModifierKeyProvider (this);
  386. }
  387. }
  388. }
  389. void DeleteViewContainer() override
  390. {
  391. if (component != nullptr)
  392. {
  393. JUCE_AUTORELEASEPOOL
  394. {
  395. if (auto* modReceiver = dynamic_cast<ModifierKeyReceiver*> (component->getPeer()))
  396. modReceiver->removeModifierKeyProvider();
  397. component->removeFromDesktop();
  398. component = nullptr;
  399. }
  400. }
  401. }
  402. AAX_Result GetViewSize (AAX_Point* viewSize) const override
  403. {
  404. if (component != nullptr)
  405. {
  406. *viewSize = convertToHostBounds ({ (float) component->getHeight(),
  407. (float) component->getWidth() });
  408. return AAX_SUCCESS;
  409. }
  410. return AAX_ERROR_NULL_OBJECT;
  411. }
  412. AAX_Result ParameterUpdated (AAX_CParamID) override
  413. {
  414. return AAX_SUCCESS;
  415. }
  416. AAX_Result SetControlHighlightInfo (AAX_CParamID paramID, AAX_CBoolean isHighlighted, AAX_EHighlightColor colour) override
  417. {
  418. if (component != nullptr && component->pluginEditor != nullptr)
  419. {
  420. auto index = getParamIndexFromID (paramID);
  421. if (index >= 0)
  422. {
  423. AudioProcessorEditor::ParameterControlHighlightInfo info;
  424. info.parameterIndex = index;
  425. info.isHighlighted = (isHighlighted != 0);
  426. info.suggestedColour = getColourFromHighlightEnum (colour);
  427. component->pluginEditor->setControlHighlight (info);
  428. }
  429. return AAX_SUCCESS;
  430. }
  431. return AAX_ERROR_NULL_OBJECT;
  432. }
  433. int getWin32Modifiers() const override
  434. {
  435. int modifierFlags = 0;
  436. if (auto* viewContainer = GetViewContainer())
  437. {
  438. uint32 aaxViewMods = 0;
  439. const_cast<AAX_IViewContainer*> (viewContainer)->GetModifiers (&aaxViewMods);
  440. if ((aaxViewMods & AAX_eModifiers_Shift) != 0) modifierFlags |= ModifierKeys::shiftModifier;
  441. if ((aaxViewMods & AAX_eModifiers_Alt ) != 0) modifierFlags |= ModifierKeys::altModifier;
  442. }
  443. return modifierFlags;
  444. }
  445. private:
  446. //==============================================================================
  447. int getParamIndexFromID (AAX_CParamID paramID) const noexcept;
  448. AAX_CParamID getAAXParamIDFromJuceIndex (int index) const noexcept;
  449. //==============================================================================
  450. static AAX_Point convertToHostBounds (AAX_Point pluginSize)
  451. {
  452. auto desktopScale = Desktop::getInstance().getGlobalScaleFactor();
  453. if (approximatelyEqual (desktopScale, 1.0f))
  454. return pluginSize;
  455. return { pluginSize.vert * desktopScale,
  456. pluginSize.horz * desktopScale };
  457. }
  458. //==============================================================================
  459. struct ContentWrapperComponent : public Component
  460. {
  461. ContentWrapperComponent (JuceAAX_GUI& gui, AudioProcessor& plugin)
  462. : owner (gui)
  463. {
  464. setOpaque (true);
  465. setBroughtToFrontOnMouseClick (true);
  466. pluginEditor.reset (plugin.createEditorIfNeeded());
  467. addAndMakeVisible (pluginEditor.get());
  468. if (pluginEditor != nullptr)
  469. {
  470. lastValidSize = pluginEditor->getLocalBounds();
  471. setBounds (lastValidSize);
  472. pluginEditor->addMouseListener (this, true);
  473. }
  474. }
  475. ~ContentWrapperComponent() override
  476. {
  477. if (pluginEditor != nullptr)
  478. {
  479. PopupMenu::dismissAllActiveMenus();
  480. pluginEditor->removeMouseListener (this);
  481. pluginEditor->processor.editorBeingDeleted (pluginEditor.get());
  482. }
  483. }
  484. void paint (Graphics& g) override
  485. {
  486. g.fillAll (Colours::black);
  487. }
  488. template <typename MethodType>
  489. void callMouseMethod (const MouseEvent& e, MethodType method)
  490. {
  491. if (auto* vc = owner.GetViewContainer())
  492. {
  493. auto parameterIndex = pluginEditor->getControlParameterIndex (*e.eventComponent);
  494. if (auto aaxParamID = owner.getAAXParamIDFromJuceIndex (parameterIndex))
  495. {
  496. uint32_t mods = 0;
  497. vc->GetModifiers (&mods);
  498. (vc->*method) (aaxParamID, mods);
  499. }
  500. }
  501. }
  502. void mouseDown (const MouseEvent& e) override { callMouseMethod (e, &AAX_IViewContainer::HandleParameterMouseDown); }
  503. void mouseUp (const MouseEvent& e) override { callMouseMethod (e, &AAX_IViewContainer::HandleParameterMouseUp); }
  504. void mouseDrag (const MouseEvent& e) override { callMouseMethod (e, &AAX_IViewContainer::HandleParameterMouseDrag); }
  505. void parentSizeChanged() override
  506. {
  507. resizeHostWindow();
  508. if (pluginEditor != nullptr)
  509. pluginEditor->repaint();
  510. }
  511. void childBoundsChanged (Component*) override
  512. {
  513. if (resizeHostWindow())
  514. {
  515. setSize (pluginEditor->getWidth(), pluginEditor->getHeight());
  516. lastValidSize = getBounds();
  517. }
  518. else
  519. {
  520. pluginEditor->setBoundsConstrained (pluginEditor->getBounds().withSize (lastValidSize.getWidth(),
  521. lastValidSize.getHeight()));
  522. }
  523. }
  524. bool resizeHostWindow()
  525. {
  526. if (pluginEditor != nullptr)
  527. {
  528. auto newSize = convertToHostBounds ({ (float) pluginEditor->getHeight(),
  529. (float) pluginEditor->getWidth() });
  530. return owner.GetViewContainer()->SetViewSize (newSize) == AAX_SUCCESS;
  531. }
  532. return false;
  533. }
  534. std::unique_ptr<AudioProcessorEditor> pluginEditor;
  535. JuceAAX_GUI& owner;
  536. #if JUCE_WINDOWS
  537. WindowsHooks hooks;
  538. #endif
  539. juce::Rectangle<int> lastValidSize;
  540. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent)
  541. };
  542. std::unique_ptr<ContentWrapperComponent> component;
  543. ScopedJuceInitialiser_GUI libraryInitialiser;
  544. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceAAX_GUI)
  545. };
  546. // Copied here, because not all versions of the AAX SDK define all of these values
  547. enum JUCE_AAX_EFrameRate : std::underlying_type_t<AAX_EFrameRate>
  548. {
  549. JUCE_AAX_eFrameRate_Undeclared = 0,
  550. JUCE_AAX_eFrameRate_24Frame = 1,
  551. JUCE_AAX_eFrameRate_25Frame = 2,
  552. JUCE_AAX_eFrameRate_2997NonDrop = 3,
  553. JUCE_AAX_eFrameRate_2997DropFrame = 4,
  554. JUCE_AAX_eFrameRate_30NonDrop = 5,
  555. JUCE_AAX_eFrameRate_30DropFrame = 6,
  556. JUCE_AAX_eFrameRate_23976 = 7,
  557. JUCE_AAX_eFrameRate_47952 = 8,
  558. JUCE_AAX_eFrameRate_48Frame = 9,
  559. JUCE_AAX_eFrameRate_50Frame = 10,
  560. JUCE_AAX_eFrameRate_5994NonDrop = 11,
  561. JUCE_AAX_eFrameRate_5994DropFrame = 12,
  562. JUCE_AAX_eFrameRate_60NonDrop = 13,
  563. JUCE_AAX_eFrameRate_60DropFrame = 14,
  564. JUCE_AAX_eFrameRate_100Frame = 15,
  565. JUCE_AAX_eFrameRate_11988NonDrop = 16,
  566. JUCE_AAX_eFrameRate_11988DropFrame = 17,
  567. JUCE_AAX_eFrameRate_120NonDrop = 18,
  568. JUCE_AAX_eFrameRate_120DropFrame = 19
  569. };
  570. static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[], const void* const instancesEnd);
  571. static Array<JuceAAX_Processor*> activeProcessors;
  572. //==============================================================================
  573. class JuceAAX_Processor : public AAX_CEffectParameters,
  574. public juce::AudioPlayHead,
  575. public AudioProcessorListener,
  576. private AsyncUpdater
  577. {
  578. public:
  579. JuceAAX_Processor()
  580. : pluginInstance (createPluginFilterOfType (AudioProcessor::wrapperType_AAX))
  581. {
  582. inParameterChangedCallback = false;
  583. pluginInstance->setPlayHead (this);
  584. pluginInstance->addListener (this);
  585. rebuildChannelMapArrays();
  586. AAX_CEffectParameters::GetNumberOfChunks (&juceChunkIndex);
  587. activeProcessors.add (this);
  588. }
  589. ~JuceAAX_Processor() override
  590. {
  591. activeProcessors.removeAllInstancesOf (this);
  592. }
  593. static AAX_CEffectParameters* AAX_CALLBACK Create()
  594. {
  595. PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_AAX;
  596. if (PluginHostType::jucePlugInIsRunningInAudioSuiteFn == nullptr)
  597. {
  598. PluginHostType::jucePlugInIsRunningInAudioSuiteFn = [] (AudioProcessor& processor)
  599. {
  600. for (auto* p : activeProcessors)
  601. if (&p->getPluginInstance() == &processor)
  602. return p->isInAudioSuite();
  603. return false;
  604. };
  605. }
  606. return new JuceAAX_Processor();
  607. }
  608. AAX_Result Uninitialize() override
  609. {
  610. cancelPendingUpdate();
  611. juceParameters.clear();
  612. if (isPrepared && pluginInstance != nullptr)
  613. {
  614. isPrepared = false;
  615. processingSidechainChange = false;
  616. pluginInstance->releaseResources();
  617. }
  618. return AAX_CEffectParameters::Uninitialize();
  619. }
  620. AAX_Result EffectInit() override
  621. {
  622. cancelPendingUpdate();
  623. check (Controller()->GetSampleRate (&sampleRate));
  624. processingSidechainChange = false;
  625. auto err = preparePlugin();
  626. if (err != AAX_SUCCESS)
  627. return err;
  628. addAudioProcessorParameters();
  629. return AAX_SUCCESS;
  630. }
  631. AAX_Result GetNumberOfChunks (int32_t* numChunks) const override
  632. {
  633. // The juceChunk is the last chunk.
  634. *numChunks = juceChunkIndex + 1;
  635. return AAX_SUCCESS;
  636. }
  637. AAX_Result GetChunkIDFromIndex (int32_t index, AAX_CTypeID* chunkID) const override
  638. {
  639. if (index != juceChunkIndex)
  640. return AAX_CEffectParameters::GetChunkIDFromIndex (index, chunkID);
  641. *chunkID = juceChunkType;
  642. return AAX_SUCCESS;
  643. }
  644. AAX_Result GetChunkSize (AAX_CTypeID chunkID, uint32_t* oSize) const override
  645. {
  646. if (chunkID != juceChunkType)
  647. return AAX_CEffectParameters::GetChunkSize (chunkID, oSize);
  648. auto& chunkMemoryBlock = perThreadFilterData.get();
  649. chunkMemoryBlock.data.reset();
  650. pluginInstance->getStateInformation (chunkMemoryBlock.data);
  651. chunkMemoryBlock.isValid = true;
  652. *oSize = (uint32_t) chunkMemoryBlock.data.getSize();
  653. return AAX_SUCCESS;
  654. }
  655. AAX_Result GetChunk (AAX_CTypeID chunkID, AAX_SPlugInChunk* oChunk) const override
  656. {
  657. if (chunkID != juceChunkType)
  658. return AAX_CEffectParameters::GetChunk (chunkID, oChunk);
  659. auto& chunkMemoryBlock = perThreadFilterData.get();
  660. if (! chunkMemoryBlock.isValid)
  661. return 20700; // AAX_ERROR_PLUGIN_API_INVALID_THREAD
  662. oChunk->fSize = (int32_t) chunkMemoryBlock.data.getSize();
  663. chunkMemoryBlock.data.copyTo (oChunk->fData, 0, chunkMemoryBlock.data.getSize());
  664. chunkMemoryBlock.isValid = false;
  665. return AAX_SUCCESS;
  666. }
  667. AAX_Result SetChunk (AAX_CTypeID chunkID, const AAX_SPlugInChunk* chunk) override
  668. {
  669. if (chunkID != juceChunkType)
  670. return AAX_CEffectParameters::SetChunk (chunkID, chunk);
  671. pluginInstance->setStateInformation ((void*) chunk->fData, chunk->fSize);
  672. // Notify Pro Tools that the parameters were updated.
  673. // Without it a bug happens in these circumstances:
  674. // * A preset is saved with the RTAS version of the plugin (".tfx" preset format).
  675. // * The preset is loaded in PT 10 using the AAX version.
  676. // * The session is then saved, and closed.
  677. // * The saved session is loaded, but acting as if the preset was never loaded.
  678. // IMPORTANT! If the plugin doesn't manage its own bypass parameter, don't try
  679. // to overwrite the bypass parameter value.
  680. auto numParameters = juceParameters.getNumParameters();
  681. for (int i = 0; i < numParameters; ++i)
  682. if (auto* juceParam = juceParameters.getParamForIndex (i))
  683. if (juceParam != ownedBypassParameter.get())
  684. if (auto paramID = getAAXParamIDFromJuceIndex (i))
  685. SetParameterNormalizedValue (paramID, juceParam->getValue());
  686. return AAX_SUCCESS;
  687. }
  688. AAX_Result ResetFieldData (AAX_CFieldIndex fieldIndex, void* data, uint32_t dataSize) const override
  689. {
  690. switch (fieldIndex)
  691. {
  692. case JUCEAlgorithmIDs::pluginInstance:
  693. {
  694. auto numObjects = dataSize / sizeof (PluginInstanceInfo);
  695. auto* objects = static_cast<PluginInstanceInfo*> (data);
  696. jassert (numObjects == 1); // not sure how to handle more than one..
  697. for (size_t i = 0; i < numObjects; ++i)
  698. new (objects + i) PluginInstanceInfo (const_cast<JuceAAX_Processor&> (*this));
  699. break;
  700. }
  701. case JUCEAlgorithmIDs::preparedFlag:
  702. {
  703. const_cast<JuceAAX_Processor*>(this)->preparePlugin();
  704. auto numObjects = dataSize / sizeof (uint32_t);
  705. auto* objects = static_cast<uint32_t*> (data);
  706. for (size_t i = 0; i < numObjects; ++i)
  707. objects[i] = 1;
  708. break;
  709. }
  710. case JUCEAlgorithmIDs::meterTapBuffers:
  711. {
  712. // this is a dummy field only when there are no aaxMeters
  713. jassert (aaxMeters.size() == 0);
  714. {
  715. auto numObjects = dataSize / sizeof (float*);
  716. auto* objects = static_cast<float**> (data);
  717. for (size_t i = 0; i < numObjects; ++i)
  718. objects[i] = nullptr;
  719. }
  720. break;
  721. }
  722. }
  723. return AAX_SUCCESS;
  724. }
  725. void setAudioProcessorParameter (AAX_CParamID paramID, double value)
  726. {
  727. if (auto* param = getParameterFromID (paramID))
  728. {
  729. auto newValue = static_cast<float> (value);
  730. if (newValue != param->getValue())
  731. {
  732. param->setValue (newValue);
  733. inParameterChangedCallback = true;
  734. param->sendValueChangedMessageToListeners (newValue);
  735. }
  736. }
  737. }
  738. AAX_Result GetNumberOfChanges (int32_t* numChanges) const override
  739. {
  740. const auto result = AAX_CEffectParameters::GetNumberOfChanges (numChanges);
  741. *numChanges += numSetDirtyCalls;
  742. return result;
  743. }
  744. AAX_Result UpdateParameterNormalizedValue (AAX_CParamID paramID, double value, AAX_EUpdateSource source) override
  745. {
  746. auto result = AAX_CEffectParameters::UpdateParameterNormalizedValue (paramID, value, source);
  747. setAudioProcessorParameter (paramID, value);
  748. return result;
  749. }
  750. AAX_Result GetParameterValueFromString (AAX_CParamID paramID, double* result, const AAX_IString& text) const override
  751. {
  752. if (auto* param = getParameterFromID (paramID))
  753. {
  754. if (! LegacyAudioParameter::isLegacy (param))
  755. {
  756. *result = param->getValueForText (text.Get());
  757. return AAX_SUCCESS;
  758. }
  759. }
  760. return AAX_CEffectParameters::GetParameterValueFromString (paramID, result, text);
  761. }
  762. AAX_Result GetParameterStringFromValue (AAX_CParamID paramID, double value, AAX_IString* result, int32_t maxLen) const override
  763. {
  764. if (auto* param = getParameterFromID (paramID))
  765. result->Set (param->getText ((float) value, maxLen).toRawUTF8());
  766. return AAX_SUCCESS;
  767. }
  768. AAX_Result GetParameterNumberofSteps (AAX_CParamID paramID, int32_t* result) const
  769. {
  770. if (auto* param = getParameterFromID (paramID))
  771. *result = getSafeNumberOfParameterSteps (*param);
  772. return AAX_SUCCESS;
  773. }
  774. AAX_Result GetParameterNormalizedValue (AAX_CParamID paramID, double* result) const override
  775. {
  776. if (auto* param = getParameterFromID (paramID))
  777. *result = (double) param->getValue();
  778. else
  779. *result = 0.0;
  780. return AAX_SUCCESS;
  781. }
  782. AAX_Result SetParameterNormalizedValue (AAX_CParamID paramID, double newValue) override
  783. {
  784. if (auto* p = mParameterManager.GetParameterByID (paramID))
  785. p->SetValueWithFloat ((float) newValue);
  786. setAudioProcessorParameter (paramID, (float) newValue);
  787. return AAX_SUCCESS;
  788. }
  789. AAX_Result SetParameterNormalizedRelative (AAX_CParamID paramID, double newDeltaValue) override
  790. {
  791. if (auto* param = getParameterFromID (paramID))
  792. {
  793. auto newValue = param->getValue() + (float) newDeltaValue;
  794. setAudioProcessorParameter (paramID, jlimit (0.0f, 1.0f, newValue));
  795. if (auto* p = mParameterManager.GetParameterByID (paramID))
  796. p->SetValueWithFloat (newValue);
  797. }
  798. return AAX_SUCCESS;
  799. }
  800. AAX_Result GetParameterNameOfLength (AAX_CParamID paramID, AAX_IString* result, int32_t maxLen) const override
  801. {
  802. if (auto* param = getParameterFromID (paramID))
  803. result->Set (param->getName (maxLen).toRawUTF8());
  804. return AAX_SUCCESS;
  805. }
  806. AAX_Result GetParameterName (AAX_CParamID paramID, AAX_IString* result) const override
  807. {
  808. if (auto* param = getParameterFromID (paramID))
  809. result->Set (param->getName (31).toRawUTF8());
  810. return AAX_SUCCESS;
  811. }
  812. AAX_Result GetParameterDefaultNormalizedValue (AAX_CParamID paramID, double* result) const override
  813. {
  814. if (auto* param = getParameterFromID (paramID))
  815. *result = (double) param->getDefaultValue();
  816. else
  817. *result = 0.0;
  818. jassert (*result >= 0 && *result <= 1.0f);
  819. return AAX_SUCCESS;
  820. }
  821. AudioProcessor& getPluginInstance() const noexcept { return *pluginInstance; }
  822. bool getCurrentPosition (juce::AudioPlayHead::CurrentPositionInfo& info) override
  823. {
  824. const AAX_ITransport& transport = *Transport();
  825. info.bpm = 0.0;
  826. check (transport.GetCurrentTempo (&info.bpm));
  827. int32_t num = 4, den = 4;
  828. transport.GetCurrentMeter (&num, &den);
  829. info.timeSigNumerator = (int) num;
  830. info.timeSigDenominator = (int) den;
  831. info.timeInSamples = 0;
  832. if (transport.IsTransportPlaying (&info.isPlaying) != AAX_SUCCESS)
  833. info.isPlaying = false;
  834. if (info.isPlaying
  835. || transport.GetTimelineSelectionStartPosition (&info.timeInSamples) != AAX_SUCCESS)
  836. check (transport.GetCurrentNativeSampleLocation (&info.timeInSamples));
  837. info.timeInSeconds = (float) info.timeInSamples / sampleRate;
  838. int64_t ticks = 0;
  839. if (info.isPlaying)
  840. check (transport.GetCustomTickPosition (&ticks, info.timeInSamples));
  841. else
  842. check (transport.GetCurrentTickPosition (&ticks));
  843. info.ppqPosition = (double) ticks / 960000.0;
  844. info.isLooping = false;
  845. int64_t loopStartTick = 0, loopEndTick = 0;
  846. check (transport.GetCurrentLoopPosition (&info.isLooping, &loopStartTick, &loopEndTick));
  847. info.ppqLoopStart = (double) loopStartTick / 960000.0;
  848. info.ppqLoopEnd = (double) loopEndTick / 960000.0;
  849. std::tie (info.frameRate, info.editOriginTime) = [&transport]
  850. {
  851. AAX_EFrameRate frameRate;
  852. int32_t offset;
  853. if (transport.GetTimeCodeInfo (&frameRate, &offset) != AAX_SUCCESS)
  854. return std::make_tuple (FrameRate(), 0.0);
  855. const auto rate = [&]
  856. {
  857. switch ((JUCE_AAX_EFrameRate) frameRate)
  858. {
  859. case JUCE_AAX_eFrameRate_24Frame: return FrameRate().withBaseRate (24);
  860. case JUCE_AAX_eFrameRate_23976: return FrameRate().withBaseRate (24).withPullDown();
  861. case JUCE_AAX_eFrameRate_25Frame: return FrameRate().withBaseRate (25);
  862. case JUCE_AAX_eFrameRate_30NonDrop: return FrameRate().withBaseRate (30);
  863. case JUCE_AAX_eFrameRate_30DropFrame: return FrameRate().withBaseRate (30).withDrop();
  864. case JUCE_AAX_eFrameRate_2997NonDrop: return FrameRate().withBaseRate (30).withPullDown();
  865. case JUCE_AAX_eFrameRate_2997DropFrame: return FrameRate().withBaseRate (30).withPullDown().withDrop();
  866. case JUCE_AAX_eFrameRate_48Frame: return FrameRate().withBaseRate (48);
  867. case JUCE_AAX_eFrameRate_47952: return FrameRate().withBaseRate (48).withPullDown();
  868. case JUCE_AAX_eFrameRate_50Frame: return FrameRate().withBaseRate (50);
  869. case JUCE_AAX_eFrameRate_60NonDrop: return FrameRate().withBaseRate (60);
  870. case JUCE_AAX_eFrameRate_60DropFrame: return FrameRate().withBaseRate (60).withDrop();
  871. case JUCE_AAX_eFrameRate_5994NonDrop: return FrameRate().withBaseRate (60).withPullDown();
  872. case JUCE_AAX_eFrameRate_5994DropFrame: return FrameRate().withBaseRate (60).withPullDown().withDrop();
  873. case JUCE_AAX_eFrameRate_100Frame: return FrameRate().withBaseRate (100);
  874. case JUCE_AAX_eFrameRate_120NonDrop: return FrameRate().withBaseRate (120);
  875. case JUCE_AAX_eFrameRate_120DropFrame: return FrameRate().withBaseRate (120).withDrop();
  876. case JUCE_AAX_eFrameRate_11988NonDrop: return FrameRate().withBaseRate (120).withPullDown();
  877. case JUCE_AAX_eFrameRate_11988DropFrame: return FrameRate().withBaseRate (120).withPullDown().withDrop();
  878. case JUCE_AAX_eFrameRate_Undeclared: break;
  879. }
  880. return FrameRate();
  881. }();
  882. const auto effectiveRate = rate.getEffectiveRate();
  883. return std::make_tuple (rate, effectiveRate != 0.0 ? offset / effectiveRate : 0.0);
  884. }();
  885. // No way to get these: (?)
  886. info.isRecording = false;
  887. info.ppqPositionOfLastBarStart = 0;
  888. return true;
  889. }
  890. void audioProcessorParameterChanged (AudioProcessor* /*processor*/, int parameterIndex, float newValue) override
  891. {
  892. if (inParameterChangedCallback.get())
  893. {
  894. inParameterChangedCallback = false;
  895. return;
  896. }
  897. if (auto paramID = getAAXParamIDFromJuceIndex (parameterIndex))
  898. SetParameterNormalizedValue (paramID, (double) newValue);
  899. }
  900. void audioProcessorChanged (AudioProcessor* processor, const ChangeDetails& details) override
  901. {
  902. ++mNumPlugInChanges;
  903. if (details.parameterInfoChanged)
  904. {
  905. auto numParameters = juceParameters.getNumParameters();
  906. for (int i = 0; i < numParameters; ++i)
  907. {
  908. if (auto* p = mParameterManager.GetParameterByID (getAAXParamIDFromJuceIndex (i)))
  909. {
  910. auto newName = juceParameters.getParamForIndex (i)->getName (31);
  911. if (p->Name() != newName.toRawUTF8())
  912. p->SetName (AAX_CString (newName.toRawUTF8()));
  913. }
  914. }
  915. }
  916. if (details.latencyChanged)
  917. check (Controller()->SetSignalLatency (processor->getLatencySamples()));
  918. if (details.nonParameterStateChanged)
  919. ++numSetDirtyCalls;
  920. }
  921. void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex) override
  922. {
  923. if (auto paramID = getAAXParamIDFromJuceIndex (parameterIndex))
  924. TouchParameter (paramID);
  925. }
  926. void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int parameterIndex) override
  927. {
  928. if (auto paramID = getAAXParamIDFromJuceIndex (parameterIndex))
  929. ReleaseParameter (paramID);
  930. }
  931. AAX_Result NotificationReceived (AAX_CTypeID type, const void* data, uint32_t size) override
  932. {
  933. switch (type)
  934. {
  935. case AAX_eNotificationEvent_EnteringOfflineMode: pluginInstance->setNonRealtime (true); break;
  936. case AAX_eNotificationEvent_ExitingOfflineMode: pluginInstance->setNonRealtime (false); break;
  937. case AAX_eNotificationEvent_ASProcessingState:
  938. {
  939. if (data != nullptr && size == sizeof (AAX_EProcessingState))
  940. {
  941. const auto state = *static_cast<const AAX_EProcessingState*> (data);
  942. const auto nonRealtime = state == AAX_eProcessingState_Start
  943. || state == AAX_eProcessingState_StartPass
  944. || state == AAX_eProcessingState_BeginPassGroup;
  945. pluginInstance->setNonRealtime (nonRealtime);
  946. }
  947. break;
  948. }
  949. case AAX_eNotificationEvent_TrackNameChanged:
  950. if (data != nullptr)
  951. {
  952. AudioProcessor::TrackProperties props;
  953. props.name = String::fromUTF8 (static_cast<const AAX_IString*> (data)->Get());
  954. pluginInstance->updateTrackProperties (props);
  955. }
  956. break;
  957. case AAX_eNotificationEvent_SideChainBeingConnected:
  958. case AAX_eNotificationEvent_SideChainBeingDisconnected:
  959. {
  960. processingSidechainChange = true;
  961. sidechainDesired = (type == AAX_eNotificationEvent_SideChainBeingConnected);
  962. updateSidechainState();
  963. break;
  964. }
  965. }
  966. return AAX_CEffectParameters::NotificationReceived (type, data, size);
  967. }
  968. const float* getAudioBufferForInput (const float* const* inputs, int sidechain, int mainNumIns, int idx) const noexcept
  969. {
  970. jassert (idx < (mainNumIns + 1));
  971. if (idx < mainNumIns)
  972. return inputs[inputLayoutMap[idx]];
  973. return (sidechain != -1 ? inputs[sidechain] : sideChainBuffer.getData());
  974. }
  975. void process (const float* const* inputs, float* const* outputs, const int sideChainBufferIdx,
  976. const int bufferSize, const bool bypass,
  977. AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut,
  978. float* const meterBuffers)
  979. {
  980. auto numIns = pluginInstance->getTotalNumInputChannels();
  981. auto numOuts = pluginInstance->getTotalNumOutputChannels();
  982. auto numMeters = aaxMeters.size();
  983. const ScopedLock sl (pluginInstance->getCallbackLock());
  984. bool isSuspended = [this, sideChainBufferIdx]
  985. {
  986. if (processingSidechainChange)
  987. return true;
  988. bool processWantsSidechain = (sideChainBufferIdx != -1);
  989. if (hasSidechain && canDisableSidechain && (sidechainDesired != processWantsSidechain))
  990. {
  991. sidechainDesired = processWantsSidechain;
  992. processingSidechainChange = true;
  993. triggerAsyncUpdate();
  994. return true;
  995. }
  996. return pluginInstance->isSuspended();
  997. }();
  998. if (isSuspended)
  999. {
  1000. for (int i = 0; i < numOuts; ++i)
  1001. FloatVectorOperations::clear (outputs[i], bufferSize);
  1002. if (meterBuffers != nullptr)
  1003. FloatVectorOperations::clear (meterBuffers, numMeters);
  1004. }
  1005. else
  1006. {
  1007. auto mainNumIns = pluginInstance->getMainBusNumInputChannels();
  1008. auto sidechain = (pluginInstance->getChannelCountOfBus (true, 1) > 0 ? sideChainBufferIdx : -1);
  1009. auto numChans = jmax (numIns, numOuts);
  1010. if (numChans == 0)
  1011. return;
  1012. if (channelList.size() <= numChans)
  1013. channelList.insertMultiple (-1, nullptr, 1 + numChans - channelList.size());
  1014. float** channels = channelList.getRawDataPointer();
  1015. if (numOuts >= numIns)
  1016. {
  1017. for (int i = 0; i < numOuts; ++i)
  1018. channels[i] = outputs[outputLayoutMap[i]];
  1019. for (int i = 0; i < numIns; ++i)
  1020. memcpy (channels[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float));
  1021. for (int i = numIns; i < numOuts; ++i)
  1022. zeromem (channels[i], (size_t) bufferSize * sizeof (float));
  1023. process (channels, numOuts, bufferSize, bypass, midiNodeIn, midiNodesOut);
  1024. }
  1025. else
  1026. {
  1027. for (int i = 0; i < numOuts; ++i)
  1028. channels[i] = outputs[outputLayoutMap[i]];
  1029. for (int i = 0; i < numOuts; ++i)
  1030. memcpy (channels[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float));
  1031. for (int i = numOuts; i < numIns; ++i)
  1032. channels[i] = const_cast<float*> (getAudioBufferForInput (inputs, sidechain, mainNumIns, i));
  1033. process (channels, numIns, bufferSize, bypass, midiNodeIn, midiNodesOut);
  1034. }
  1035. if (meterBuffers != nullptr)
  1036. for (int i = 0; i < numMeters; ++i)
  1037. meterBuffers[i] = aaxMeters[i]->getValue();
  1038. }
  1039. }
  1040. //==============================================================================
  1041. // In aax, the format of the aux and sidechain buses need to be fully determined
  1042. // by the format on the main buses. This function tried to provide such a mapping.
  1043. // Returns false if the in/out main layout is not supported
  1044. static bool fullBusesLayoutFromMainLayout (const AudioProcessor& p,
  1045. const AudioChannelSet& mainInput, const AudioChannelSet& mainOutput,
  1046. AudioProcessor::BusesLayout& fullLayout)
  1047. {
  1048. auto currentLayout = getDefaultLayout (p, true);
  1049. bool success = p.checkBusesLayoutSupported (currentLayout);
  1050. jassertquiet (success);
  1051. auto numInputBuses = p.getBusCount (true);
  1052. auto numOutputBuses = p.getBusCount (false);
  1053. if (auto* bus = p.getBus (true, 0))
  1054. if (! bus->isLayoutSupported (mainInput, &currentLayout))
  1055. return false;
  1056. if (auto* bus = p.getBus (false, 0))
  1057. if (! bus->isLayoutSupported (mainOutput, &currentLayout))
  1058. return false;
  1059. // did this change the input again
  1060. if (numInputBuses > 0 && currentLayout.inputBuses.getReference (0) != mainInput)
  1061. return false;
  1062. #ifdef JucePlugin_PreferredChannelConfigurations
  1063. short configs[][2] = { JucePlugin_PreferredChannelConfigurations };
  1064. if (! AudioProcessor::containsLayout (currentLayout, configs))
  1065. return false;
  1066. #endif
  1067. bool foundValid = false;
  1068. {
  1069. auto onlyMains = currentLayout;
  1070. for (int i = 1; i < numInputBuses; ++i)
  1071. onlyMains.inputBuses.getReference (i) = AudioChannelSet::disabled();
  1072. for (int i = 1; i < numOutputBuses; ++i)
  1073. onlyMains.outputBuses.getReference (i) = AudioChannelSet::disabled();
  1074. if (p.checkBusesLayoutSupported (onlyMains))
  1075. {
  1076. foundValid = true;
  1077. fullLayout = onlyMains;
  1078. }
  1079. }
  1080. if (numInputBuses > 1)
  1081. {
  1082. // can the first bus be a sidechain or disabled, if not then we can't use this layout combination
  1083. if (auto* bus = p.getBus (true, 1))
  1084. if (! bus->isLayoutSupported (AudioChannelSet::mono(), &currentLayout) && ! bus->isLayoutSupported (AudioChannelSet::disabled(), &currentLayout))
  1085. return foundValid;
  1086. // can all the other inputs be disabled, if not then we can't use this layout combination
  1087. for (int i = 2; i < numInputBuses; ++i)
  1088. if (auto* bus = p.getBus (true, i))
  1089. if (! bus->isLayoutSupported (AudioChannelSet::disabled(), &currentLayout))
  1090. return foundValid;
  1091. if (auto* bus = p.getBus (true, 0))
  1092. if (! bus->isLayoutSupported (mainInput, &currentLayout))
  1093. return foundValid;
  1094. if (auto* bus = p.getBus (false, 0))
  1095. if (! bus->isLayoutSupported (mainOutput, &currentLayout))
  1096. return foundValid;
  1097. // recheck if the format is correct
  1098. if ((numInputBuses > 0 && currentLayout.inputBuses .getReference (0) != mainInput)
  1099. || (numOutputBuses > 0 && currentLayout.outputBuses.getReference (0) != mainOutput))
  1100. return foundValid;
  1101. auto& sidechainBus = currentLayout.inputBuses.getReference (1);
  1102. if (sidechainBus != AudioChannelSet::mono() && sidechainBus != AudioChannelSet::disabled())
  1103. return foundValid;
  1104. for (int i = 2; i < numInputBuses; ++i)
  1105. if (! currentLayout.inputBuses.getReference (i).isDisabled())
  1106. return foundValid;
  1107. }
  1108. const bool hasSidechain = (numInputBuses > 1 && currentLayout.inputBuses.getReference (1) == AudioChannelSet::mono());
  1109. if (hasSidechain)
  1110. {
  1111. auto onlyMainsAndSidechain = currentLayout;
  1112. for (int i = 1; i < numOutputBuses; ++i)
  1113. onlyMainsAndSidechain.outputBuses.getReference (i) = AudioChannelSet::disabled();
  1114. if (p.checkBusesLayoutSupported (onlyMainsAndSidechain))
  1115. {
  1116. foundValid = true;
  1117. fullLayout = onlyMainsAndSidechain;
  1118. }
  1119. }
  1120. if (numOutputBuses > 1)
  1121. {
  1122. auto copy = currentLayout;
  1123. int maxAuxBuses = jmin (16, numOutputBuses);
  1124. for (int i = 1; i < maxAuxBuses; ++i)
  1125. copy.outputBuses.getReference (i) = mainOutput;
  1126. for (int i = maxAuxBuses; i < numOutputBuses; ++i)
  1127. copy.outputBuses.getReference (i) = AudioChannelSet::disabled();
  1128. if (p.checkBusesLayoutSupported (copy))
  1129. {
  1130. fullLayout = copy;
  1131. foundValid = true;
  1132. }
  1133. else
  1134. {
  1135. for (int i = 1; i < maxAuxBuses; ++i)
  1136. if (currentLayout.outputBuses.getReference (i).isDisabled())
  1137. return foundValid;
  1138. for (int i = maxAuxBuses; i < numOutputBuses; ++i)
  1139. if (auto* bus = p.getBus (false, i))
  1140. if (! bus->isLayoutSupported (AudioChannelSet::disabled(), &currentLayout))
  1141. return foundValid;
  1142. if (auto* bus = p.getBus (true, 0))
  1143. if (! bus->isLayoutSupported (mainInput, &currentLayout))
  1144. return foundValid;
  1145. if (auto* bus = p.getBus (false, 0))
  1146. if (! bus->isLayoutSupported (mainOutput, &currentLayout))
  1147. return foundValid;
  1148. if ((numInputBuses > 0 && currentLayout.inputBuses .getReference (0) != mainInput)
  1149. || (numOutputBuses > 0 && currentLayout.outputBuses.getReference (0) != mainOutput))
  1150. return foundValid;
  1151. if (numInputBuses > 1)
  1152. {
  1153. auto& sidechainBus = currentLayout.inputBuses.getReference (1);
  1154. if (sidechainBus != AudioChannelSet::mono() && sidechainBus != AudioChannelSet::disabled())
  1155. return foundValid;
  1156. }
  1157. for (int i = maxAuxBuses; i < numOutputBuses; ++i)
  1158. if (! currentLayout.outputBuses.getReference (i).isDisabled())
  1159. return foundValid;
  1160. fullLayout = currentLayout;
  1161. foundValid = true;
  1162. }
  1163. }
  1164. return foundValid;
  1165. }
  1166. bool isInAudioSuite()
  1167. {
  1168. AAX_CBoolean res;
  1169. Controller()->GetIsAudioSuite (&res);
  1170. return res > 0;
  1171. }
  1172. private:
  1173. friend class JuceAAX_GUI;
  1174. friend void AAX_CALLBACK AAXClasses::algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[], const void* const instancesEnd);
  1175. void process (float* const* channels, const int numChans, const int bufferSize,
  1176. const bool bypass, AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut)
  1177. {
  1178. AudioBuffer<float> buffer (channels, numChans, bufferSize);
  1179. midiBuffer.clear();
  1180. ignoreUnused (midiNodeIn, midiNodesOut);
  1181. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  1182. {
  1183. auto* midiStream = midiNodeIn->GetNodeBuffer();
  1184. auto numMidiEvents = midiStream->mBufferSize;
  1185. for (uint32_t i = 0; i < numMidiEvents; ++i)
  1186. {
  1187. auto& m = midiStream->mBuffer[i];
  1188. jassert ((int) m.mTimestamp < bufferSize);
  1189. midiBuffer.addEvent (m.mData, (int) m.mLength,
  1190. jlimit (0, (int) bufferSize - 1, (int) m.mTimestamp));
  1191. }
  1192. }
  1193. #endif
  1194. {
  1195. if (lastBufferSize != bufferSize)
  1196. {
  1197. lastBufferSize = bufferSize;
  1198. pluginInstance->setRateAndBufferSizeDetails (sampleRate, bufferSize);
  1199. if (bufferSize > maxBufferSize)
  1200. {
  1201. // we only call prepareToPlay here if the new buffer size is larger than
  1202. // the one used last time prepareToPlay was called.
  1203. // currently, this should never actually happen, because as of Pro Tools 12,
  1204. // the maximum possible value is 1024, and we call prepareToPlay with that
  1205. // value during initialisation.
  1206. pluginInstance->prepareToPlay (sampleRate, bufferSize);
  1207. maxBufferSize = bufferSize;
  1208. sideChainBuffer.calloc (static_cast<size_t> (maxBufferSize));
  1209. }
  1210. }
  1211. if (bypass && pluginInstance->getBypassParameter() == nullptr)
  1212. pluginInstance->processBlockBypassed (buffer, midiBuffer);
  1213. else
  1214. pluginInstance->processBlock (buffer, midiBuffer);
  1215. }
  1216. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
  1217. {
  1218. AAX_CMidiPacket packet;
  1219. packet.mIsImmediate = false;
  1220. for (const auto metadata : midiBuffer)
  1221. {
  1222. jassert (isPositiveAndBelow (metadata.samplePosition, bufferSize));
  1223. if (metadata.numBytes <= 4)
  1224. {
  1225. packet.mTimestamp = (uint32_t) metadata.samplePosition;
  1226. packet.mLength = (uint32_t) metadata.numBytes;
  1227. memcpy (packet.mData, metadata.data, (size_t) metadata.numBytes);
  1228. check (midiNodesOut->PostMIDIPacket (&packet));
  1229. }
  1230. }
  1231. }
  1232. #endif
  1233. }
  1234. bool isBypassPartOfRegularParemeters() const
  1235. {
  1236. auto& audioProcessor = getPluginInstance();
  1237. int n = juceParameters.getNumParameters();
  1238. if (auto* bypassParam = audioProcessor.getBypassParameter())
  1239. for (int i = 0; i < n; ++i)
  1240. if (juceParameters.getParamForIndex (i) == bypassParam)
  1241. return true;
  1242. return false;
  1243. }
  1244. // Some older Pro Tools control surfaces (EUCON [PT version 12.4] and
  1245. // Avid S6 before version 2.1) cannot cope with a large number of
  1246. // parameter steps.
  1247. static int32_t getSafeNumberOfParameterSteps (const AudioProcessorParameter& param)
  1248. {
  1249. return jmin (param.getNumSteps(), 2048);
  1250. }
  1251. void addAudioProcessorParameters()
  1252. {
  1253. auto& audioProcessor = getPluginInstance();
  1254. #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
  1255. const bool forceLegacyParamIDs = true;
  1256. #else
  1257. const bool forceLegacyParamIDs = false;
  1258. #endif
  1259. auto bypassPartOfRegularParams = isBypassPartOfRegularParemeters();
  1260. juceParameters.update (audioProcessor, forceLegacyParamIDs);
  1261. auto* bypassParameter = pluginInstance->getBypassParameter();
  1262. if (bypassParameter == nullptr)
  1263. {
  1264. ownedBypassParameter.reset (new AudioParameterBool (cDefaultMasterBypassID, "Master Bypass", false));
  1265. bypassParameter = ownedBypassParameter.get();
  1266. }
  1267. if (! bypassPartOfRegularParams)
  1268. juceParameters.addNonOwning (bypassParameter);
  1269. int parameterIndex = 0;
  1270. for (auto* juceParam : juceParameters)
  1271. {
  1272. auto isBypassParameter = (juceParam == bypassParameter);
  1273. auto category = juceParam->getCategory();
  1274. auto paramID = isBypassParameter ? String (cDefaultMasterBypassID)
  1275. : juceParameters.getParamID (audioProcessor, parameterIndex);
  1276. aaxParamIDs.add (paramID);
  1277. auto* aaxParamID = aaxParamIDs.getReference (parameterIndex++).toRawUTF8();
  1278. paramMap.set (AAXClasses::getAAXParamHash (aaxParamID), juceParam);
  1279. // is this a meter?
  1280. if (((category & 0xffff0000) >> 16) == 2)
  1281. {
  1282. aaxMeters.add (juceParam);
  1283. continue;
  1284. }
  1285. auto parameter = new AAX_CParameter<float> (aaxParamID,
  1286. AAX_CString (juceParam->getName (31).toRawUTF8()),
  1287. juceParam->getDefaultValue(),
  1288. AAX_CLinearTaperDelegate<float, 0>(),
  1289. AAX_CNumberDisplayDelegate<float, 3>(),
  1290. juceParam->isAutomatable());
  1291. parameter->AddShortenedName (juceParam->getName (4).toRawUTF8());
  1292. auto parameterNumSteps = getSafeNumberOfParameterSteps (*juceParam);
  1293. parameter->SetNumberOfSteps ((uint32_t) parameterNumSteps);
  1294. #if JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE
  1295. parameter->SetType (parameterNumSteps > 1000 ? AAX_eParameterType_Continuous
  1296. : AAX_eParameterType_Discrete);
  1297. #else
  1298. parameter->SetType (juceParam->isDiscrete() ? AAX_eParameterType_Discrete
  1299. : AAX_eParameterType_Continuous);
  1300. #endif
  1301. parameter->SetOrientation (juceParam->isOrientationInverted()
  1302. ? (AAX_eParameterOrientation_RightMinLeftMax | AAX_eParameterOrientation_TopMinBottomMax
  1303. | AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryRightMinLeftMax)
  1304. : (AAX_eParameterOrientation_LeftMinRightMax | AAX_eParameterOrientation_BottomMinTopMax
  1305. | AAX_eParameterOrientation_RotarySingleDotMode | AAX_eParameterOrientation_RotaryLeftMinRightMax));
  1306. mParameterManager.AddParameter (parameter);
  1307. if (isBypassParameter)
  1308. mPacketDispatcher.RegisterPacket (aaxParamID, JUCEAlgorithmIDs::bypass);
  1309. }
  1310. }
  1311. bool getMainBusFormats (AudioChannelSet& inputSet, AudioChannelSet& outputSet)
  1312. {
  1313. auto& audioProcessor = getPluginInstance();
  1314. #if JucePlugin_IsMidiEffect
  1315. // MIDI effect plug-ins do not support any audio channels
  1316. jassert (audioProcessor.getTotalNumInputChannels() == 0
  1317. && audioProcessor.getTotalNumOutputChannels() == 0);
  1318. inputSet = outputSet = AudioChannelSet();
  1319. return true;
  1320. #else
  1321. auto inputBuses = audioProcessor.getBusCount (true);
  1322. auto outputBuses = audioProcessor.getBusCount (false);
  1323. AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
  1324. check (Controller()->GetInputStemFormat (&inputStemFormat));
  1325. AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
  1326. check (Controller()->GetOutputStemFormat (&outputStemFormat));
  1327. #if JucePlugin_IsSynth
  1328. if (inputBuses == 0)
  1329. inputStemFormat = AAX_eStemFormat_None;
  1330. #endif
  1331. inputSet = (inputBuses > 0 ? channelSetFromStemFormat (inputStemFormat, false) : AudioChannelSet());
  1332. outputSet = (outputBuses > 0 ? channelSetFromStemFormat (outputStemFormat, false) : AudioChannelSet());
  1333. if ((inputSet == AudioChannelSet::disabled() && inputStemFormat != AAX_eStemFormat_None) || (outputSet == AudioChannelSet::disabled() && outputStemFormat != AAX_eStemFormat_None)
  1334. || (inputSet != AudioChannelSet::disabled() && inputBuses == 0) || (outputSet != AudioChannelSet::disabled() && outputBuses == 0))
  1335. return false;
  1336. return true;
  1337. #endif
  1338. }
  1339. AAX_Result preparePlugin()
  1340. {
  1341. auto& audioProcessor = getPluginInstance();
  1342. auto oldLayout = audioProcessor.getBusesLayout();
  1343. AudioChannelSet inputSet, outputSet;
  1344. if (! getMainBusFormats (inputSet, outputSet))
  1345. {
  1346. if (isPrepared)
  1347. {
  1348. isPrepared = false;
  1349. audioProcessor.releaseResources();
  1350. }
  1351. return AAX_ERROR_UNIMPLEMENTED;
  1352. }
  1353. AudioProcessor::BusesLayout newLayout;
  1354. if (! fullBusesLayoutFromMainLayout (audioProcessor, inputSet, outputSet, newLayout))
  1355. {
  1356. if (isPrepared)
  1357. {
  1358. isPrepared = false;
  1359. audioProcessor.releaseResources();
  1360. }
  1361. return AAX_ERROR_UNIMPLEMENTED;
  1362. }
  1363. hasSidechain = (newLayout.getNumChannels (true, 1) == 1);
  1364. if (hasSidechain)
  1365. {
  1366. sidechainDesired = true;
  1367. auto disabledSidechainLayout = newLayout;
  1368. disabledSidechainLayout.inputBuses.getReference (1) = AudioChannelSet::disabled();
  1369. canDisableSidechain = audioProcessor.checkBusesLayoutSupported (disabledSidechainLayout);
  1370. if (canDisableSidechain && ! lastSideChainState)
  1371. {
  1372. sidechainDesired = false;
  1373. newLayout = disabledSidechainLayout;
  1374. }
  1375. }
  1376. if (isInAudioSuite())
  1377. {
  1378. // AudioSuite doesn't support multiple output buses
  1379. for (int i = 1; i < newLayout.outputBuses.size(); ++i)
  1380. newLayout.outputBuses.getReference (i) = AudioChannelSet::disabled();
  1381. if (! audioProcessor.checkBusesLayoutSupported (newLayout))
  1382. {
  1383. // your plug-in needs to support a single output bus if running in AudioSuite
  1384. jassertfalse;
  1385. if (isPrepared)
  1386. {
  1387. isPrepared = false;
  1388. audioProcessor.releaseResources();
  1389. }
  1390. return AAX_ERROR_UNIMPLEMENTED;
  1391. }
  1392. }
  1393. const bool layoutChanged = (oldLayout != newLayout);
  1394. if (layoutChanged)
  1395. {
  1396. if (! audioProcessor.setBusesLayout (newLayout))
  1397. {
  1398. if (isPrepared)
  1399. {
  1400. isPrepared = false;
  1401. audioProcessor.releaseResources();
  1402. }
  1403. return AAX_ERROR_UNIMPLEMENTED;
  1404. }
  1405. rebuildChannelMapArrays();
  1406. }
  1407. if (layoutChanged || (! isPrepared))
  1408. {
  1409. if (isPrepared)
  1410. {
  1411. isPrepared = false;
  1412. audioProcessor.releaseResources();
  1413. }
  1414. audioProcessor.setRateAndBufferSizeDetails (sampleRate, lastBufferSize);
  1415. audioProcessor.prepareToPlay (sampleRate, lastBufferSize);
  1416. maxBufferSize = lastBufferSize;
  1417. midiBuffer.ensureSize (2048);
  1418. midiBuffer.clear();
  1419. sideChainBuffer.calloc (static_cast<size_t> (maxBufferSize));
  1420. }
  1421. check (Controller()->SetSignalLatency (audioProcessor.getLatencySamples()));
  1422. isPrepared = true;
  1423. return AAX_SUCCESS;
  1424. }
  1425. void rebuildChannelMapArrays()
  1426. {
  1427. auto& audioProcessor = getPluginInstance();
  1428. for (int dir = 0; dir < 2; ++dir)
  1429. {
  1430. bool isInput = (dir == 0);
  1431. auto& layoutMap = isInput ? inputLayoutMap : outputLayoutMap;
  1432. layoutMap.clear();
  1433. auto numBuses = audioProcessor.getBusCount (isInput);
  1434. int chOffset = 0;
  1435. for (int busIdx = 0; busIdx < numBuses; ++busIdx)
  1436. {
  1437. auto channelFormat = audioProcessor.getChannelLayoutOfBus (isInput, busIdx);
  1438. if (channelFormat != AudioChannelSet::disabled())
  1439. {
  1440. auto numChannels = channelFormat.size();
  1441. for (int ch = 0; ch < numChannels; ++ch)
  1442. layoutMap.add (juceChannelIndexToAax (ch, channelFormat) + chOffset);
  1443. chOffset += numChannels;
  1444. }
  1445. }
  1446. }
  1447. }
  1448. static void algorithmCallback (JUCEAlgorithmContext* const instancesBegin[], const void* const instancesEnd)
  1449. {
  1450. for (auto iter = instancesBegin; iter < instancesEnd; ++iter)
  1451. {
  1452. auto& i = **iter;
  1453. int sideChainBufferIdx = i.pluginInstance->parameters.hasSidechain && i.sideChainBuffers != nullptr
  1454. ? static_cast<int> (*i.sideChainBuffers) : -1;
  1455. // sidechain index of zero is an invalid index
  1456. if (sideChainBufferIdx <= 0)
  1457. sideChainBufferIdx = -1;
  1458. auto numMeters = i.pluginInstance->parameters.aaxMeters.size();
  1459. float* const meterTapBuffers = (i.meterTapBuffers != nullptr && numMeters > 0 ? *i.meterTapBuffers : nullptr);
  1460. i.pluginInstance->parameters.process (i.inputChannels, i.outputChannels, sideChainBufferIdx,
  1461. *(i.bufferSize), *(i.bypass) != 0,
  1462. getMidiNodeIn(i), getMidiNodeOut(i),
  1463. meterTapBuffers);
  1464. }
  1465. }
  1466. //==============================================================================
  1467. void updateSidechainState()
  1468. {
  1469. if (! processingSidechainChange)
  1470. return;
  1471. auto& audioProcessor = getPluginInstance();
  1472. bool sidechainActual = audioProcessor.getChannelCountOfBus (true, 1) > 0;
  1473. if (hasSidechain && canDisableSidechain && sidechainDesired != sidechainActual)
  1474. {
  1475. lastSideChainState = sidechainDesired;
  1476. if (isPrepared)
  1477. {
  1478. isPrepared = false;
  1479. audioProcessor.releaseResources();
  1480. }
  1481. if (auto* bus = audioProcessor.getBus (true, 1))
  1482. bus->setCurrentLayout (lastSideChainState ? AudioChannelSet::mono()
  1483. : AudioChannelSet::disabled());
  1484. audioProcessor.prepareToPlay (audioProcessor.getSampleRate(), audioProcessor.getBlockSize());
  1485. isPrepared = true;
  1486. }
  1487. processingSidechainChange = false;
  1488. }
  1489. void handleAsyncUpdate() override
  1490. {
  1491. updateSidechainState();
  1492. }
  1493. //==============================================================================
  1494. static AudioProcessor::CurveData::Type aaxCurveTypeToJUCE (AAX_CTypeID type) noexcept
  1495. {
  1496. switch (type)
  1497. {
  1498. case AAX_eCurveType_EQ: return AudioProcessor::CurveData::Type::EQ;
  1499. case AAX_eCurveType_Dynamics: return AudioProcessor::CurveData::Type::Dynamics;
  1500. case AAX_eCurveType_Reduction: return AudioProcessor::CurveData::Type::GainReduction;
  1501. default: break;
  1502. }
  1503. return AudioProcessor::CurveData::Type::Unknown;
  1504. }
  1505. uint32_t getAAXMeterIdForParamId (const String& paramID) const noexcept
  1506. {
  1507. int idx;
  1508. for (idx = 0; idx < aaxMeters.size(); ++idx)
  1509. if (LegacyAudioParameter::getParamID (aaxMeters[idx], false) == paramID)
  1510. break;
  1511. // you specified a parameter id in your curve but the parameter does not have the meter
  1512. // category
  1513. jassert (idx < aaxMeters.size());
  1514. return 'Metr' + static_cast<AAX_CTypeID> (idx);
  1515. }
  1516. //==============================================================================
  1517. AAX_Result GetCurveData (AAX_CTypeID iCurveType, const float * iValues, uint32_t iNumValues, float * oValues ) const override
  1518. {
  1519. auto curveType = aaxCurveTypeToJUCE (iCurveType);
  1520. if (curveType != AudioProcessor::CurveData::Type::Unknown)
  1521. {
  1522. auto& audioProcessor = getPluginInstance();
  1523. auto curve = audioProcessor.getResponseCurve (curveType);
  1524. if (curve.curve)
  1525. {
  1526. if (oValues != nullptr && iValues != nullptr)
  1527. {
  1528. for (uint32_t i = 0; i < iNumValues; ++i)
  1529. oValues[i] = curve.curve (iValues[i]);
  1530. }
  1531. return AAX_SUCCESS;
  1532. }
  1533. }
  1534. return AAX_ERROR_UNIMPLEMENTED;
  1535. }
  1536. AAX_Result GetCurveDataMeterIds (AAX_CTypeID iCurveType, uint32_t *oXMeterId, uint32_t *oYMeterId) const override
  1537. {
  1538. auto curveType = aaxCurveTypeToJUCE (iCurveType);
  1539. if (curveType != AudioProcessor::CurveData::Type::Unknown)
  1540. {
  1541. auto& audioProcessor = getPluginInstance();
  1542. auto curve = audioProcessor.getResponseCurve (curveType);
  1543. if (curve.curve && curve.xMeterID.isNotEmpty() && curve.yMeterID.isNotEmpty())
  1544. {
  1545. if (oXMeterId != nullptr) *oXMeterId = getAAXMeterIdForParamId (curve.xMeterID);
  1546. if (oYMeterId != nullptr) *oYMeterId = getAAXMeterIdForParamId (curve.yMeterID);
  1547. return AAX_SUCCESS;
  1548. }
  1549. }
  1550. return AAX_ERROR_UNIMPLEMENTED;
  1551. }
  1552. AAX_Result GetCurveDataDisplayRange (AAX_CTypeID iCurveType, float *oXMin, float *oXMax, float *oYMin, float *oYMax) const override
  1553. {
  1554. auto curveType = aaxCurveTypeToJUCE (iCurveType);
  1555. if (curveType != AudioProcessor::CurveData::Type::Unknown)
  1556. {
  1557. auto& audioProcessor = getPluginInstance();
  1558. auto curve = audioProcessor.getResponseCurve (curveType);
  1559. if (curve.curve)
  1560. {
  1561. if (oXMin != nullptr) *oXMin = curve.xRange.getStart();
  1562. if (oXMax != nullptr) *oXMax = curve.xRange.getEnd();
  1563. if (oYMin != nullptr) *oYMin = curve.yRange.getStart();
  1564. if (oYMax != nullptr) *oYMax = curve.yRange.getEnd();
  1565. return AAX_SUCCESS;
  1566. }
  1567. }
  1568. return AAX_ERROR_UNIMPLEMENTED;
  1569. }
  1570. //==============================================================================
  1571. inline int getParamIndexFromID (AAX_CParamID paramID) const noexcept
  1572. {
  1573. if (auto* param = getParameterFromID (paramID))
  1574. return LegacyAudioParameter::getParamIndex (getPluginInstance(), param);
  1575. return -1;
  1576. }
  1577. inline AAX_CParamID getAAXParamIDFromJuceIndex (int index) const noexcept
  1578. {
  1579. if (isPositiveAndBelow (index, aaxParamIDs.size()))
  1580. return aaxParamIDs.getReference (index).toRawUTF8();
  1581. return nullptr;
  1582. }
  1583. AudioProcessorParameter* getParameterFromID (AAX_CParamID paramID) const noexcept
  1584. {
  1585. return paramMap [AAXClasses::getAAXParamHash (paramID)];
  1586. }
  1587. //==============================================================================
  1588. static AudioProcessor::BusesLayout getDefaultLayout (const AudioProcessor& p, bool enableAll)
  1589. {
  1590. AudioProcessor::BusesLayout defaultLayout;
  1591. for (int dir = 0; dir < 2; ++dir)
  1592. {
  1593. bool isInput = (dir == 0);
  1594. auto numBuses = p.getBusCount (isInput);
  1595. auto& layouts = (isInput ? defaultLayout.inputBuses : defaultLayout.outputBuses);
  1596. for (int i = 0; i < numBuses; ++i)
  1597. if (auto* bus = p.getBus (isInput, i))
  1598. layouts.add (enableAll || bus->isEnabledByDefault() ? bus->getDefaultLayout() : AudioChannelSet());
  1599. }
  1600. return defaultLayout;
  1601. }
  1602. static AudioProcessor::BusesLayout getDefaultLayout (AudioProcessor& p)
  1603. {
  1604. auto defaultLayout = getDefaultLayout (p, true);
  1605. if (! p.checkBusesLayoutSupported (defaultLayout))
  1606. defaultLayout = getDefaultLayout (p, false);
  1607. // Your processor must support the default layout
  1608. jassert (p.checkBusesLayoutSupported (defaultLayout));
  1609. return defaultLayout;
  1610. }
  1611. //==============================================================================
  1612. ScopedJuceInitialiser_GUI libraryInitialiser;
  1613. std::unique_ptr<AudioProcessor> pluginInstance;
  1614. bool isPrepared = false;
  1615. MidiBuffer midiBuffer;
  1616. Array<float*> channelList;
  1617. int32_t juceChunkIndex = 0, numSetDirtyCalls = 0;
  1618. AAX_CSampleRate sampleRate = 0;
  1619. int lastBufferSize = 1024, maxBufferSize = 1024;
  1620. bool hasSidechain = false, canDisableSidechain = false, lastSideChainState = false;
  1621. std::atomic<bool> processingSidechainChange, sidechainDesired;
  1622. HeapBlock<float> sideChainBuffer;
  1623. Array<int> inputLayoutMap, outputLayoutMap;
  1624. Array<String> aaxParamIDs;
  1625. HashMap<int32, AudioProcessorParameter*> paramMap;
  1626. LegacyAudioParametersWrapper juceParameters;
  1627. std::unique_ptr<AudioProcessorParameter> ownedBypassParameter;
  1628. Array<AudioProcessorParameter*> aaxMeters;
  1629. struct ChunkMemoryBlock
  1630. {
  1631. juce::MemoryBlock data;
  1632. bool isValid;
  1633. };
  1634. // temporary filter data is generated in GetChunkSize
  1635. // and the size of the data returned. To avoid generating
  1636. // it again in GetChunk, we need to store it somewhere.
  1637. // However, as GetChunkSize and GetChunk can be called
  1638. // on different threads, we store it in thread dependent storage
  1639. // in a hash map with the thread id as a key.
  1640. mutable ThreadLocalValue<ChunkMemoryBlock> perThreadFilterData;
  1641. CriticalSection perThreadDataLock;
  1642. ThreadLocalValue<bool> inParameterChangedCallback;
  1643. JUCE_DECLARE_NON_COPYABLE (JuceAAX_Processor)
  1644. };
  1645. //==============================================================================
  1646. void JuceAAX_GUI::CreateViewContents()
  1647. {
  1648. if (component == nullptr)
  1649. {
  1650. if (auto* params = dynamic_cast<JuceAAX_Processor*> (GetEffectParameters()))
  1651. component.reset (new ContentWrapperComponent (*this, params->getPluginInstance()));
  1652. else
  1653. jassertfalse;
  1654. }
  1655. }
  1656. int JuceAAX_GUI::getParamIndexFromID (AAX_CParamID paramID) const noexcept
  1657. {
  1658. if (auto* params = dynamic_cast<const JuceAAX_Processor*> (GetEffectParameters()))
  1659. return params->getParamIndexFromID (paramID);
  1660. return -1;
  1661. }
  1662. AAX_CParamID JuceAAX_GUI::getAAXParamIDFromJuceIndex (int index) const noexcept
  1663. {
  1664. if (auto* params = dynamic_cast<const JuceAAX_Processor*> (GetEffectParameters()))
  1665. return params->getAAXParamIDFromJuceIndex (index);
  1666. return nullptr;
  1667. }
  1668. //==============================================================================
  1669. struct AAXFormatConfiguration
  1670. {
  1671. AAXFormatConfiguration() noexcept {}
  1672. AAXFormatConfiguration (AAX_EStemFormat inFormat, AAX_EStemFormat outFormat) noexcept
  1673. : inputFormat (inFormat), outputFormat (outFormat) {}
  1674. AAX_EStemFormat inputFormat = AAX_eStemFormat_None,
  1675. outputFormat = AAX_eStemFormat_None;
  1676. bool operator== (const AAXFormatConfiguration other) const noexcept
  1677. {
  1678. return inputFormat == other.inputFormat && outputFormat == other.outputFormat;
  1679. }
  1680. bool operator< (const AAXFormatConfiguration other) const noexcept
  1681. {
  1682. return inputFormat == other.inputFormat ? (outputFormat < other.outputFormat)
  1683. : (inputFormat < other.inputFormat);
  1684. }
  1685. };
  1686. //==============================================================================
  1687. static int addAAXMeters (AudioProcessor& p, AAX_IEffectDescriptor& descriptor)
  1688. {
  1689. LegacyAudioParametersWrapper params;
  1690. #if JUCE_FORCE_USE_LEGACY_PARAM_IDS
  1691. const bool forceLegacyParamIDs = true;
  1692. #else
  1693. const bool forceLegacyParamIDs = false;
  1694. #endif
  1695. params.update (p, forceLegacyParamIDs);
  1696. int meterIdx = 0;
  1697. for (auto* param : params)
  1698. {
  1699. auto category = param->getCategory();
  1700. // is this a meter?
  1701. if (((category & 0xffff0000) >> 16) == 2)
  1702. {
  1703. if (auto* meterProperties = descriptor.NewPropertyMap())
  1704. {
  1705. meterProperties->AddProperty (AAX_eProperty_Meter_Type, getMeterTypeForCategory (category));
  1706. meterProperties->AddProperty (AAX_eProperty_Meter_Orientation, AAX_eMeterOrientation_TopRight);
  1707. descriptor.AddMeterDescription ('Metr' + static_cast<AAX_CTypeID> (meterIdx++),
  1708. param->getName (1024).toRawUTF8(), meterProperties);
  1709. }
  1710. }
  1711. }
  1712. return meterIdx;
  1713. }
  1714. static void createDescriptor (AAX_IComponentDescriptor& desc,
  1715. const AudioProcessor::BusesLayout& fullLayout,
  1716. AudioProcessor& processor,
  1717. Array<int32>& pluginIds,
  1718. const int numMeters)
  1719. {
  1720. auto aaxInputFormat = getFormatForAudioChannelSet (fullLayout.getMainInputChannelSet(), false);
  1721. auto aaxOutputFormat = getFormatForAudioChannelSet (fullLayout.getMainOutputChannelSet(), false);
  1722. #if JucePlugin_IsSynth
  1723. if (aaxInputFormat == AAX_eStemFormat_None)
  1724. aaxInputFormat = aaxOutputFormat;
  1725. #endif
  1726. #if JucePlugin_IsMidiEffect
  1727. aaxInputFormat = aaxOutputFormat = AAX_eStemFormat_Mono;
  1728. #endif
  1729. check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
  1730. check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
  1731. check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
  1732. check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
  1733. #if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
  1734. check (desc.AddMIDINode (JUCEAlgorithmIDs::midiNodeIn, AAX_eMIDINodeType_LocalInput,
  1735. JucePlugin_Name, 0xffff));
  1736. #endif
  1737. #if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
  1738. check (desc.AddMIDINode (JUCEAlgorithmIDs::midiNodeOut, AAX_eMIDINodeType_LocalOutput,
  1739. JucePlugin_Name " Out", 0xffff));
  1740. #endif
  1741. check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
  1742. check (desc.AddPrivateData (JUCEAlgorithmIDs::preparedFlag, sizeof (int32_t)));
  1743. if (numMeters > 0)
  1744. {
  1745. HeapBlock<AAX_CTypeID> meterIDs (static_cast<size_t> (numMeters));
  1746. for (int i = 0; i < numMeters; ++i)
  1747. meterIDs[i] = 'Metr' + static_cast<AAX_CTypeID> (i);
  1748. check (desc.AddMeters (JUCEAlgorithmIDs::meterTapBuffers, meterIDs.getData(), static_cast<uint32_t> (numMeters)));
  1749. }
  1750. else
  1751. {
  1752. // AAX does not allow there to be any gaps in the fields of the algorithm context structure
  1753. // so just add a dummy one here if there aren't any meters
  1754. check (desc.AddPrivateData (JUCEAlgorithmIDs::meterTapBuffers, sizeof (uintptr_t)));
  1755. }
  1756. // Create a property map
  1757. AAX_IPropertyMap* const properties = desc.NewPropertyMap();
  1758. jassert (properties != nullptr);
  1759. properties->AddProperty (AAX_eProperty_ManufacturerID, JucePlugin_AAXManufacturerCode);
  1760. properties->AddProperty (AAX_eProperty_ProductID, JucePlugin_AAXProductId);
  1761. #if JucePlugin_AAXDisableBypass
  1762. properties->AddProperty (AAX_eProperty_CanBypass, false);
  1763. #else
  1764. properties->AddProperty (AAX_eProperty_CanBypass, true);
  1765. #endif
  1766. properties->AddProperty (AAX_eProperty_InputStemFormat, static_cast<AAX_CPropertyValue> (aaxInputFormat));
  1767. properties->AddProperty (AAX_eProperty_OutputStemFormat, static_cast<AAX_CPropertyValue> (aaxOutputFormat));
  1768. // This value needs to match the RTAS wrapper's Type ID, so that
  1769. // the host knows that the RTAS/AAX plugins are equivalent.
  1770. const int32 pluginID = processor.getAAXPluginIDForMainBusConfig (fullLayout.getMainInputChannelSet(),
  1771. fullLayout.getMainOutputChannelSet(),
  1772. false);
  1773. // The plugin id generated from your AudioProcessor's getAAXPluginIDForMainBusConfig callback
  1774. // it not unique. Please fix your implementation!
  1775. jassert (! pluginIds.contains (pluginID));
  1776. pluginIds.add (pluginID);
  1777. properties->AddProperty (AAX_eProperty_PlugInID_Native, pluginID);
  1778. #if ! JucePlugin_AAXDisableAudioSuite
  1779. properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite,
  1780. processor.getAAXPluginIDForMainBusConfig (fullLayout.getMainInputChannelSet(),
  1781. fullLayout.getMainOutputChannelSet(),
  1782. true));
  1783. #endif
  1784. #if JucePlugin_AAXDisableMultiMono
  1785. properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, false);
  1786. #else
  1787. properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, true);
  1788. #endif
  1789. #if JucePlugin_AAXDisableDynamicProcessing
  1790. properties->AddProperty (AAX_eProperty_Constraint_AlwaysProcess, true);
  1791. #endif
  1792. #if JucePlugin_AAXDisableDefaultSettingsChunks
  1793. properties->AddProperty (AAX_eProperty_Constraint_DoNotApplyDefaultSettings, true);
  1794. #endif
  1795. #if JucePlugin_AAXDisableSaveRestore
  1796. properties->AddProperty (AAX_eProperty_SupportsSaveRestore, false);
  1797. #endif
  1798. if (fullLayout.getChannelSet (true, 1) == AudioChannelSet::mono())
  1799. {
  1800. check (desc.AddSideChainIn (JUCEAlgorithmIDs::sideChainBuffers));
  1801. properties->AddProperty (AAX_eProperty_SupportsSideChainInput, true);
  1802. }
  1803. else
  1804. {
  1805. // AAX does not allow there to be any gaps in the fields of the algorithm context structure
  1806. // so just add a dummy one here if there aren't any side chains
  1807. check (desc.AddPrivateData (JUCEAlgorithmIDs::sideChainBuffers, sizeof (uintptr_t)));
  1808. }
  1809. auto maxAuxBuses = jmax (0, jmin (15, fullLayout.outputBuses.size() - 1));
  1810. // add the output buses
  1811. // This is incredibly dumb: the output bus format must be well defined
  1812. // for every main bus in/out format pair. This means that there cannot
  1813. // be two configurations with different aux formats but
  1814. // identical main bus in/out formats.
  1815. for (int busIdx = 1; busIdx < maxAuxBuses + 1; ++busIdx)
  1816. {
  1817. auto set = fullLayout.getChannelSet (false, busIdx);
  1818. if (set.isDisabled())
  1819. break;
  1820. auto auxFormat = getFormatForAudioChannelSet (set, true);
  1821. if (auxFormat != AAX_eStemFormat_INT32_MAX && auxFormat != AAX_eStemFormat_None)
  1822. {
  1823. auto& name = processor.getBus (false, busIdx)->getName();
  1824. check (desc.AddAuxOutputStem (0, static_cast<int32_t> (auxFormat), name.toRawUTF8()));
  1825. }
  1826. }
  1827. check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
  1828. }
  1829. static bool hostSupportsStemFormat (AAX_EStemFormat stemFormat, const AAX_IFeatureInfo* featureInfo)
  1830. {
  1831. if (featureInfo != nullptr)
  1832. {
  1833. AAX_ESupportLevel supportLevel;
  1834. if (featureInfo->SupportLevel (supportLevel) == AAX_SUCCESS && supportLevel == AAX_eSupportLevel_ByProperty)
  1835. {
  1836. std::unique_ptr<const AAX_IPropertyMap> props (featureInfo->AcquireProperties());
  1837. // Due to a bug in ProTools 12.8, ProTools thinks that AAX_eStemFormat_Ambi_1_ACN is not supported
  1838. // To workaround this bug, check if ProTools supports AAX_eStemFormat_Ambi_2_ACN, and, if yes,
  1839. // we can safely assume that it will also support AAX_eStemFormat_Ambi_1_ACN
  1840. if (stemFormat == AAX_eStemFormat_Ambi_1_ACN)
  1841. stemFormat = AAX_eStemFormat_Ambi_2_ACN;
  1842. if (props != nullptr && props->GetProperty ((AAX_EProperty) stemFormat, (AAX_CPropertyValue*) &supportLevel) != 0)
  1843. return (supportLevel == AAX_eSupportLevel_Supported);
  1844. }
  1845. }
  1846. return (AAX_STEM_FORMAT_INDEX (stemFormat) <= 12);
  1847. }
  1848. static void getPlugInDescription (AAX_IEffectDescriptor& descriptor, const AAX_IFeatureInfo* featureInfo)
  1849. {
  1850. PluginHostType::jucePlugInClientCurrentWrapperType = AudioProcessor::wrapperType_AAX;
  1851. std::unique_ptr<AudioProcessor> plugin (createPluginFilterOfType (AudioProcessor::wrapperType_AAX));
  1852. auto numInputBuses = plugin->getBusCount (true);
  1853. auto numOutputBuses = plugin->getBusCount (false);
  1854. auto pluginNames = plugin->getAlternateDisplayNames();
  1855. pluginNames.insert (0, JucePlugin_Name);
  1856. pluginNames.removeDuplicates (false);
  1857. for (auto name : pluginNames)
  1858. descriptor.AddName (name.toRawUTF8());
  1859. descriptor.AddCategory (JucePlugin_AAXCategory);
  1860. const int numMeters = addAAXMeters (*plugin, descriptor);
  1861. #ifdef JucePlugin_AAXPageTableFile
  1862. // optional page table setting - define this macro in your project if you want
  1863. // to set this value - see Avid documentation for details about its format.
  1864. descriptor.AddResourceInfo (AAX_eResourceType_PageTable, JucePlugin_AAXPageTableFile);
  1865. #endif
  1866. check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
  1867. check (descriptor.AddProcPtr ((void*) JuceAAX_Processor::Create, kAAX_ProcPtrID_Create_EffectParameters));
  1868. Array<int32> pluginIds;
  1869. #if JucePlugin_IsMidiEffect
  1870. // MIDI effect plug-ins do not support any audio channels
  1871. jassert (numInputBuses == 0 && numOutputBuses == 0);
  1872. ignoreUnused (featureInfo);
  1873. if (auto* desc = descriptor.NewComponentDescriptor())
  1874. {
  1875. createDescriptor (*desc, plugin->getBusesLayout(), *plugin, pluginIds, numMeters);
  1876. check (descriptor.AddComponent (desc));
  1877. }
  1878. #else
  1879. const int numIns = numInputBuses > 0 ? numElementsInArray (aaxFormats) : 0;
  1880. const int numOuts = numOutputBuses > 0 ? numElementsInArray (aaxFormats) : 0;
  1881. for (int inIdx = 0; inIdx < jmax (numIns, 1); ++inIdx)
  1882. {
  1883. auto aaxInFormat = numIns > 0 ? aaxFormats[inIdx] : AAX_eStemFormat_None;
  1884. auto inLayout = channelSetFromStemFormat (aaxInFormat, false);
  1885. for (int outIdx = 0; outIdx < jmax (numOuts, 1); ++outIdx)
  1886. {
  1887. auto aaxOutFormat = numOuts > 0 ? aaxFormats[outIdx] : AAX_eStemFormat_None;
  1888. auto outLayout = channelSetFromStemFormat (aaxOutFormat, false);
  1889. if (hostSupportsStemFormat (aaxInFormat, featureInfo)
  1890. && hostSupportsStemFormat (aaxOutFormat, featureInfo))
  1891. {
  1892. AudioProcessor::BusesLayout fullLayout;
  1893. if (! JuceAAX_Processor::fullBusesLayoutFromMainLayout (*plugin, inLayout, outLayout, fullLayout))
  1894. continue;
  1895. if (auto* desc = descriptor.NewComponentDescriptor())
  1896. {
  1897. createDescriptor (*desc, fullLayout, *plugin, pluginIds, numMeters);
  1898. check (descriptor.AddComponent (desc));
  1899. }
  1900. }
  1901. }
  1902. }
  1903. // You don't have any supported layouts
  1904. jassert (pluginIds.size() > 0);
  1905. #endif
  1906. }
  1907. } // namespace AAXClasses
  1908. void AAX_CALLBACK AAXClasses::algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[], const void* const instancesEnd)
  1909. {
  1910. AAXClasses::JuceAAX_Processor::algorithmCallback (instancesBegin, instancesEnd);
  1911. }
  1912. //==============================================================================
  1913. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection*);
  1914. AAX_Result JUCE_CDECL GetEffectDescriptions (AAX_ICollection* collection)
  1915. {
  1916. ScopedJuceInitialiser_GUI libraryInitialiser;
  1917. std::unique_ptr<const AAX_IFeatureInfo> stemFormatFeatureInfo;
  1918. if (const auto* hostDescription = collection->DescriptionHost())
  1919. stemFormatFeatureInfo.reset (hostDescription->AcquireFeatureProperties (AAXATTR_ClientFeature_StemFormat));
  1920. if (auto* descriptor = collection->NewDescriptor())
  1921. {
  1922. AAXClasses::getPlugInDescription (*descriptor, stemFormatFeatureInfo.get());
  1923. collection->AddEffect (JUCE_STRINGIFY (JucePlugin_AAXIdentifier), descriptor);
  1924. collection->SetManufacturerName (JucePlugin_Manufacturer);
  1925. collection->AddPackageName (JucePlugin_Desc);
  1926. collection->AddPackageName (JucePlugin_Name);
  1927. collection->SetPackageVersion (JucePlugin_VersionCode);
  1928. return AAX_SUCCESS;
  1929. }
  1930. return AAX_ERROR_NULL_OBJECT;
  1931. }
  1932. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  1933. //==============================================================================
  1934. #if _MSC_VER || JUCE_MINGW
  1935. extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH) Process::setCurrentModuleInstanceHandle (instance); return true; }
  1936. #endif
  1937. #endif