1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/modules/juce_midi_ci/ci/juce_CIMessages.h

777 lines
24 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce::midi_ci
{
//==============================================================================
/**
Byte values representing different addresses within a group.
@tags{Audio}
*/
enum class ChannelInGroup : uint8_t
{
channel0 = 0x0,
channel1 = 0x1,
channel2 = 0x2,
channel3 = 0x3,
channel4 = 0x4,
channel5 = 0x5,
channel6 = 0x6,
channel7 = 0x7,
channel8 = 0x8,
channel9 = 0x9,
channelA = 0xA,
channelB = 0xB,
channelC = 0xC,
channelD = 0xD,
channelE = 0xE,
channelF = 0xF,
wholeGroup = 0x7e, ///< Refers to all channels in the UMP group
wholeBlock = 0x7f, ///< Refers to all channels in the function block that contains the UMP group
};
/**
Utility functions for working with the ChannelInGroup enum.
@tags{Audio}
*/
struct ChannelInGroupUtils
{
ChannelInGroupUtils() = delete;
/** Converts a ChannelInGroup to a descriptive string. */
static String toString (ChannelInGroup c)
{
if (c == ChannelInGroup::wholeGroup)
return "Group";
if (c == ChannelInGroup::wholeBlock)
return "Function Block";
const auto underlying = (std::underlying_type_t<ChannelInGroup>) c;
return "Channel " + String (underlying + 1);
}
};
using Profile = std::array<std::byte, 5>;
//==============================================================================
/**
Namespace containing structs representing different kinds of MIDI-CI message.
@tags{Audio}
*/
namespace Message
{
/** Wraps a span, providing equality operators that compare the span
contents elementwise.
@tags{Audio}
*/
template <typename T>
struct ComparableRange
{
T& data;
bool operator== (const ComparableRange& other) const
{
return std::equal (data.begin(), data.end(), other.data.begin(), other.data.end());
}
bool operator!= (const ComparableRange& other) const { return ! operator== (other); }
};
template <typename T> static constexpr auto makeComparableRange ( T& t) { return ComparableRange< T> { t }; }
template <typename T> static constexpr auto makeComparableRange (const T& t) { return ComparableRange<const T> { t }; }
//==============================================================================
/**
Holds fields that can be found at the beginning of every MIDI CI message.
@tags{Audio}
*/
struct Header
{
ChannelInGroup deviceID{};
std::byte category{};
std::byte version{};
MUID source = MUID::makeUnchecked (0);
MUID destination = MUID::makeUnchecked (0);
auto tie() const
{
return std::tuple (deviceID, category, version, source, destination);
}
bool operator== (const Header& x) const { return tie() == x.tie(); }
bool operator!= (const Header& x) const { return ! operator== (x); }
};
/**
Groups together a CI message header, and some number of trailing bytes.
@tags{Audio}
*/
struct Generic
{
Header header;
Span<const std::byte> data;
};
//==============================================================================
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct DiscoveryResponse
{
ump::DeviceInfo device;
std::byte capabilities{};
uint32_t maximumSysexSize{};
std::byte outputPathID{}; /**< Only valid if the message header specifies version 0x02 or greater. */
std::byte functionBlock{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (device, capabilities, maximumSysexSize, outputPathID, functionBlock);
}
bool operator== (const DiscoveryResponse& x) const { return tie() == x.tie(); }
bool operator!= (const DiscoveryResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct Discovery
{
ump::DeviceInfo device;
std::byte capabilities{};
uint32_t maximumSysexSize{};
std::byte outputPathID{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (device, capabilities, maximumSysexSize, outputPathID);
}
bool operator== (const Discovery& x) const { return tie() == x.tie(); }
bool operator!= (const Discovery& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct EndpointInquiryResponse
{
std::byte status;
Span<const std::byte> data;
auto tie() const
{
return std::tuple (status, makeComparableRange (data));
}
bool operator== (const EndpointInquiryResponse& x) const { return tie() == x.tie(); }
bool operator!= (const EndpointInquiryResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct EndpointInquiry
{
std::byte status;
auto tie() const
{
return std::tuple (status);
}
bool operator== (const EndpointInquiry& x) const { return tie() == x.tie(); }
bool operator!= (const EndpointInquiry& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct InvalidateMUID
{
MUID target = MUID::makeUnchecked (0);
auto tie() const
{
return std::tuple (target);
}
bool operator== (const InvalidateMUID& x) const { return tie() == x.tie(); }
bool operator!= (const InvalidateMUID& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ACK
{
std::byte originalCategory{};
std::byte statusCode{};
std::byte statusData{};
std::array<std::byte, 5> details{};
Span<const std::byte> messageText{};
/** Convenience function that returns the message's text as a String. */
String getMessageTextAsString() const
{
return Encodings::stringFrom7BitText (messageText);
}
auto tie() const
{
return std::tuple (originalCategory, statusCode, statusData, details, makeComparableRange (messageText));
}
bool operator== (const ACK& x) const { return tie() == x.tie(); }
bool operator!= (const ACK& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct NAK
{
std::byte originalCategory{}; /**< Only valid if the message header specifies version 0x02 or greater. */
std::byte statusCode{}; /**< Only valid if the message header specifies version 0x02 or greater. */
std::byte statusData{}; /**< Only valid if the message header specifies version 0x02 or greater. */
std::array<std::byte, 5> details{}; /**< Only valid if the message header specifies version 0x02 or greater. */
Span<const std::byte> messageText{}; /**< Only valid if the message header specifies version 0x02 or greater. */
/** Convenience function that returns the message's text as a String. */
String getMessageTextAsString() const
{
return Encodings::stringFrom7BitText (messageText);
}
auto tie() const
{
return std::tuple (originalCategory, statusCode, statusData, details, makeComparableRange (messageText));
}
bool operator== (const NAK& x) const { return tie() == x.tie(); }
bool operator!= (const NAK& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileInquiryResponse
{
Span<const Profile> enabledProfiles;
Span<const Profile> disabledProfiles;
auto tie() const
{
return std::tuple (makeComparableRange (enabledProfiles), makeComparableRange (disabledProfiles));
}
bool operator== (const ProfileInquiryResponse& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileInquiryResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileInquiry
{
auto tie() const
{
return std::tuple<>();
}
bool operator== (const ProfileInquiry& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileInquiry& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileAdded
{
Profile profile{};
auto tie() const
{
return std::tuple (profile);
}
bool operator== (const ProfileAdded& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileAdded& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileRemoved
{
Profile profile{};
auto tie() const
{
return std::tuple (profile);
}
bool operator== (const ProfileRemoved& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileRemoved& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileDetailsResponse
{
Profile profile{};
std::byte target{};
Span<const std::byte> data;
auto tie() const
{
return std::tuple (profile, target, makeComparableRange (data));
}
bool operator== (const ProfileDetailsResponse& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileDetailsResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileDetails
{
Profile profile{};
std::byte target{};
auto tie() const
{
return std::tuple (profile, target);
}
bool operator== (const ProfileDetails& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileDetails& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileOn
{
Profile profile{};
uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (profile, numChannels);
}
bool operator== (const ProfileOn& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileOn& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileOff
{
Profile profile{};
auto tie() const
{
return std::tuple (profile);
}
bool operator== (const ProfileOff& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileOff& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileEnabledReport
{
Profile profile{};
uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (profile, numChannels);
}
bool operator== (const ProfileEnabledReport& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileEnabledReport& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileDisabledReport
{
Profile profile{};
uint16_t numChannels{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (profile, numChannels);
}
bool operator== (const ProfileDisabledReport& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileDisabledReport& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProfileSpecificData
{
Profile profile{};
Span<const std::byte> data;
auto tie() const
{
return std::tuple (profile, makeComparableRange (data));
}
bool operator== (const ProfileSpecificData& x) const { return tie() == x.tie(); }
bool operator!= (const ProfileSpecificData& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertyExchangeCapabilitiesResponse
{
std::byte numSimultaneousRequestsSupported{};
std::byte majorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
std::byte minorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (numSimultaneousRequestsSupported, majorVersion, minorVersion);
}
bool operator== (const PropertyExchangeCapabilitiesResponse& x) const { return tie() == x.tie(); }
bool operator!= (const PropertyExchangeCapabilitiesResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertyExchangeCapabilities
{
std::byte numSimultaneousRequestsSupported{};
std::byte majorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
std::byte minorVersion{}; /**< Only valid if the message header specifies version 0x02 or greater. */
auto tie() const
{
return std::tuple (numSimultaneousRequestsSupported, majorVersion, minorVersion);
}
bool operator== (const PropertyExchangeCapabilities& x) const { return tie() == x.tie(); }
bool operator!= (const PropertyExchangeCapabilities& x) const { return ! operator== (x); }
};
/** A property-exchange message that has no payload, and must therefore
be contained in a single chunk.
@tags{Audio}
*/
struct StaticSizePropertyExchange
{
std::byte requestID{};
Span<const std::byte> header;
auto tie() const
{
return std::tuple (requestID, makeComparableRange (header));
}
};
/** A property-exchange message that may form part of a multi-chunk
message sequence.
@tags{Audio}
*/
struct DynamicSizePropertyExchange
{
std::byte requestID{};
Span<const std::byte> header;
uint16_t totalNumChunks{};
uint16_t thisChunkNum{};
Span<const std::byte> data;
auto tie() const
{
return std::tuple (requestID,
makeComparableRange (header),
totalNumChunks,
thisChunkNum,
makeComparableRange (data));
}
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertyGetDataResponse : public DynamicSizePropertyExchange
{
bool operator== (const PropertyGetDataResponse& x) const { return tie() == x.tie(); }
bool operator!= (const PropertyGetDataResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertyGetData : public StaticSizePropertyExchange
{
bool operator== (const PropertyGetData& x) const { return tie() == x.tie(); }
bool operator!= (const PropertyGetData& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertySetDataResponse : public StaticSizePropertyExchange
{
bool operator== (const PropertySetDataResponse& x) const { return tie() == x.tie(); }
bool operator!= (const PropertySetDataResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertySetData : public DynamicSizePropertyExchange
{
bool operator== (const PropertySetData& x) const { return tie() == x.tie(); }
bool operator!= (const PropertySetData& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertySubscribeResponse : public DynamicSizePropertyExchange
{
bool operator== (const PropertySubscribeResponse& x) const { return tie() == x.tie(); }
bool operator!= (const PropertySubscribeResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertySubscribe : public DynamicSizePropertyExchange
{
bool operator== (const PropertySubscribe& x) const { return tie() == x.tie(); }
bool operator!= (const PropertySubscribe& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct PropertyNotify : public DynamicSizePropertyExchange
{
bool operator== (const PropertyNotify& x) const { return tie() == x.tie(); }
bool operator!= (const PropertyNotify& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProcessInquiryResponse
{
std::byte supportedFeatures{};
auto tie() const
{
return std::tuple (supportedFeatures);
}
bool operator== (const ProcessInquiryResponse& x) const { return tie() == x.tie(); }
bool operator!= (const ProcessInquiryResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProcessInquiry
{
auto tie() const
{
return std::tuple<>();
}
bool operator== (const ProcessInquiry& x) const { return tie() == x.tie(); }
bool operator!= (const ProcessInquiry& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProcessMidiMessageReportResponse
{
std::byte messageDataControl{};
std::byte requestedMessages{};
std::byte channelControllerMessages{};
std::byte noteDataMessages{};
auto tie() const
{
return std::tuple (messageDataControl, requestedMessages, channelControllerMessages, noteDataMessages);
}
bool operator== (const ProcessMidiMessageReportResponse& x) const { return tie() == x.tie(); }
bool operator!= (const ProcessMidiMessageReportResponse& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProcessMidiMessageReport
{
std::byte messageDataControl{};
std::byte requestedMessages{};
std::byte channelControllerMessages{};
std::byte noteDataMessages{};
auto tie() const
{
return std::tuple (messageDataControl, requestedMessages, channelControllerMessages, noteDataMessages);
}
bool operator== (const ProcessMidiMessageReport& x) const { return tie() == x.tie(); }
bool operator!= (const ProcessMidiMessageReport& x) const { return ! operator== (x); }
};
/** See the MIDI-CI specification.
@tags{Audio}
*/
struct ProcessEndMidiMessageReport
{
auto tie() const
{
return std::tuple<>();
}
bool operator== (const ProcessEndMidiMessageReport& x) const { return tie() == x.tie(); }
bool operator!= (const ProcessEndMidiMessageReport& x) const { return ! operator== (x); }
};
/**
A message with a header and optional body.
The body may be set to std::monostate to indicate some kind of failure, such as a malformed
incoming message.
@tags{Audio}
*/
struct Parsed
{
using Body = std::variant<std::monostate,
Discovery,
DiscoveryResponse,
InvalidateMUID,
EndpointInquiry,
EndpointInquiryResponse,
ACK,
NAK,
ProfileInquiry,
ProfileInquiryResponse,
ProfileAdded,
ProfileRemoved,
ProfileDetails,
ProfileDetailsResponse,
ProfileOn,
ProfileOff,
ProfileEnabledReport,
ProfileDisabledReport,
ProfileSpecificData,
PropertyExchangeCapabilities,
PropertyExchangeCapabilitiesResponse,
PropertyGetData,
PropertyGetDataResponse,
PropertySetData,
PropertySetDataResponse,
PropertySubscribe,
PropertySubscribeResponse,
PropertyNotify,
ProcessInquiry,
ProcessInquiryResponse,
ProcessMidiMessageReport,
ProcessMidiMessageReportResponse,
ProcessEndMidiMessageReport>;
Header header;
Body body;
bool operator== (const Parsed& other) const
{
const auto tie = [] (const auto& x) { return std::tie (x.header, x.body); };
return tie (*this) == tie (other);
}
bool operator!= (const Parsed& other) const { return ! operator== (other); }
};
}
} // namespace juce::midi_ci