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.

275 lines
8.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - 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. /**
  18. All sysex messages to or from a BLOCKS device begin with these header bytes.
  19. The next byte that follows indicates the device index within the topology, where
  20. the 0x40 bit is set for device->host messages, and clear for host->device messages.
  21. The lower 6 bits contain the topology index of the destination or source device.
  22. */
  23. static const uint8 roliSysexHeader[] = { 0xf0, 0x00, 0x21, 0x10, 0x77 };
  24. static uint8 calculatePacketChecksum (const uint8* data, uint32 size) noexcept
  25. {
  26. uint8 checksum = (uint8) size;
  27. for (uint32 i = 0; i < size; ++i)
  28. checksum += checksum * 2 + data[i];
  29. return checksum & 0x7f;
  30. }
  31. //==============================================================================
  32. template <int numBits>
  33. struct IntegerWithBitSize
  34. {
  35. IntegerWithBitSize() noexcept = default;
  36. IntegerWithBitSize (const IntegerWithBitSize&) noexcept = default;
  37. IntegerWithBitSize& operator= (const IntegerWithBitSize&) noexcept = default;
  38. IntegerWithBitSize (uint32 v) noexcept : value (v)
  39. {
  40. static_assert (numBits <= 32, "numBits must be <= 32");
  41. jassert (v >= 0 && v <= maxValue);
  42. }
  43. enum
  44. {
  45. bits = numBits,
  46. maxValue = static_cast<uint32> ((1ULL << numBits) - 1ULL)
  47. };
  48. operator uint32() const noexcept { return value; }
  49. uint32 get() const noexcept { return value; }
  50. uint8 getScaledToByte() const noexcept
  51. {
  52. return (uint8) (numBits < 8 ? (uint32) (value << (8 - numBits))
  53. : (uint32) (value >> (numBits - 8)));
  54. }
  55. float toUnipolarFloat() const noexcept { return value / (float) maxValue; }
  56. float toBipolarFloat() const noexcept { return static_cast<int32> (value << (32 - numBits)) / (float) 0x80000000u; }
  57. static IntegerWithBitSize fromUnipolarFloat (float value) noexcept
  58. {
  59. static_assert (numBits <= 31, "numBits must be <= 31");
  60. return IntegerWithBitSize ((uint32) jlimit (0, (int) maxValue, (int) (value * maxValue)));
  61. }
  62. static IntegerWithBitSize fromBipolarFloat (float value) noexcept
  63. {
  64. static_assert (numBits <= 31, "numBits must be <= 31");
  65. return IntegerWithBitSize (maxValue & (uint32) jlimit ((int) -(maxValue / 2), (int) (maxValue / 2), (int) (value * (maxValue / 2))));
  66. }
  67. uint32 value = 0;
  68. };
  69. //==============================================================================
  70. /**
  71. This helper class allocates a block of 7-bit bytes and can push sequences of bits into it.
  72. @see Packed7BitArrayReader
  73. */
  74. template <int allocatedBytes>
  75. struct Packed7BitArrayBuilder
  76. {
  77. const void* getData() const noexcept { return data; }
  78. int size() const noexcept { return bytesWritten + (bitsInCurrentByte > 0 ? 1 : 0); }
  79. bool hasCapacity (int bitsNeeded) const noexcept
  80. {
  81. return ((bytesWritten + 2) * 7 + bitsInCurrentByte + bitsNeeded) <= allocatedBytes * 7;
  82. }
  83. void writeHeaderSysexBytes (uint8 deviceIndex) noexcept
  84. {
  85. jassert (bytesWritten + bitsInCurrentByte == 0);
  86. for (int i = 0; i < (int) sizeof (roliSysexHeader); ++i)
  87. data[bytesWritten++] = roliSysexHeader[i];
  88. jassert (deviceIndex < 128);
  89. data[bytesWritten++] = deviceIndex & 0x7f;
  90. }
  91. void writePacketSysexFooter() noexcept
  92. {
  93. if (bitsInCurrentByte != 0)
  94. {
  95. bitsInCurrentByte = 0;
  96. ++bytesWritten;
  97. }
  98. jassert (hasCapacity (0));
  99. uint32 headerBytes = (uint32) sizeof (roliSysexHeader) + 1;
  100. data[bytesWritten] = calculatePacketChecksum (data + headerBytes, (uint32) bytesWritten - headerBytes);
  101. ++bytesWritten;
  102. data[bytesWritten++] = 0xf7;
  103. }
  104. template <int numBits>
  105. Packed7BitArrayBuilder& operator<< (IntegerWithBitSize<numBits> value) noexcept
  106. {
  107. writeBits (value.value, numBits);
  108. return *this;
  109. }
  110. void writeBits (uint32 value, int numBits) noexcept
  111. {
  112. jassert (numBits <= 32);
  113. jassert (hasCapacity (numBits));
  114. jassert (numBits == 32 || (value >> numBits) == 0);
  115. while (numBits > 0)
  116. {
  117. if (bitsInCurrentByte == 0)
  118. {
  119. if (numBits < 7)
  120. {
  121. data[bytesWritten] = (uint8) value;
  122. bitsInCurrentByte = numBits;
  123. return;
  124. }
  125. if (numBits == 7)
  126. {
  127. data[bytesWritten++] = (uint8) value;
  128. return;
  129. }
  130. data[bytesWritten++] = (uint8) (value & 0x7f);
  131. value >>= 7;
  132. numBits -= 7;
  133. }
  134. else
  135. {
  136. const int bitsToDo = jmin (7 - bitsInCurrentByte, numBits);
  137. data[bytesWritten] |= ((value & ((1 << bitsToDo) - 1)) << bitsInCurrentByte);
  138. value >>= bitsToDo;
  139. numBits -= bitsToDo;
  140. bitsInCurrentByte += bitsToDo;
  141. if (bitsInCurrentByte == 7)
  142. {
  143. bitsInCurrentByte = 0;
  144. ++bytesWritten;
  145. }
  146. }
  147. }
  148. }
  149. struct State
  150. {
  151. int bytesWritten, bitsInCurrentByte;
  152. };
  153. State getState() const noexcept
  154. {
  155. return { bytesWritten, bitsInCurrentByte };
  156. }
  157. void restore (State state) noexcept
  158. {
  159. bytesWritten = state.bytesWritten;
  160. bitsInCurrentByte = state.bitsInCurrentByte;
  161. }
  162. private:
  163. uint8 data[allocatedBytes];
  164. int bytesWritten = 0, bitsInCurrentByte = 0;
  165. };
  166. //==============================================================================
  167. /**
  168. This helper class reads from a block of 7-bit bytes as sequences of bits.
  169. @see Packed7BitArrayBuilder
  170. */
  171. struct Packed7BitArrayReader
  172. {
  173. Packed7BitArrayReader (const void* sourceData, int numBytes) noexcept
  174. : data (static_cast<const uint8*> (sourceData)), totalBits (numBytes * 7)
  175. {
  176. }
  177. int getRemainingBits() const noexcept
  178. {
  179. return totalBits - bitsReadInCurrentByte;
  180. }
  181. template <typename Target>
  182. Target read() noexcept
  183. {
  184. return Target (readBits (Target::bits));
  185. }
  186. uint32 readBits (int numBits) noexcept
  187. {
  188. jassert (numBits <= 32);
  189. jassert (getRemainingBits() >= numBits);
  190. uint32 value = 0;
  191. int bitsSoFar = 0;
  192. while (numBits > 0)
  193. {
  194. const uint32 valueInCurrentByte = (*data >> bitsReadInCurrentByte);
  195. const int bitsAvailable = 7 - bitsReadInCurrentByte;
  196. if (bitsAvailable > numBits)
  197. {
  198. value |= ((valueInCurrentByte & ((1 << numBits) - 1)) << bitsSoFar);
  199. bitsReadInCurrentByte += numBits;
  200. break;
  201. }
  202. value |= (valueInCurrentByte << bitsSoFar);
  203. numBits -= bitsAvailable;
  204. bitsSoFar += bitsAvailable;
  205. bitsReadInCurrentByte = 0;
  206. ++data;
  207. totalBits -= 7;
  208. }
  209. return value;
  210. }
  211. static bool checksumIsOK (const uint8* data, uint32 size) noexcept
  212. {
  213. return size > 1 && calculatePacketChecksum (data, size - 1) == data[size - 1];
  214. }
  215. private:
  216. const uint8* data;
  217. int totalBits, bitsReadInCurrentByte = 0;
  218. };