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.

338 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace
  18. {
  19. void checkAndLimitZoneParameters (int minValue,
  20. int maxValue,
  21. int& valueToCheckAndLimit) noexcept
  22. {
  23. if (valueToCheckAndLimit < minValue || valueToCheckAndLimit > maxValue)
  24. {
  25. // if you hit this, one of the parameters you supplied for MPEZone
  26. // was not within the allowed range!
  27. // we fit this back into the allowed range here to maintain a valid
  28. // state for the zone, but probably the resulting zone is not what you
  29. //wanted it to be!
  30. jassertfalse;
  31. valueToCheckAndLimit = jlimit (minValue, maxValue, valueToCheckAndLimit);
  32. }
  33. }
  34. }
  35. //==============================================================================
  36. MPEZone::MPEZone (int masterChannel_,
  37. int numNoteChannels_,
  38. int perNotePitchbendRange_,
  39. int masterPitchbendRange_) noexcept
  40. : masterChannel (masterChannel_),
  41. numNoteChannels (numNoteChannels_),
  42. perNotePitchbendRange (perNotePitchbendRange_),
  43. masterPitchbendRange (masterPitchbendRange_)
  44. {
  45. checkAndLimitZoneParameters (1, 15, masterChannel);
  46. checkAndLimitZoneParameters (1, 16 - masterChannel, numNoteChannels);
  47. checkAndLimitZoneParameters (0, 96, perNotePitchbendRange);
  48. checkAndLimitZoneParameters (0, 96, masterPitchbendRange);
  49. }
  50. MPEZone::MPEZone (const MPEZone& other) noexcept
  51. : masterChannel (other.masterChannel),
  52. numNoteChannels (other.numNoteChannels),
  53. perNotePitchbendRange (other.perNotePitchbendRange),
  54. masterPitchbendRange (other.masterPitchbendRange)
  55. {
  56. }
  57. MPEZone& MPEZone::operator= (const MPEZone& other) noexcept
  58. {
  59. if (this != &other)
  60. {
  61. masterChannel = other.masterChannel;
  62. numNoteChannels = other.numNoteChannels;
  63. perNotePitchbendRange = other.perNotePitchbendRange;
  64. masterPitchbendRange = other.masterPitchbendRange;
  65. }
  66. return *this;
  67. }
  68. //==============================================================================
  69. int MPEZone::getMasterChannel() const noexcept
  70. {
  71. return masterChannel;
  72. }
  73. int MPEZone::getNumNoteChannels() const noexcept
  74. {
  75. return numNoteChannels;
  76. }
  77. int MPEZone::getFirstNoteChannel() const noexcept
  78. {
  79. return masterChannel + 1;
  80. }
  81. int MPEZone::getLastNoteChannel() const noexcept
  82. {
  83. return masterChannel + numNoteChannels;
  84. }
  85. Range<int> MPEZone::getNoteChannelRange() const noexcept
  86. {
  87. return Range<int>::withStartAndLength (getFirstNoteChannel(), getNumNoteChannels());
  88. }
  89. bool MPEZone::isUsingChannel (int channel) const noexcept
  90. {
  91. jassert (channel > 0 && channel <= 16);
  92. return channel >= masterChannel && channel <= masterChannel + numNoteChannels;
  93. }
  94. bool MPEZone::isUsingChannelAsNoteChannel (int channel) const noexcept
  95. {
  96. jassert (channel > 0 && channel <= 16);
  97. return channel > masterChannel && channel <= masterChannel + numNoteChannels;
  98. }
  99. int MPEZone::getPerNotePitchbendRange() const noexcept
  100. {
  101. return perNotePitchbendRange;
  102. }
  103. int MPEZone::getMasterPitchbendRange() const noexcept
  104. {
  105. return masterPitchbendRange;
  106. }
  107. void MPEZone::setPerNotePitchbendRange (int rangeInSemitones) noexcept
  108. {
  109. checkAndLimitZoneParameters (0, 96, rangeInSemitones);
  110. perNotePitchbendRange = rangeInSemitones;
  111. }
  112. void MPEZone::setMasterPitchbendRange (int rangeInSemitones) noexcept
  113. {
  114. checkAndLimitZoneParameters (0, 96, rangeInSemitones);
  115. masterPitchbendRange = rangeInSemitones;
  116. }
  117. //==============================================================================
  118. bool MPEZone::overlapsWith (MPEZone other) const noexcept
  119. {
  120. if (masterChannel == other.masterChannel)
  121. return true;
  122. if (masterChannel > other.masterChannel)
  123. return other.overlapsWith (*this);
  124. return masterChannel + numNoteChannels >= other.masterChannel;
  125. }
  126. //==============================================================================
  127. bool MPEZone::truncateToFit (MPEZone other) noexcept
  128. {
  129. const int masterChannelDiff = other.masterChannel - masterChannel;
  130. // we need at least 2 channels to be left after truncation:
  131. // 1 master channel and 1 note channel. otherwise we can't truncate.
  132. if (masterChannelDiff < 2)
  133. return false;
  134. numNoteChannels = jmin (numNoteChannels, masterChannelDiff - 1);
  135. return true;
  136. }
  137. //==============================================================================
  138. bool MPEZone::operator== (const MPEZone& other) const noexcept
  139. {
  140. return masterChannel == other.masterChannel
  141. && numNoteChannels == other.numNoteChannels
  142. && perNotePitchbendRange == other.perNotePitchbendRange
  143. && masterPitchbendRange == other.masterPitchbendRange;
  144. }
  145. bool MPEZone::operator!= (const MPEZone& other) const noexcept
  146. {
  147. return ! operator== (other);
  148. }
  149. //==============================================================================
  150. //==============================================================================
  151. #if JUCE_UNIT_TESTS
  152. class MPEZoneTests : public UnitTest
  153. {
  154. public:
  155. MPEZoneTests() : UnitTest ("MPEZone class") {}
  156. void runTest() override
  157. {
  158. beginTest ("initialisation");
  159. {
  160. {
  161. MPEZone zone (1, 10);
  162. expectEquals (zone.getMasterChannel(), 1);
  163. expectEquals (zone.getNumNoteChannels(), 10);
  164. expectEquals (zone.getFirstNoteChannel(), 2);
  165. expectEquals (zone.getLastNoteChannel(), 11);
  166. expectEquals (zone.getPerNotePitchbendRange(), 48);
  167. expectEquals (zone.getMasterPitchbendRange(), 2);
  168. expect (zone.isUsingChannel (1));
  169. expect (zone.isUsingChannel (2));
  170. expect (zone.isUsingChannel (10));
  171. expect (zone.isUsingChannel (11));
  172. expect (! zone.isUsingChannel (12));
  173. expect (! zone.isUsingChannel (16));
  174. expect (! zone.isUsingChannelAsNoteChannel (1));
  175. expect (zone.isUsingChannelAsNoteChannel (2));
  176. expect (zone.isUsingChannelAsNoteChannel (10));
  177. expect (zone.isUsingChannelAsNoteChannel (11));
  178. expect (! zone.isUsingChannelAsNoteChannel (12));
  179. expect (! zone.isUsingChannelAsNoteChannel (16));
  180. }
  181. {
  182. MPEZone zone (5, 4);
  183. expectEquals (zone.getMasterChannel(), 5);
  184. expectEquals (zone.getNumNoteChannels(), 4);
  185. expectEquals (zone.getFirstNoteChannel(), 6);
  186. expectEquals (zone.getLastNoteChannel(), 9);
  187. expectEquals (zone.getPerNotePitchbendRange(), 48);
  188. expectEquals (zone.getMasterPitchbendRange(), 2);
  189. expect (! zone.isUsingChannel (1));
  190. expect (! zone.isUsingChannel (4));
  191. expect (zone.isUsingChannel (5));
  192. expect (zone.isUsingChannel (6));
  193. expect (zone.isUsingChannel (8));
  194. expect (zone.isUsingChannel (9));
  195. expect (! zone.isUsingChannel (10));
  196. expect (! zone.isUsingChannel (16));
  197. expect (! zone.isUsingChannelAsNoteChannel (5));
  198. expect (zone.isUsingChannelAsNoteChannel (6));
  199. expect (zone.isUsingChannelAsNoteChannel (8));
  200. expect (zone.isUsingChannelAsNoteChannel (9));
  201. expect (! zone.isUsingChannelAsNoteChannel (10));
  202. }
  203. }
  204. beginTest ("getNoteChannelRange");
  205. {
  206. MPEZone zone (2, 10);
  207. Range<int> noteChannelRange = zone.getNoteChannelRange();
  208. expectEquals (noteChannelRange.getStart(), 3);
  209. expectEquals (noteChannelRange.getEnd(), 13);
  210. }
  211. beginTest ("setting master pitchbend range");
  212. {
  213. MPEZone zone (1, 10);
  214. zone.setMasterPitchbendRange (96);
  215. expectEquals (zone.getMasterPitchbendRange(), 96);
  216. zone.setMasterPitchbendRange (0);
  217. expectEquals (zone.getMasterPitchbendRange(), 0);
  218. expectEquals (zone.getPerNotePitchbendRange(), 48);
  219. }
  220. beginTest ("setting per-note pitchbend range");
  221. {
  222. MPEZone zone (1, 10);
  223. zone.setPerNotePitchbendRange (96);
  224. expectEquals (zone.getPerNotePitchbendRange(), 96);
  225. zone.setPerNotePitchbendRange (0);
  226. expectEquals (zone.getPerNotePitchbendRange(), 0);
  227. expectEquals (zone.getMasterPitchbendRange(), 2);
  228. }
  229. beginTest ("checking overlap");
  230. {
  231. testOverlapsWith (1, 10, 1, 10, true);
  232. testOverlapsWith (1, 4, 6, 3, false);
  233. testOverlapsWith (1, 4, 8, 3, false);
  234. testOverlapsWith (2, 10, 2, 8, true);
  235. testOverlapsWith (1, 10, 3, 2, true);
  236. testOverlapsWith (3, 10, 5, 9, true);
  237. }
  238. beginTest ("truncating");
  239. {
  240. testTruncateToFit (1, 10, 3, 10, true, 1, 1);
  241. testTruncateToFit (3, 10, 1, 10, false, 3, 10);
  242. testTruncateToFit (1, 10, 5, 8, true, 1, 3);
  243. testTruncateToFit (5, 8, 1, 10, false, 5, 8);
  244. testTruncateToFit (1, 10, 4, 3, true, 1, 2);
  245. testTruncateToFit (4, 3, 1, 10, false, 4, 3);
  246. testTruncateToFit (1, 3, 5, 3, true, 1, 3);
  247. testTruncateToFit (5, 3, 1, 3, false, 5, 3);
  248. testTruncateToFit (1, 3, 7, 3, true, 1, 3);
  249. testTruncateToFit (7, 3, 1, 3, false, 7, 3);
  250. testTruncateToFit (1, 10, 2, 10, false, 1, 10);
  251. testTruncateToFit (2, 10, 1, 10, false, 2, 10);
  252. }
  253. }
  254. private:
  255. //==============================================================================
  256. void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst,
  257. int masterChannelSecond, int numNoteChannelsSecond,
  258. bool expectedRetVal)
  259. {
  260. MPEZone first (masterChannelFirst, numNoteChannelsFirst);
  261. MPEZone second (masterChannelSecond, numNoteChannelsSecond);
  262. expect (first.overlapsWith (second) == expectedRetVal);
  263. expect (second.overlapsWith (first) == expectedRetVal);
  264. }
  265. //==============================================================================
  266. void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst,
  267. int masterChannelSecond, int numNoteChannelsSecond,
  268. bool expectedRetVal,
  269. int masterChannelFirstAfter, int numNoteChannelsFirstAfter)
  270. {
  271. MPEZone first (masterChannelFirst, numNoteChannelsFirst);
  272. MPEZone second (masterChannelSecond, numNoteChannelsSecond);
  273. expect (first.truncateToFit (second) == expectedRetVal);
  274. expectEquals (first.getMasterChannel(), masterChannelFirstAfter);
  275. expectEquals (first.getNumNoteChannels(), numNoteChannelsFirstAfter);
  276. }
  277. };
  278. static MPEZoneTests MPEZoneUnitTests;
  279. #endif // JUCE_UNIT_TESTS