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.

498 lines
20KB

  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::dsp
  19. {
  20. #if JUCE_USE_SIMD
  21. template <typename SampleType>
  22. String& operator<< (String& str, SIMDRegister<SampleType>) { return str; }
  23. #endif
  24. template <typename SampleType>
  25. class AudioBlockUnitTests : public UnitTest
  26. {
  27. public:
  28. //==============================================================================
  29. using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type;
  30. AudioBlockUnitTests()
  31. : UnitTest ("AudioBlock", UnitTestCategories::dsp)
  32. {
  33. for (auto v : { &data, &otherData })
  34. for (auto& channel : *v)
  35. channel = allocateAlignedMemory (numSamples);
  36. block = { data.data(), data.size(), (size_t) numSamples };
  37. otherBlock = { otherData.data(), otherData.size(), (size_t) numSamples };
  38. resetBlocks();
  39. }
  40. ~AudioBlockUnitTests() override
  41. {
  42. for (auto v : { &data, &otherData })
  43. for (auto channel : *v)
  44. deallocateAlignedMemory (channel);
  45. }
  46. void runTest() override
  47. {
  48. beginTest ("Equality");
  49. {
  50. expect (block == block);
  51. expect (block != otherBlock);
  52. }
  53. beginTest ("Constructors");
  54. {
  55. expect (block == AudioBlock<SampleType> (data.data(), data.size(), numSamples));
  56. expect (block == AudioBlock<SampleType> (data.data(), data.size(), (size_t) 0, numSamples));
  57. expect (block == AudioBlock<SampleType> (block));
  58. expect (block == AudioBlock<const SampleType> (data.data(), data.size(), numSamples));
  59. expect (block == AudioBlock<const SampleType> (data.data(), data.size(), (size_t) 0, numSamples));
  60. expect (block == AudioBlock<const SampleType> (block));
  61. }
  62. beginTest ("Swap");
  63. {
  64. resetBlocks();
  65. expect (block != otherBlock);
  66. expectEquals (block.getSample (0, 0), SampleType (1.0));
  67. expectEquals (block.getSample (0, 4), SampleType (5.0));
  68. expectEquals (otherBlock.getSample (0, 0), SampleType (-1.0));
  69. expectEquals (otherBlock.getSample (0, 3), SampleType (-4.0));
  70. block.swap (otherBlock);
  71. expect (block != otherBlock);
  72. expectEquals (otherBlock.getSample (0, 0), SampleType (1.0));
  73. expectEquals (otherBlock.getSample (0, 4), SampleType (5.0));
  74. expectEquals (block.getSample (0, 0), SampleType (-1.0));
  75. expectEquals (block.getSample (0, 3), SampleType (-4.0));
  76. block.swap (otherBlock);
  77. expectEquals (block.getSample (0, 0), SampleType (1.0));
  78. expectEquals (block.getSample (0, 4), SampleType (5.0));
  79. expectEquals (otherBlock.getSample (0, 0), SampleType (-1.0));
  80. expectEquals (otherBlock.getSample (0, 3), SampleType (-4.0));
  81. }
  82. beginTest ("Getters and setters");
  83. {
  84. resetBlocks();
  85. expectEquals ((int) block.getNumChannels(), (int) data.size());
  86. expectEquals ((int) block.getNumSamples(), numSamples);
  87. expectEquals (block.getChannelPointer (0)[2], SampleType (3.0));
  88. block.getChannelPointer (0)[2] = SampleType (999.0);
  89. expectEquals (block.getChannelPointer (0)[2], SampleType (999.0));
  90. expectEquals (block.getSample (0, 4), SampleType (5.0));
  91. expectEquals (block.getSample (1, 4), SampleType (11.0));
  92. expectEquals (block.getSingleChannelBlock (1).getSample (0, 3), block.getSample (1, 3));
  93. expectEquals (block.getSubsetChannelBlock (0, 2).getSample (1, 3), block.getSample (1, 3));
  94. expectEquals (block.getSubsetChannelBlock (1, 1).getSample (0, 3), block.getSample (1, 3));
  95. block.setSample (1, 1, SampleType (777.0));
  96. expectEquals (block.getSample (1, 1), SampleType (777.0));
  97. block.addSample (1, 1, SampleType (1.0));
  98. expectEquals (block.getSample (1, 1), SampleType (778.0));
  99. }
  100. beginTest ("Basic copying");
  101. {
  102. block.clear();
  103. expectEquals (block.getSample (0, 2), SampleType (0.0));
  104. expectEquals (block.getSample (1, 4), SampleType (0.0));
  105. block.fill ((NumericType) 456.0);
  106. expectEquals (block.getSample (0, 2), SampleType (456.0));
  107. expectEquals (block.getSample (1, 4), SampleType (456.0));
  108. block.copyFrom (otherBlock);
  109. expect (block != otherBlock);
  110. expectEquals (block.getSample (0, 2), otherBlock.getSample (0, 2));
  111. expectEquals (block.getSample (1, 4), otherBlock.getSample (1, 4));
  112. resetBlocks();
  113. SampleType testSample1 = block.getSample (0, 2);
  114. SampleType testSample2 = block.getSample (1, 3);
  115. expectNotEquals (testSample1, block.getSample (0, 4));
  116. expectNotEquals (testSample2, block.getSample (1, 5));
  117. block.move (0, 2);
  118. expectEquals (block.getSample (0, 4), testSample1);
  119. expectEquals (block.getSample (1, 5), testSample2);
  120. }
  121. beginTest ("Addition");
  122. {
  123. resetBlocks();
  124. block.add ((NumericType) 15.0);
  125. expectEquals (block.getSample (0, 4), SampleType (20.0));
  126. expectEquals (block.getSample (1, 4), SampleType (26.0));
  127. block.add (otherBlock);
  128. expectEquals (block.getSample (0, 4), SampleType (15.0));
  129. expectEquals (block.getSample (1, 4), SampleType (15.0));
  130. block.replaceWithSumOf (otherBlock, (NumericType) 9.0);
  131. expectEquals (block.getSample (0, 4), SampleType (4.0));
  132. expectEquals (block.getSample (1, 4), SampleType (-2.0));
  133. resetBlocks();
  134. block.replaceWithSumOf (block, otherBlock);
  135. expectEquals (block.getSample (0, 4), SampleType (0.0));
  136. expectEquals (block.getSample (1, 4), SampleType (0.0));
  137. }
  138. beginTest ("Subtraction");
  139. {
  140. resetBlocks();
  141. block.subtract ((NumericType) 15.0);
  142. expectEquals (block.getSample (0, 4), SampleType (-10.0));
  143. expectEquals (block.getSample (1, 4), SampleType (-4.0));
  144. block.subtract (otherBlock);
  145. expectEquals (block.getSample (0, 4), SampleType (-5.0));
  146. expectEquals (block.getSample (1, 4), SampleType (7.0));
  147. block.replaceWithDifferenceOf (otherBlock, (NumericType) 9.0);
  148. expectEquals (block.getSample (0, 4), SampleType (-14.0));
  149. expectEquals (block.getSample (1, 4), SampleType (-20.0));
  150. resetBlocks();
  151. block.replaceWithDifferenceOf (block, otherBlock);
  152. expectEquals (block.getSample (0, 4), SampleType (10.0));
  153. expectEquals (block.getSample (1, 4), SampleType (22.0));
  154. }
  155. beginTest ("Multiplication");
  156. {
  157. resetBlocks();
  158. block.multiplyBy ((NumericType) 10.0);
  159. expectEquals (block.getSample (0, 4), SampleType (50.0));
  160. expectEquals (block.getSample (1, 4), SampleType (110.0));
  161. block.multiplyBy (otherBlock);
  162. expectEquals (block.getSample (0, 4), SampleType (-250.0));
  163. expectEquals (block.getSample (1, 4), SampleType (-1210.0));
  164. block.replaceWithProductOf (otherBlock, (NumericType) 3.0);
  165. expectEquals (block.getSample (0, 4), SampleType (-15.0));
  166. expectEquals (block.getSample (1, 4), SampleType (-33.0));
  167. resetBlocks();
  168. block.replaceWithProductOf (block, otherBlock);
  169. expectEquals (block.getSample (0, 4), SampleType (-25.0));
  170. expectEquals (block.getSample (1, 4), SampleType (-121.0));
  171. }
  172. beginTest ("Multiply add");
  173. {
  174. resetBlocks();
  175. block.addProductOf (otherBlock, (NumericType) -1.0);
  176. expectEquals (block.getSample (0, 4), SampleType (10.0));
  177. expectEquals (block.getSample (1, 4), SampleType (22.0));
  178. block.addProductOf (otherBlock, otherBlock);
  179. expectEquals (block.getSample (0, 4), SampleType (35.0));
  180. expectEquals (block.getSample (1, 4), SampleType (143.0));
  181. }
  182. beginTest ("Negative abs min max");
  183. {
  184. resetBlocks();
  185. otherBlock.negate();
  186. block.add (otherBlock);
  187. expectEquals (block.getSample (0, 4), SampleType (10.0));
  188. expectEquals (block.getSample (1, 4), SampleType (22.0));
  189. block.replaceWithNegativeOf (otherBlock);
  190. expectEquals (block.getSample (0, 4), SampleType (-5.0));
  191. expectEquals (block.getSample (1, 4), SampleType (-11.0));
  192. block.clear();
  193. otherBlock.negate();
  194. block.replaceWithAbsoluteValueOf (otherBlock);
  195. expectEquals (block.getSample (0, 4), SampleType (5.0));
  196. expectEquals (block.getSample (1, 4), SampleType (11.0));
  197. resetBlocks();
  198. block.replaceWithMinOf (block, otherBlock);
  199. expectEquals (block.getSample (0, 4), SampleType (-5.0));
  200. expectEquals (block.getSample (1, 4), SampleType (-11.0));
  201. resetBlocks();
  202. block.replaceWithMaxOf (block, otherBlock);
  203. expectEquals (block.getSample (0, 4), SampleType (5.0));
  204. expectEquals (block.getSample (1, 4), SampleType (11.0));
  205. resetBlocks();
  206. auto range = block.findMinAndMax();
  207. expectEquals (SampleType (range.getStart()), SampleType (1.0));
  208. expectEquals (SampleType (range.getEnd()), SampleType (12.0));
  209. }
  210. beginTest ("Operators");
  211. {
  212. resetBlocks();
  213. block += (NumericType) 10.0;
  214. expectEquals (block.getSample (0, 4), SampleType (15.0));
  215. expectEquals (block.getSample (1, 4), SampleType (21.0));
  216. block += otherBlock;
  217. expectEquals (block.getSample (0, 4), SampleType (10.0));
  218. expectEquals (block.getSample (1, 4), SampleType (10.0));
  219. resetBlocks();
  220. block -= (NumericType) 10.0;
  221. expectEquals (block.getSample (0, 4), SampleType (-5.0));
  222. expectEquals (block.getSample (1, 4), SampleType (1.0));
  223. block -= otherBlock;
  224. expectEquals (block.getSample (0, 4), SampleType (0.0));
  225. expectEquals (block.getSample (1, 4), SampleType (12.0));
  226. resetBlocks();
  227. block *= (NumericType) 10.0;
  228. expectEquals (block.getSample (0, 4), SampleType (50.0));
  229. expectEquals (block.getSample (1, 4), SampleType (110.0));
  230. block *= otherBlock;
  231. expectEquals (block.getSample (0, 4), SampleType (-250.0));
  232. expectEquals (block.getSample (1, 4), SampleType (-1210.0));
  233. }
  234. beginTest ("Process");
  235. {
  236. resetBlocks();
  237. AudioBlock<SampleType>::process (block, otherBlock, [] (SampleType x) { return x + (NumericType) 1.0; });
  238. expectEquals (otherBlock.getSample (0, 4), SampleType (6.0));
  239. expectEquals (otherBlock.getSample (1, 4), SampleType (12.0));
  240. }
  241. beginTest ("Copying");
  242. {
  243. resetBlocks();
  244. copyingTests();
  245. }
  246. beginTest ("Smoothing");
  247. {
  248. resetBlocks();
  249. smoothedValueTests();
  250. }
  251. }
  252. private:
  253. //==============================================================================
  254. void copyingTests()
  255. {
  256. if constexpr (std::is_scalar_v<SampleType>)
  257. {
  258. auto unchangedElement1 = block.getSample (0, 4);
  259. auto unchangedElement2 = block.getSample (1, 1);
  260. AudioBuffer<SampleType> otherBuffer (otherData.data(), (int) otherData.size(), numSamples);
  261. block.copyFrom (otherBuffer, 1, 2, 2);
  262. expectEquals (block.getSample (0, 4), unchangedElement1);
  263. expectEquals (block.getSample (1, 1), unchangedElement2);
  264. expectEquals (block.getSample (0, 2), otherBuffer.getSample (0, 1));
  265. expectEquals (block.getSample (1, 3), otherBuffer.getSample (1, 2));
  266. resetBlocks();
  267. unchangedElement1 = otherBuffer.getSample (0, 4);
  268. unchangedElement2 = otherBuffer.getSample (1, 3);
  269. block.copyTo (otherBuffer, 2, 1, 2);
  270. expectEquals (otherBuffer.getSample (0, 4), unchangedElement1);
  271. expectEquals (otherBuffer.getSample (1, 3), unchangedElement2);
  272. expectEquals (otherBuffer.getSample (0, 1), block.getSample (0, 2));
  273. expectEquals (otherBuffer.getSample (1, 2), block.getSample (1, 3));
  274. }
  275. #if JUCE_USE_SIMD
  276. else
  277. {
  278. auto numSIMDElements = SIMDRegister<NumericType>::SIMDNumElements;
  279. AudioBuffer<NumericType> numericData ((int) block.getNumChannels(),
  280. (int) (block.getNumSamples() * numSIMDElements));
  281. for (int c = 0; c < numericData.getNumChannels(); ++c)
  282. std::fill_n (numericData.getWritePointer (c), numericData.getNumSamples(), (NumericType) 1.0);
  283. numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 0.127, (NumericType) 17.3);
  284. auto lastUnchangedIndexBeforeCopiedRange = (int) ((numSIMDElements * 2) - 1);
  285. auto firstUnchangedIndexAfterCopiedRange = (int) ((numSIMDElements * 4) + 1);
  286. auto unchangedElement1 = numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange);
  287. auto unchangedElement2 = numericData.getSample (1, firstUnchangedIndexAfterCopiedRange);
  288. block.copyTo (numericData, 1, 2, 2);
  289. expectEquals (numericData.getSample (0, lastUnchangedIndexBeforeCopiedRange), unchangedElement1);
  290. expectEquals (numericData.getSample (1, firstUnchangedIndexAfterCopiedRange), unchangedElement2);
  291. expect (SampleType (numericData.getSample (0, 2 * (int) numSIMDElements)) == block.getSample (0, 1));
  292. expect (SampleType (numericData.getSample (1, 3 * (int) numSIMDElements)) == block.getSample (1, 2));
  293. numericData.applyGainRamp (0, numericData.getNumSamples(), (NumericType) 15.1, (NumericType) 0.7);
  294. auto unchangedSIMDElement1 = block.getSample (0, 1);
  295. auto unchangedSIMDElement2 = block.getSample (1, 4);
  296. block.copyFrom (numericData, 1, 2, 2);
  297. expect (block.getSample (0, 1) == unchangedSIMDElement1);
  298. expect (block.getSample (1, 4) == unchangedSIMDElement2);
  299. expectEquals (block.getSample (0, 2).get (0), numericData.getSample (0, (int) numSIMDElements));
  300. expectEquals (block.getSample (1, 3).get (0), numericData.getSample (1, (int) (numSIMDElements * 2)));
  301. if (numSIMDElements > 1)
  302. {
  303. expectEquals (block.getSample (0, 2).get (1), numericData.getSample (0, (int) (numSIMDElements + 1)));
  304. expectEquals (block.getSample (1, 3).get (1), numericData.getSample (1, (int) ((numSIMDElements * 2) + 1)));
  305. }
  306. }
  307. #endif
  308. }
  309. //==============================================================================
  310. void smoothedValueTests()
  311. {
  312. if constexpr (std::is_scalar_v<SampleType>)
  313. {
  314. block.fill ((SampleType) 1.0);
  315. SmoothedValue<SampleType> sv { (SampleType) 1.0 };
  316. sv.reset (1, 4);
  317. sv.setTargetValue ((SampleType) 0.0);
  318. block.multiplyBy (sv);
  319. expect (block.getSample (0, 2) < (SampleType) 1.0);
  320. expect (block.getSample (1, 2) < (SampleType) 1.0);
  321. expect (block.getSample (0, 2) > (SampleType) 0.0);
  322. expect (block.getSample (1, 2) > (SampleType) 0.0);
  323. expectEquals (block.getSample (0, 5), (SampleType) 0.0);
  324. expectEquals (block.getSample (1, 5), (SampleType) 0.0);
  325. sv.setCurrentAndTargetValue (-1.0f);
  326. sv.setTargetValue (0.0f);
  327. otherBlock.fill (-1.0f);
  328. block.replaceWithProductOf (otherBlock, sv);
  329. expect (block.getSample (0, 2) < (SampleType) 1.0);
  330. expect (block.getSample (1, 2) < (SampleType) 1.0);
  331. expect (block.getSample (0, 2) > (SampleType) 0.0);
  332. expect (block.getSample (1, 2) > (SampleType) 0.0);
  333. expectEquals (block.getSample (0, 5), (SampleType) 0.0);
  334. expectEquals (block.getSample (1, 5), (SampleType) 0.0);
  335. }
  336. }
  337. //==============================================================================
  338. void resetBlocks()
  339. {
  340. auto value = SampleType (1.0);
  341. for (size_t c = 0; c < block.getNumChannels(); ++c)
  342. {
  343. for (size_t i = 0; i < block.getNumSamples(); ++i)
  344. {
  345. block.setSample ((int) c, (int) i, value);
  346. value += SampleType (1.0);
  347. }
  348. }
  349. otherBlock.replaceWithNegativeOf (block);
  350. }
  351. //==============================================================================
  352. static SampleType* allocateAlignedMemory (int numSamplesToAllocate)
  353. {
  354. auto alignmentLowerBound = std::alignment_of_v<SampleType>;
  355. #if ! JUCE_WINDOWS
  356. alignmentLowerBound = jmax (sizeof (void*), alignmentLowerBound);
  357. #endif
  358. auto alignmentOrder = std::ceil (std::log2 (alignmentLowerBound));
  359. auto requiredAlignment = (size_t) std::pow (2, alignmentOrder);
  360. auto size = (size_t) numSamplesToAllocate * sizeof (SampleType);
  361. #if JUCE_WINDOWS
  362. auto* memory = _aligned_malloc (size, requiredAlignment);
  363. #else
  364. void* memory;
  365. auto result = posix_memalign (&memory, requiredAlignment, size);
  366. if (result != 0)
  367. {
  368. jassertfalse;
  369. return nullptr;
  370. }
  371. #endif
  372. return static_cast<SampleType*> (memory);
  373. }
  374. void deallocateAlignedMemory (void* address)
  375. {
  376. #if JUCE_WINDOWS
  377. _aligned_free (address);
  378. #else
  379. free (address);
  380. #endif
  381. }
  382. //==============================================================================
  383. static constexpr int numChannels = 2, numSamples = 6;
  384. std::array<SampleType*, numChannels> data, otherData;
  385. AudioBlock<SampleType> block, otherBlock;
  386. };
  387. static AudioBlockUnitTests<float> audioBlockFloatUnitTests;
  388. static AudioBlockUnitTests<double> audioBlockDoubleUnitTests;
  389. #if JUCE_USE_SIMD
  390. static AudioBlockUnitTests<SIMDRegister<float>> audioBlockSIMDFloatUnitTests;
  391. static AudioBlockUnitTests<SIMDRegister<double>> audioBlockSIMDDoubleUnitTests;
  392. #endif
  393. } // namespace juce::dsp