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.

778 lines
24KB

  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::midi_ci
  19. {
  20. //==============================================================================
  21. /**
  22. Byte values representing different addresses within a group.
  23. @tags{Audio}
  24. */
  25. enum class ChannelInGroup : uint8_t
  26. {
  27. channel0 = 0x0,
  28. channel1 = 0x1,
  29. channel2 = 0x2,
  30. channel3 = 0x3,
  31. channel4 = 0x4,
  32. channel5 = 0x5,
  33. channel6 = 0x6,
  34. channel7 = 0x7,
  35. channel8 = 0x8,
  36. channel9 = 0x9,
  37. channelA = 0xA,
  38. channelB = 0xB,
  39. channelC = 0xC,
  40. channelD = 0xD,
  41. channelE = 0xE,
  42. channelF = 0xF,
  43. wholeGroup = 0x7e, ///< Refers to all channels in the UMP group
  44. wholeBlock = 0x7f, ///< Refers to all channels in the function block that contains the UMP group
  45. };
  46. /**
  47. Utility functions for working with the ChannelInGroup enum.
  48. @tags{Audio}
  49. */
  50. struct ChannelInGroupUtils
  51. {
  52. ChannelInGroupUtils() = delete;
  53. /** Converts a ChannelInGroup to a descriptive string. */
  54. static String toString (ChannelInGroup c)
  55. {
  56. if (c == ChannelInGroup::wholeGroup)
  57. return "Group";
  58. if (c == ChannelInGroup::wholeBlock)
  59. return "Function Block";
  60. const auto underlying = (std::underlying_type_t<ChannelInGroup>) c;
  61. return "Channel " + String (underlying + 1);
  62. }
  63. };
  64. using Profile = std::array<std::byte, 5>;
  65. //==============================================================================
  66. /**
  67. Namespace containing structs representing different kinds of MIDI-CI message.
  68. @tags{Audio}
  69. */
  70. namespace Message
  71. {
  72. /** Wraps a span, providing equality operators that compare the span
  73. contents elementwise.
  74. @tags{Audio}
  75. */
  76. template <typename T>
  77. struct ComparableRange
  78. {
  79. T& data;
  80. bool operator== (const ComparableRange& other) const
  81. {
  82. return std::equal (data.begin(), data.end(), other.data.begin(), other.data.end());
  83. }
  84. bool operator!= (const ComparableRange& other) const { return ! operator== (other); }
  85. };
  86. template <typename T> static constexpr auto makeComparableRange ( T& t) { return ComparableRange< T> { t }; }
  87. template <typename T> static constexpr auto makeComparableRange (const T& t) { return ComparableRange<const T> { t }; }
  88. //==============================================================================
  89. /**
  90. Holds fields that can be found at the beginning of every MIDI CI message.
  91. @tags{Audio}
  92. */
  93. struct Header
  94. {
  95. ChannelInGroup deviceID{};
  96. std::byte category{};
  97. std::byte version{};
  98. MUID source = MUID::makeUnchecked (0);
  99. MUID destination = MUID::makeUnchecked (0);
  100. auto tie() const
  101. {
  102. return std::tuple (deviceID, category, version, source, destination);
  103. }
  104. bool operator== (const Header& x) const { return tie() == x.tie(); }
  105. bool operator!= (const Header& x) const { return ! operator== (x); }
  106. };
  107. /**
  108. Groups together a CI message header, and some number of trailing bytes.
  109. @tags{Audio}
  110. */
  111. struct Generic
  112. {
  113. Header header;
  114. Span<const std::byte> data;
  115. };
  116. //==============================================================================
  117. /** See the MIDI-CI specification.
  118. @tags{Audio}
  119. */
  120. struct DiscoveryResponse
  121. {
  122. ump::DeviceInfo device;
  123. std::byte capabilities{};
  124. uint32_t maximumSysexSize{};
  125. std::byte outputPathID{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  126. std::byte functionBlock{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  127. auto tie() const
  128. {
  129. return std::tuple (device, capabilities, maximumSysexSize, outputPathID, functionBlock);
  130. }
  131. bool operator== (const DiscoveryResponse& x) const { return tie() == x.tie(); }
  132. bool operator!= (const DiscoveryResponse& x) const { return ! operator== (x); }
  133. };
  134. /** See the MIDI-CI specification.
  135. @tags{Audio}
  136. */
  137. struct Discovery
  138. {
  139. ump::DeviceInfo device;
  140. std::byte capabilities{};
  141. uint32_t maximumSysexSize{};
  142. std::byte outputPathID{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  143. auto tie() const
  144. {
  145. return std::tuple (device, capabilities, maximumSysexSize, outputPathID);
  146. }
  147. bool operator== (const Discovery& x) const { return tie() == x.tie(); }
  148. bool operator!= (const Discovery& x) const { return ! operator== (x); }
  149. };
  150. /** See the MIDI-CI specification.
  151. @tags{Audio}
  152. */
  153. struct EndpointInquiryResponse
  154. {
  155. std::byte status;
  156. Span<const std::byte> data;
  157. auto tie() const
  158. {
  159. return std::tuple (status, makeComparableRange (data));
  160. }
  161. bool operator== (const EndpointInquiryResponse& x) const { return tie() == x.tie(); }
  162. bool operator!= (const EndpointInquiryResponse& x) const { return ! operator== (x); }
  163. };
  164. /** See the MIDI-CI specification.
  165. @tags{Audio}
  166. */
  167. struct EndpointInquiry
  168. {
  169. std::byte status;
  170. auto tie() const
  171. {
  172. return std::tuple (status);
  173. }
  174. bool operator== (const EndpointInquiry& x) const { return tie() == x.tie(); }
  175. bool operator!= (const EndpointInquiry& x) const { return ! operator== (x); }
  176. };
  177. /** See the MIDI-CI specification.
  178. @tags{Audio}
  179. */
  180. struct InvalidateMUID
  181. {
  182. MUID target = MUID::makeUnchecked (0);
  183. auto tie() const
  184. {
  185. return std::tuple (target);
  186. }
  187. bool operator== (const InvalidateMUID& x) const { return tie() == x.tie(); }
  188. bool operator!= (const InvalidateMUID& x) const { return ! operator== (x); }
  189. };
  190. /** See the MIDI-CI specification.
  191. @tags{Audio}
  192. */
  193. struct ACK
  194. {
  195. std::byte originalCategory{};
  196. std::byte statusCode{};
  197. std::byte statusData{};
  198. std::array<std::byte, 5> details{};
  199. Span<const std::byte> messageText{};
  200. /** Convenience function that returns the message's text as a String. */
  201. String getMessageTextAsString() const
  202. {
  203. return Encodings::stringFrom7BitText (messageText);
  204. }
  205. auto tie() const
  206. {
  207. return std::tuple (originalCategory, statusCode, statusData, details, makeComparableRange (messageText));
  208. }
  209. bool operator== (const ACK& x) const { return tie() == x.tie(); }
  210. bool operator!= (const ACK& x) const { return ! operator== (x); }
  211. };
  212. /** See the MIDI-CI specification.
  213. @tags{Audio}
  214. */
  215. struct NAK
  216. {
  217. std::byte originalCategory{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  218. std::byte statusCode{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  219. std::byte statusData{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  220. std::array<std::byte, 5> details{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  221. Span<const std::byte> messageText{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  222. /** Convenience function that returns the message's text as a String. */
  223. String getMessageTextAsString() const
  224. {
  225. return Encodings::stringFrom7BitText (messageText);
  226. }
  227. auto tie() const
  228. {
  229. return std::tuple (originalCategory, statusCode, statusData, details, makeComparableRange (messageText));
  230. }
  231. bool operator== (const NAK& x) const { return tie() == x.tie(); }
  232. bool operator!= (const NAK& x) const { return ! operator== (x); }
  233. };
  234. /** See the MIDI-CI specification.
  235. @tags{Audio}
  236. */
  237. struct ProfileInquiryResponse
  238. {
  239. Span<const Profile> enabledProfiles;
  240. Span<const Profile> disabledProfiles;
  241. auto tie() const
  242. {
  243. return std::tuple (makeComparableRange (enabledProfiles), makeComparableRange (disabledProfiles));
  244. }
  245. bool operator== (const ProfileInquiryResponse& x) const { return tie() == x.tie(); }
  246. bool operator!= (const ProfileInquiryResponse& x) const { return ! operator== (x); }
  247. };
  248. /** See the MIDI-CI specification.
  249. @tags{Audio}
  250. */
  251. struct ProfileInquiry
  252. {
  253. auto tie() const
  254. {
  255. return std::tuple<>();
  256. }
  257. bool operator== (const ProfileInquiry& x) const { return tie() == x.tie(); }
  258. bool operator!= (const ProfileInquiry& x) const { return ! operator== (x); }
  259. };
  260. /** See the MIDI-CI specification.
  261. @tags{Audio}
  262. */
  263. struct ProfileAdded
  264. {
  265. Profile profile{};
  266. auto tie() const
  267. {
  268. return std::tuple (profile);
  269. }
  270. bool operator== (const ProfileAdded& x) const { return tie() == x.tie(); }
  271. bool operator!= (const ProfileAdded& x) const { return ! operator== (x); }
  272. };
  273. /** See the MIDI-CI specification.
  274. @tags{Audio}
  275. */
  276. struct ProfileRemoved
  277. {
  278. Profile profile{};
  279. auto tie() const
  280. {
  281. return std::tuple (profile);
  282. }
  283. bool operator== (const ProfileRemoved& x) const { return tie() == x.tie(); }
  284. bool operator!= (const ProfileRemoved& x) const { return ! operator== (x); }
  285. };
  286. /** See the MIDI-CI specification.
  287. @tags{Audio}
  288. */
  289. struct ProfileDetailsResponse
  290. {
  291. Profile profile{};
  292. std::byte target{};
  293. Span<const std::byte> data;
  294. auto tie() const
  295. {
  296. return std::tuple (profile, target, makeComparableRange (data));
  297. }
  298. bool operator== (const ProfileDetailsResponse& x) const { return tie() == x.tie(); }
  299. bool operator!= (const ProfileDetailsResponse& x) const { return ! operator== (x); }
  300. };
  301. /** See the MIDI-CI specification.
  302. @tags{Audio}
  303. */
  304. struct ProfileDetails
  305. {
  306. Profile profile{};
  307. std::byte target{};
  308. auto tie() const
  309. {
  310. return std::tuple (profile, target);
  311. }
  312. bool operator== (const ProfileDetails& x) const { return tie() == x.tie(); }
  313. bool operator!= (const ProfileDetails& x) const { return ! operator== (x); }
  314. };
  315. /** See the MIDI-CI specification.
  316. @tags{Audio}
  317. */
  318. struct ProfileOn
  319. {
  320. Profile profile{};
  321. uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  322. auto tie() const
  323. {
  324. return std::tuple (profile, numChannels);
  325. }
  326. bool operator== (const ProfileOn& x) const { return tie() == x.tie(); }
  327. bool operator!= (const ProfileOn& x) const { return ! operator== (x); }
  328. };
  329. /** See the MIDI-CI specification.
  330. @tags{Audio}
  331. */
  332. struct ProfileOff
  333. {
  334. Profile profile{};
  335. auto tie() const
  336. {
  337. return std::tuple (profile);
  338. }
  339. bool operator== (const ProfileOff& x) const { return tie() == x.tie(); }
  340. bool operator!= (const ProfileOff& x) const { return ! operator== (x); }
  341. };
  342. /** See the MIDI-CI specification.
  343. @tags{Audio}
  344. */
  345. struct ProfileEnabledReport
  346. {
  347. Profile profile{};
  348. uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  349. auto tie() const
  350. {
  351. return std::tuple (profile, numChannels);
  352. }
  353. bool operator== (const ProfileEnabledReport& x) const { return tie() == x.tie(); }
  354. bool operator!= (const ProfileEnabledReport& x) const { return ! operator== (x); }
  355. };
  356. /** See the MIDI-CI specification.
  357. @tags{Audio}
  358. */
  359. struct ProfileDisabledReport
  360. {
  361. Profile profile{};
  362. uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  363. auto tie() const
  364. {
  365. return std::tuple (profile, numChannels);
  366. }
  367. bool operator== (const ProfileDisabledReport& x) const { return tie() == x.tie(); }
  368. bool operator!= (const ProfileDisabledReport& x) const { return ! operator== (x); }
  369. };
  370. /** See the MIDI-CI specification.
  371. @tags{Audio}
  372. */
  373. struct ProfileSpecificData
  374. {
  375. Profile profile{};
  376. Span<const std::byte> data;
  377. auto tie() const
  378. {
  379. return std::tuple (profile, makeComparableRange (data));
  380. }
  381. bool operator== (const ProfileSpecificData& x) const { return tie() == x.tie(); }
  382. bool operator!= (const ProfileSpecificData& x) const { return ! operator== (x); }
  383. };
  384. /** See the MIDI-CI specification.
  385. @tags{Audio}
  386. */
  387. struct PropertyExchangeCapabilitiesResponse
  388. {
  389. std::byte numSimultaneousRequestsSupported{};
  390. std::byte majorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  391. std::byte minorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  392. auto tie() const
  393. {
  394. return std::tuple (numSimultaneousRequestsSupported, majorVersion, minorVersion);
  395. }
  396. bool operator== (const PropertyExchangeCapabilitiesResponse& x) const { return tie() == x.tie(); }
  397. bool operator!= (const PropertyExchangeCapabilitiesResponse& x) const { return ! operator== (x); }
  398. };
  399. /** See the MIDI-CI specification.
  400. @tags{Audio}
  401. */
  402. struct PropertyExchangeCapabilities
  403. {
  404. std::byte numSimultaneousRequestsSupported{};
  405. std::byte majorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  406. std::byte minorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
  407. auto tie() const
  408. {
  409. return std::tuple (numSimultaneousRequestsSupported, majorVersion, minorVersion);
  410. }
  411. bool operator== (const PropertyExchangeCapabilities& x) const { return tie() == x.tie(); }
  412. bool operator!= (const PropertyExchangeCapabilities& x) const { return ! operator== (x); }
  413. };
  414. /** A property-exchange message that has no payload, and must therefore
  415. be contained in a single chunk.
  416. @tags{Audio}
  417. */
  418. struct StaticSizePropertyExchange
  419. {
  420. std::byte requestID{};
  421. Span<const std::byte> header;
  422. auto tie() const
  423. {
  424. return std::tuple (requestID, makeComparableRange (header));
  425. }
  426. };
  427. /** A property-exchange message that may form part of a multi-chunk
  428. message sequence.
  429. @tags{Audio}
  430. */
  431. struct DynamicSizePropertyExchange
  432. {
  433. std::byte requestID{};
  434. Span<const std::byte> header;
  435. uint16_t totalNumChunks{};
  436. uint16_t thisChunkNum{};
  437. Span<const std::byte> data;
  438. auto tie() const
  439. {
  440. return std::tuple (requestID,
  441. makeComparableRange (header),
  442. totalNumChunks,
  443. thisChunkNum,
  444. makeComparableRange (data));
  445. }
  446. };
  447. /** See the MIDI-CI specification.
  448. @tags{Audio}
  449. */
  450. struct PropertyGetDataResponse : public DynamicSizePropertyExchange
  451. {
  452. bool operator== (const PropertyGetDataResponse& x) const { return tie() == x.tie(); }
  453. bool operator!= (const PropertyGetDataResponse& x) const { return ! operator== (x); }
  454. };
  455. /** See the MIDI-CI specification.
  456. @tags{Audio}
  457. */
  458. struct PropertyGetData : public StaticSizePropertyExchange
  459. {
  460. bool operator== (const PropertyGetData& x) const { return tie() == x.tie(); }
  461. bool operator!= (const PropertyGetData& x) const { return ! operator== (x); }
  462. };
  463. /** See the MIDI-CI specification.
  464. @tags{Audio}
  465. */
  466. struct PropertySetDataResponse : public StaticSizePropertyExchange
  467. {
  468. bool operator== (const PropertySetDataResponse& x) const { return tie() == x.tie(); }
  469. bool operator!= (const PropertySetDataResponse& x) const { return ! operator== (x); }
  470. };
  471. /** See the MIDI-CI specification.
  472. @tags{Audio}
  473. */
  474. struct PropertySetData : public DynamicSizePropertyExchange
  475. {
  476. bool operator== (const PropertySetData& x) const { return tie() == x.tie(); }
  477. bool operator!= (const PropertySetData& x) const { return ! operator== (x); }
  478. };
  479. /** See the MIDI-CI specification.
  480. @tags{Audio}
  481. */
  482. struct PropertySubscribeResponse : public DynamicSizePropertyExchange
  483. {
  484. bool operator== (const PropertySubscribeResponse& x) const { return tie() == x.tie(); }
  485. bool operator!= (const PropertySubscribeResponse& x) const { return ! operator== (x); }
  486. };
  487. /** See the MIDI-CI specification.
  488. @tags{Audio}
  489. */
  490. struct PropertySubscribe : public DynamicSizePropertyExchange
  491. {
  492. bool operator== (const PropertySubscribe& x) const { return tie() == x.tie(); }
  493. bool operator!= (const PropertySubscribe& x) const { return ! operator== (x); }
  494. };
  495. /** See the MIDI-CI specification.
  496. @tags{Audio}
  497. */
  498. struct PropertyNotify : public DynamicSizePropertyExchange
  499. {
  500. bool operator== (const PropertyNotify& x) const { return tie() == x.tie(); }
  501. bool operator!= (const PropertyNotify& x) const { return ! operator== (x); }
  502. };
  503. /** See the MIDI-CI specification.
  504. @tags{Audio}
  505. */
  506. struct ProcessInquiryResponse
  507. {
  508. std::byte supportedFeatures{};
  509. auto tie() const
  510. {
  511. return std::tuple (supportedFeatures);
  512. }
  513. bool operator== (const ProcessInquiryResponse& x) const { return tie() == x.tie(); }
  514. bool operator!= (const ProcessInquiryResponse& x) const { return ! operator== (x); }
  515. };
  516. /** See the MIDI-CI specification.
  517. @tags{Audio}
  518. */
  519. struct ProcessInquiry
  520. {
  521. auto tie() const
  522. {
  523. return std::tuple<>();
  524. }
  525. bool operator== (const ProcessInquiry& x) const { return tie() == x.tie(); }
  526. bool operator!= (const ProcessInquiry& x) const { return ! operator== (x); }
  527. };
  528. /** See the MIDI-CI specification.
  529. @tags{Audio}
  530. */
  531. struct ProcessMidiMessageReportResponse
  532. {
  533. std::byte messageDataControl{};
  534. std::byte requestedMessages{};
  535. std::byte channelControllerMessages{};
  536. std::byte noteDataMessages{};
  537. auto tie() const
  538. {
  539. return std::tuple (messageDataControl, requestedMessages, channelControllerMessages, noteDataMessages);
  540. }
  541. bool operator== (const ProcessMidiMessageReportResponse& x) const { return tie() == x.tie(); }
  542. bool operator!= (const ProcessMidiMessageReportResponse& x) const { return ! operator== (x); }
  543. };
  544. /** See the MIDI-CI specification.
  545. @tags{Audio}
  546. */
  547. struct ProcessMidiMessageReport
  548. {
  549. std::byte messageDataControl{};
  550. std::byte requestedMessages{};
  551. std::byte channelControllerMessages{};
  552. std::byte noteDataMessages{};
  553. auto tie() const
  554. {
  555. return std::tuple (messageDataControl, requestedMessages, channelControllerMessages, noteDataMessages);
  556. }
  557. bool operator== (const ProcessMidiMessageReport& x) const { return tie() == x.tie(); }
  558. bool operator!= (const ProcessMidiMessageReport& x) const { return ! operator== (x); }
  559. };
  560. /** See the MIDI-CI specification.
  561. @tags{Audio}
  562. */
  563. struct ProcessEndMidiMessageReport
  564. {
  565. auto tie() const
  566. {
  567. return std::tuple<>();
  568. }
  569. bool operator== (const ProcessEndMidiMessageReport& x) const { return tie() == x.tie(); }
  570. bool operator!= (const ProcessEndMidiMessageReport& x) const { return ! operator== (x); }
  571. };
  572. /**
  573. A message with a header and optional body.
  574. The body may be set to std::monostate to indicate some kind of failure, such as a malformed
  575. incoming message.
  576. @tags{Audio}
  577. */
  578. struct Parsed
  579. {
  580. using Body = std::variant<std::monostate,
  581. Discovery,
  582. DiscoveryResponse,
  583. InvalidateMUID,
  584. EndpointInquiry,
  585. EndpointInquiryResponse,
  586. ACK,
  587. NAK,
  588. ProfileInquiry,
  589. ProfileInquiryResponse,
  590. ProfileAdded,
  591. ProfileRemoved,
  592. ProfileDetails,
  593. ProfileDetailsResponse,
  594. ProfileOn,
  595. ProfileOff,
  596. ProfileEnabledReport,
  597. ProfileDisabledReport,
  598. ProfileSpecificData,
  599. PropertyExchangeCapabilities,
  600. PropertyExchangeCapabilitiesResponse,
  601. PropertyGetData,
  602. PropertyGetDataResponse,
  603. PropertySetData,
  604. PropertySetDataResponse,
  605. PropertySubscribe,
  606. PropertySubscribeResponse,
  607. PropertyNotify,
  608. ProcessInquiry,
  609. ProcessInquiryResponse,
  610. ProcessMidiMessageReport,
  611. ProcessMidiMessageReportResponse,
  612. ProcessEndMidiMessageReport>;
  613. Header header;
  614. Body body;
  615. bool operator== (const Parsed& other) const
  616. {
  617. const auto tie = [] (const auto& x) { return std::tie (x.header, x.body); };
  618. return tie (*this) == tie (other);
  619. }
  620. bool operator!= (const Parsed& other) const { return ! operator== (other); }
  621. };
  622. }
  623. } // namespace juce::midi_ci