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.

300 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. class AAXPluginId
  21. {
  22. public:
  23. static std::optional<AAXPluginId> create (std::array<char, 4> letters)
  24. {
  25. std::array<size_t, 4> indices{};
  26. for (size_t i = 0; i < letters.size(); ++i)
  27. {
  28. if (const auto index = findIndexOfChar (letters[i]))
  29. indices[i] = *index;
  30. else
  31. return std::nullopt;
  32. }
  33. return AAXPluginId { std::move (indices) };
  34. }
  35. std::optional<AAXPluginId> withIncrementedLetter (size_t index, size_t increment) const
  36. {
  37. if (indices.size() <= index)
  38. return std::nullopt;
  39. auto copy = *this;
  40. copy.indices[index] += increment;
  41. if ((size_t) std::size (validChars) <= copy.indices[index])
  42. return std::nullopt;
  43. return copy;
  44. }
  45. int32 getPluginId() const
  46. {
  47. int32 result = 0;
  48. for (size_t i = 0; i < indices.size(); ++i)
  49. result |= static_cast<int32> (validChars[indices[i]]) << (8 * (indices.size() - 1 - i));
  50. return result;
  51. }
  52. static std::optional<size_t> findIndexOfChar (char c)
  53. {
  54. const auto ptr = std::find (std::begin (validChars), std::end (validChars), c);
  55. return ptr != std::end (validChars) ? std::make_optional (std::distance (std::begin (validChars), ptr))
  56. : std::nullopt;
  57. }
  58. private:
  59. static inline constexpr char validChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  60. explicit AAXPluginId (std::array<size_t, 4> indicesIn)
  61. : indices (std::move (indicesIn))
  62. {}
  63. std::array<size_t, 4> indices;
  64. };
  65. static const AudioChannelSet channelSets[] { AudioChannelSet::disabled(),
  66. AudioChannelSet::mono(),
  67. AudioChannelSet::stereo(),
  68. AudioChannelSet::createLCR(),
  69. AudioChannelSet::createLCRS(),
  70. AudioChannelSet::quadraphonic(),
  71. AudioChannelSet::create5point0(),
  72. AudioChannelSet::create5point1(),
  73. AudioChannelSet::create6point0(),
  74. AudioChannelSet::create6point1(),
  75. AudioChannelSet::create7point0(),
  76. AudioChannelSet::create7point1(),
  77. AudioChannelSet::create7point0SDDS(),
  78. AudioChannelSet::create7point1SDDS(),
  79. AudioChannelSet::create7point0point2(),
  80. AudioChannelSet::create7point1point2(),
  81. AudioChannelSet::ambisonic (1),
  82. AudioChannelSet::ambisonic (2),
  83. AudioChannelSet::ambisonic (3),
  84. AudioChannelSet::create5point0point2(),
  85. AudioChannelSet::create5point1point2(),
  86. AudioChannelSet::create5point0point4(),
  87. AudioChannelSet::create5point1point4(),
  88. AudioChannelSet::create7point0point4(),
  89. AudioChannelSet::create7point1point4(),
  90. AudioChannelSet::create7point0point6(),
  91. AudioChannelSet::create7point1point6(),
  92. AudioChannelSet::create9point0point4(),
  93. AudioChannelSet::create9point1point4(),
  94. AudioChannelSet::create9point0point6(),
  95. AudioChannelSet::create9point1point6(),
  96. AudioChannelSet::ambisonic (4),
  97. AudioChannelSet::ambisonic (5),
  98. AudioChannelSet::ambisonic (6),
  99. AudioChannelSet::ambisonic (7) };
  100. int32 AAXClientExtensions::getPluginIDForMainBusConfig (const AudioChannelSet& mainInputLayout,
  101. const AudioChannelSet& mainOutputLayout,
  102. bool idForAudioSuite) const
  103. {
  104. auto pluginId = [&]
  105. {
  106. auto opt = idForAudioSuite ? AAXPluginId::create ({ 'j', 'y', 'a', 'a' })
  107. : AAXPluginId::create ({ 'j', 'c', 'a', 'a' });
  108. jassert (opt.has_value());
  109. return *opt;
  110. }();
  111. for (const auto& [channelSet, indexToModify] : { std::tuple (&mainInputLayout, (size_t) 2),
  112. std::tuple (&mainOutputLayout, (size_t) 3) })
  113. {
  114. const auto increment = (size_t) std::distance (std::begin (channelSets),
  115. std::find (std::begin (channelSets),
  116. std::end (channelSets),
  117. *channelSet));
  118. if (auto modifiedPluginIdOpt = pluginId.withIncrementedLetter (indexToModify, increment);
  119. increment < (size_t) std::size (channelSets) && modifiedPluginIdOpt.has_value())
  120. {
  121. pluginId = *modifiedPluginIdOpt;
  122. }
  123. else
  124. {
  125. jassertfalse;
  126. }
  127. }
  128. return pluginId.getPluginId();
  129. }
  130. String AAXClientExtensions::getPageFileName() const
  131. {
  132. #ifdef JucePlugin_AAXPageTableFile
  133. #warning "JucePlugin_AAXPageTableFile is deprecated, instead implement AAXClientExtensions::getPageFileName()"
  134. return JucePlugin_AAXPageTableFile;
  135. #else
  136. return {};
  137. #endif
  138. }
  139. //==============================================================================
  140. //==============================================================================
  141. #if JUCE_UNIT_TESTS
  142. static std::array<char, 4> toCharArray (int32 identifier)
  143. {
  144. std::array<char, 4> result;
  145. for (size_t i = 0; i < result.size(); ++i)
  146. result[i] = static_cast<char> ((identifier >> (i * 8)) & 0xff);
  147. return result;
  148. }
  149. static bool isValidAAXPluginId (int32 pluginId)
  150. {
  151. const auto chars = toCharArray (pluginId);
  152. return std::all_of (std::begin (chars),
  153. std::end (chars),
  154. [] (const auto& c)
  155. {
  156. return AAXPluginId::findIndexOfChar (c).has_value();
  157. });
  158. }
  159. static int32 getPluginIDForMainBusConfigJuce705 (const AudioChannelSet& mainInputLayout,
  160. const AudioChannelSet& mainOutputLayout,
  161. bool idForAudioSuite)
  162. {
  163. int uniqueFormatId = 0;
  164. for (int dir = 0; dir < 2; ++dir)
  165. {
  166. const bool isInput = (dir == 0);
  167. auto& set = (isInput ? mainInputLayout : mainOutputLayout);
  168. int aaxFormatIndex = 0;
  169. const AudioChannelSet sets[]
  170. {
  171. AudioChannelSet::disabled(),
  172. AudioChannelSet::mono(),
  173. AudioChannelSet::stereo(),
  174. AudioChannelSet::createLCR(),
  175. AudioChannelSet::createLCRS(),
  176. AudioChannelSet::quadraphonic(),
  177. AudioChannelSet::create5point0(),
  178. AudioChannelSet::create5point1(),
  179. AudioChannelSet::create6point0(),
  180. AudioChannelSet::create6point1(),
  181. AudioChannelSet::create7point0(),
  182. AudioChannelSet::create7point1(),
  183. AudioChannelSet::create7point0SDDS(),
  184. AudioChannelSet::create7point1SDDS(),
  185. AudioChannelSet::create7point0point2(),
  186. AudioChannelSet::create7point1point2(),
  187. AudioChannelSet::ambisonic (1),
  188. AudioChannelSet::ambisonic (2),
  189. AudioChannelSet::ambisonic (3),
  190. AudioChannelSet::create5point0point2(),
  191. AudioChannelSet::create5point1point2(),
  192. AudioChannelSet::create5point0point4(),
  193. AudioChannelSet::create5point1point4(),
  194. AudioChannelSet::create7point0point4(),
  195. AudioChannelSet::create7point1point4(),
  196. AudioChannelSet::create7point0point6(),
  197. AudioChannelSet::create7point1point6(),
  198. AudioChannelSet::create9point0point4(),
  199. AudioChannelSet::create9point1point4(),
  200. AudioChannelSet::create9point0point6(),
  201. AudioChannelSet::create9point1point6(),
  202. AudioChannelSet::ambisonic (4),
  203. AudioChannelSet::ambisonic (5),
  204. AudioChannelSet::ambisonic (6),
  205. AudioChannelSet::ambisonic (7)
  206. };
  207. const auto index = (int) std::distance (std::begin (sets), std::find (std::begin (sets), std::end (sets), set));
  208. if (index != (int) std::size (sets))
  209. aaxFormatIndex = index;
  210. else
  211. jassertfalse;
  212. uniqueFormatId = (uniqueFormatId << 8) | aaxFormatIndex;
  213. }
  214. return (idForAudioSuite ? 0x6a796161 /* 'jyaa' */ : 0x6a636161 /* 'jcaa' */) + uniqueFormatId;
  215. }
  216. class AAXClientExtensionsTests : public UnitTest
  217. {
  218. public:
  219. AAXClientExtensionsTests()
  220. : UnitTest ("AAXClientExtensionsTests", UnitTestCategories::audioProcessors)
  221. {}
  222. void runTest() override
  223. {
  224. AAXClientExtensions extensions;
  225. beginTest ("Previously valid PluginIds should be unchanged");
  226. {
  227. for (const auto& input : channelSets)
  228. for (const auto& output : channelSets)
  229. for (const auto idForAudioSuite : { false, true })
  230. if (const auto oldId = getPluginIDForMainBusConfigJuce705 (input, output, idForAudioSuite); isValidAAXPluginId (oldId))
  231. expect (extensions.getPluginIDForMainBusConfig (input, output, idForAudioSuite) == oldId);
  232. }
  233. beginTest ("Valid, unique PluginIds should be generated for all configurations");
  234. {
  235. std::set<int32> pluginIds;
  236. for (const auto& input : channelSets)
  237. for (const auto& output : channelSets)
  238. for (const auto idForAudioSuite : { false, true })
  239. pluginIds.insert (extensions.getPluginIDForMainBusConfig (input, output, idForAudioSuite));
  240. for (auto identifier : pluginIds)
  241. expect (isValidAAXPluginId (identifier));
  242. expect (pluginIds.size() == square (std::size (channelSets)) * 2);
  243. }
  244. }
  245. };
  246. static AAXClientExtensionsTests aaxClientExtensionsTests;
  247. #endif
  248. } // namespace juce