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.

377 lines
10KB

  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. /*
  19. Utilities for converting sequences of bytes to and from
  20. C++ struct types.
  21. */
  22. namespace juce::midi_ci::detail::Marshalling
  23. {
  24. template <uint8_t> struct IntForNumBytes;
  25. template <> struct IntForNumBytes<1> { using Type = uint8_t; };
  26. template <> struct IntForNumBytes<2> { using Type = uint16_t; };
  27. template <> struct IntForNumBytes<4> { using Type = uint32_t; };
  28. template <uint8_t NumBytes> using IntForNumBytesT = typename IntForNumBytes<NumBytes>::Type;
  29. //==============================================================================
  30. /*
  31. Reads a sequence of bytes representing a MIDI-CI message, and populates
  32. structs with the information contained in the message.
  33. */
  34. class Reader
  35. {
  36. public:
  37. /* Constructs a reader that will parse the provided buffer, using the most
  38. recent known MIDI-CI version.
  39. */
  40. explicit Reader (Span<const std::byte> b)
  41. : Reader (b, static_cast<uint8_t> (MessageMeta::implementationVersion)) {}
  42. /* Constructs a reader for the provided MIDI-CI version that will parse
  43. the provided buffer. Fields introduced in later versions will be ignored,
  44. and so left with their default values.
  45. */
  46. Reader (Span<const std::byte> b, int v)
  47. : bytes (b), version (v) {}
  48. std::optional<int> getVersion() const { return version; }
  49. /* Attempts to interpret the byte sequence passed to the constructor
  50. as a sequence of structs 'T'.
  51. Returns true if parsing succeeds, otherwise returns false.
  52. */
  53. template <typename... T>
  54. bool operator() (T&&... t)
  55. {
  56. return (doArchiveChecked (std::forward<T> (t)) && ...);
  57. }
  58. private:
  59. template <typename T>
  60. bool doArchiveChecked (T&& t)
  61. {
  62. if (failed)
  63. return false;
  64. doArchive (t);
  65. return ! failed;
  66. }
  67. void doArchive (ChannelInGroup& x)
  68. {
  69. if (const auto popped = popBytes (1))
  70. {
  71. const auto p = *popped;
  72. x = ChannelInGroup (p[0] & std::byte { 0x7f });
  73. return;
  74. }
  75. failed = true;
  76. }
  77. // If we're trying to parse into a constant, then we should check that the next byte(s)
  78. // match that constant.
  79. void doArchive (const std::byte& x)
  80. {
  81. std::byte temp{};
  82. if (! doArchiveChecked (temp))
  83. return;
  84. failed |= x != temp;
  85. }
  86. void doArchive (std::byte& x)
  87. {
  88. if (const auto popped = popBytes (1))
  89. {
  90. const auto p = *popped;
  91. x = p[0] & std::byte { 0x7f };
  92. return;
  93. }
  94. failed = true;
  95. }
  96. void doArchive (const uint16_t& x)
  97. {
  98. uint16_t temp{};
  99. if (! doArchiveChecked (temp))
  100. return;
  101. failed |= temp != x;
  102. }
  103. void doArchive (uint16_t& x)
  104. {
  105. if (const auto popped = popBytes (2))
  106. {
  107. const auto p = *popped;
  108. x = (uint16_t) (((uint16_t) p[0] & 0x7f) << 0x00)
  109. | (uint16_t) (((uint16_t) p[1] & 0x7f) << 0x07);
  110. return;
  111. }
  112. failed = true;
  113. }
  114. void doArchive (const uint32_t& x)
  115. {
  116. uint32_t temp{};
  117. if (! doArchiveChecked (temp))
  118. return;
  119. failed |= temp != x;
  120. }
  121. void doArchive (uint32_t& x)
  122. {
  123. if (const auto popped = popBytes (4))
  124. {
  125. const auto p = *popped;
  126. x = (((uint32_t) p[0] & 0x7f) << 0x00)
  127. | (((uint32_t) p[1] & 0x7f) << 0x07)
  128. | (((uint32_t) p[2] & 0x7f) << 0x0e)
  129. | (((uint32_t) p[3] & 0x7f) << 0x15);
  130. return;
  131. }
  132. failed = true;
  133. }
  134. template <uint8_t NumBytes, bool B>
  135. void doArchive (MessageMeta::SpanWithSizeBytes<NumBytes, Span<const std::byte>, B> x)
  136. {
  137. IntForNumBytesT<NumBytes> numBytes{};
  138. // Read the number of bytes in the field
  139. if (! doArchiveChecked (numBytes))
  140. return;
  141. // Attempt to pop that many bytes
  142. if (const auto popped = popBytes (numBytes))
  143. {
  144. x.span = *popped;
  145. return;
  146. }
  147. failed = true;
  148. }
  149. template <uint8_t NumBytes, size_t N>
  150. void doArchive (MessageMeta::SpanWithSizeBytes<NumBytes, Span<const std::array<std::byte, N>>> x)
  151. {
  152. IntForNumBytesT<NumBytes> numItems{};
  153. // Read the number of items in the field
  154. if (! doArchiveChecked (numItems))
  155. return;
  156. if (const auto popped = popBytes (numItems * N))
  157. {
  158. x.span = Span (unalignedPointerCast<const std::array<std::byte, N>*> (popped->data()), numItems);
  159. return;
  160. }
  161. failed = true;
  162. }
  163. template <size_t N>
  164. void doArchive (Span<const std::byte, N>& x)
  165. {
  166. if (const auto popped = popBytes (bytes.size()))
  167. {
  168. x = *popped;
  169. return;
  170. }
  171. failed = true;
  172. }
  173. template <size_t N>
  174. void doArchive (std::array<std::byte, N>& x)
  175. {
  176. if (const auto popped = popBytes (x.size()))
  177. {
  178. const auto p = *popped;
  179. std::transform (p.begin(), p.end(), x.begin(), [] (std::byte b)
  180. {
  181. return b & std::byte { 0x7f };
  182. });
  183. return;
  184. }
  185. failed = true;
  186. }
  187. template <typename T>
  188. void doArchive (T& t)
  189. {
  190. juce::detail::doLoad (*this, t);
  191. }
  192. template <typename T>
  193. void doArchive (Named<T> named)
  194. {
  195. doArchiveChecked (named.value);
  196. }
  197. std::optional<Span<const std::byte>> popBytes (size_t num)
  198. {
  199. if (bytes.size() < num)
  200. return {};
  201. const Span result { bytes.data(), num };
  202. bytes = Span { bytes.data() + num, bytes.size() - num };
  203. return result;
  204. }
  205. Span<const std::byte> bytes; /* Bytes making up a CI message. */
  206. int version{}; /* The version to assume when parsing the message, specified in the message header. */
  207. bool failed = false;
  208. };
  209. //==============================================================================
  210. /*
  211. Converts one or more structs into a byte sequence suitable for transmission
  212. as a MIDI-CI message.
  213. */
  214. class Writer
  215. {
  216. public:
  217. /* Constructs a writer that will write into the provided buffer. */
  218. explicit Writer (std::vector<std::byte>& b)
  219. : Writer (b, static_cast<uint8_t> (MessageMeta::implementationVersion)) {}
  220. /* Constructs a writer that will write a MIDI-CI message of the requested
  221. version to the provided buffer.
  222. Fields introduced in later MIDI-CI versions will be ignored.
  223. */
  224. Writer (std::vector<std::byte>& b, int v)
  225. : bytes (b), version (v) {}
  226. std::optional<int> getVersion() const { return version; }
  227. /* Formats the information contained in the provided structs into a
  228. MIDI-CI message, and returns a bool indicating success or failure.
  229. */
  230. template <typename... T>
  231. bool operator() (const T&... t)
  232. {
  233. return (doArchiveChecked (t) && ...);
  234. }
  235. private:
  236. template <typename T>
  237. bool doArchiveChecked (T&& t)
  238. {
  239. if (failed)
  240. return false;
  241. doArchive (t);
  242. return ! failed;
  243. }
  244. void doArchive (ChannelInGroup x)
  245. {
  246. doArchiveChecked (std::byte (x));
  247. }
  248. void doArchive (std::byte x)
  249. {
  250. bytes.push_back (x);
  251. }
  252. void doArchive (uint16_t x)
  253. {
  254. bytes.insert (bytes.end(), { (std::byte) ((x >> 0x00) & 0x7f),
  255. (std::byte) ((x >> 0x07) & 0x7f) });
  256. }
  257. void doArchive (uint32_t x)
  258. {
  259. bytes.insert (bytes.end(), { (std::byte) ((x >> 0x00) & 0x7f),
  260. (std::byte) ((x >> 0x07) & 0x7f),
  261. (std::byte) ((x >> 0x0e) & 0x7f),
  262. (std::byte) ((x >> 0x15) & 0x7f) });
  263. }
  264. template <uint8_t NumBytes, typename T, bool B>
  265. void doArchive (MessageMeta::SpanWithSizeBytes<NumBytes, T, B> x)
  266. {
  267. if (x.span.size() >= (1 << (7 * NumBytes)))
  268. {
  269. // Unable to express the size of the field in the requested number of bytes
  270. jassertfalse;
  271. failed = true;
  272. return;
  273. }
  274. // Write the number of bytes, followed by the bytes themselves.
  275. const auto numBytes = (IntForNumBytesT<NumBytes>) x.span.size();
  276. doArchiveChecked (numBytes);
  277. doArchiveChecked (x.span);
  278. }
  279. template <typename T, size_t N>
  280. void doArchive (Span<const T, N> x)
  281. {
  282. failed = ! std::all_of (x.begin(), x.end(), [&] (const auto& item)
  283. {
  284. return doArchiveChecked (item);
  285. });
  286. }
  287. template <size_t N>
  288. void doArchive (const std::array<std::byte, N>& x)
  289. {
  290. bytes.insert (bytes.end(), x.begin(), x.end());
  291. }
  292. template <typename T>
  293. void doArchive (const T& t)
  294. {
  295. juce::detail::doSave (*this, t);
  296. }
  297. template <typename T>
  298. void doArchive (Named<T> named)
  299. {
  300. doArchiveChecked (named.value);
  301. }
  302. std::vector<std::byte>& bytes; /* The buffer that will hold the completed message. */
  303. int version{}; /* The version to assume when writing the message, specified in the message header. */
  304. bool failed = false;
  305. };
  306. } // namespace juce::midi_ci::detail::Marshalling