360 lines
12 KiB
C++
360 lines
12 KiB
C++
#pragma once
|
|
|
|
#include <Def/Cable.hpp>
|
|
#include <Def/Channel.hpp>
|
|
#include <Settings/NamespaceSettings.hpp>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
BEGIN_CS_NAMESPACE
|
|
|
|
/// A struct for saving a MIDI address consisting of a 7-bit address, a 4-bit
|
|
/// channel, and a 4-bit cable number.
|
|
/// A MIDI address can be marked "invalid". The MIDI sending functions
|
|
/// (@ref MIDI_Sender) will never send messages addressed to an invalid address.
|
|
struct __attribute__((packed)) RawMIDIAddress {
|
|
bool valid : 1;
|
|
uint8_t address : 7;
|
|
uint8_t channel : 4;
|
|
uint8_t cableNumber : 4;
|
|
};
|
|
|
|
/// A class for saving a MIDI channel and cable number.
|
|
/// A MIDI channel and cable number can be marked "invalid".
|
|
/// The MIDI sending functions (@ref MIDI_Sender) will never send messages
|
|
/// addressed to invalid channels or cables.
|
|
class MIDIChannelCable {
|
|
friend class MIDIAddress;
|
|
|
|
public:
|
|
/// @name Constructors
|
|
/// @{
|
|
|
|
constexpr MIDIChannelCable() : addresses {0, 0, 0, 0} {}
|
|
constexpr MIDIChannelCable(Channel channel, Cable cableNumber = Cable_1)
|
|
: addresses {1, 0, channel.getRaw(), cableNumber.getRaw()} {}
|
|
|
|
constexpr static MIDIChannelCable invalid() { return {}; }
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Member access
|
|
/// @{
|
|
|
|
/// Get the channel [1, 16].
|
|
constexpr Channel getChannel() const { return Channel {addresses.channel}; }
|
|
/// Get the channel as an integer [0, 15].
|
|
constexpr uint8_t getRawChannel() const { return addresses.channel; }
|
|
|
|
/// Get the cable number [Cable_1, Cable_16].
|
|
constexpr Cable getCableNumber() const {
|
|
return Cable(addresses.cableNumber);
|
|
}
|
|
/// Get the cable number as an integer [0, 15].
|
|
constexpr uint8_t getRawCableNumber() const {
|
|
return addresses.cableNumber;
|
|
}
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Checks
|
|
/// @{
|
|
|
|
/// Check for equality: two addresses are equal if and only if they are both
|
|
/// valid addresses and the MIDI channel and MIDI USB cable number are
|
|
/// equal.
|
|
/// @note Invalid addresses are not equal nor inequal.
|
|
constexpr bool operator==(MIDIChannelCable rhs) const {
|
|
return this->addresses.valid && rhs.addresses.valid &&
|
|
this->addresses.channel == rhs.addresses.channel &&
|
|
this->addresses.cableNumber == rhs.addresses.cableNumber;
|
|
}
|
|
|
|
/// Check for inequality: two addresses are not equal if and only if they
|
|
/// are both valid addresses and have a MIDI channel or MIDI USB cable
|
|
/// number that differs.
|
|
/// @note Invalid addresses are not equal nor inequal.
|
|
constexpr bool operator!=(MIDIChannelCable rhs) const {
|
|
return this->addresses.valid && rhs.addresses.valid &&
|
|
!(this->addresses.channel == rhs.addresses.channel &&
|
|
this->addresses.cableNumber == rhs.addresses.cableNumber);
|
|
}
|
|
|
|
/// Check if the MIDI address is valid.
|
|
constexpr bool isValid() const { return addresses.valid; }
|
|
|
|
/// Check if the MIDI address is valid.
|
|
/// @see isValid
|
|
constexpr explicit operator bool() const { return isValid(); }
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Base functions for address pattern matching.
|
|
/// @{
|
|
|
|
/// Check if two addresses match (are equal).
|
|
static bool matchSingle(MIDIChannelCable toMatch, MIDIChannelCable base) {
|
|
return base == toMatch;
|
|
}
|
|
|
|
/// @}
|
|
|
|
protected:
|
|
constexpr MIDIChannelCable(RawMIDIAddress addresses)
|
|
: addresses(addresses) {}
|
|
|
|
private:
|
|
RawMIDIAddress addresses;
|
|
};
|
|
|
|
/// A class for saving an offset to a MIDI address.
|
|
/// It can be added to a MIDIAddress.
|
|
class RelativeMIDIAddress {
|
|
friend class MIDIAddress;
|
|
|
|
public:
|
|
constexpr RelativeMIDIAddress() : addresses {0, 0, 0, 0} {}
|
|
constexpr RelativeMIDIAddress(int deltaAddress, int deltaChannel = 0,
|
|
int deltaCableNumber = 0)
|
|
: addresses {
|
|
1,
|
|
static_cast<uint8_t>(deltaAddress),
|
|
static_cast<uint8_t>(deltaChannel),
|
|
static_cast<uint8_t>(deltaCableNumber),
|
|
} {}
|
|
constexpr bool isValid() const { return addresses.valid; }
|
|
|
|
/// Compound addition. Element-wise addition, result is valid if both
|
|
/// operands were valid.
|
|
RelativeMIDIAddress &operator+=(RelativeMIDIAddress that);
|
|
|
|
private:
|
|
RawMIDIAddress addresses;
|
|
static_assert(((-1) & 0x7F) == 0x7F,
|
|
"Negative numbers must be two's complement");
|
|
};
|
|
|
|
/// A type-safe utility class for saving a MIDI address consisting of a 7-bit
|
|
/// address, a 4-bit channel, and a 4-bit cable number.
|
|
/// A MIDI address can be marked "invalid". The MIDI sending functions
|
|
/// (@ref MIDI_Sender) will never send messages addressed to invalid addresses.
|
|
///
|
|
/// See @ref midi_md-midi-addresses "MIDI Tutorial: MIDI addresses" for a
|
|
/// tutorial on how to use MIDI addresses.
|
|
class MIDIAddress {
|
|
public:
|
|
/// @name Constructors
|
|
/// @{
|
|
|
|
/// Default constructor, creates an invalid address.
|
|
constexpr MIDIAddress()
|
|
: addresses {
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
} {}
|
|
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param address
|
|
* The 7-bit MIDI address. Depending on the message type, this can
|
|
* be the MIDI note number, the number of the MIDI Control Change
|
|
* Controller, etc.
|
|
* Must be a number in the range [0, 127].
|
|
* @param channelCN
|
|
* The MIDI Channel and the MIDI USB cable number.
|
|
*/
|
|
constexpr MIDIAddress(int address, MIDIChannelCable channelCN)
|
|
: addresses {
|
|
1,
|
|
static_cast<uint8_t>(address),
|
|
channelCN.getRawChannel(),
|
|
channelCN.getRawCableNumber(),
|
|
} {} // Deliberate overflow for negative numbers
|
|
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param address
|
|
* The 7-bit MIDI address.
|
|
* Depending on the message type, this can be the MIDI note number,
|
|
* the number of the MIDI Control Change Controller, etc.
|
|
* Must be a number in the range [0, 127].
|
|
* @param channel
|
|
* The MIDI Channel.
|
|
* Use the constants @ref Channel_1 through @ref Channel_16.
|
|
* @param cableNumber
|
|
* The MIDI USB cable number.
|
|
* Use the constants @ref Cable_1 through @ref Cable_16.
|
|
*/
|
|
constexpr MIDIAddress(int address, Channel channel = Channel_1,
|
|
Cable cableNumber = Cable_1)
|
|
: addresses {
|
|
1,
|
|
static_cast<uint8_t>(address),
|
|
channel.getRaw(),
|
|
cableNumber.getRaw(),
|
|
} {} // Deliberate overflow for negative numbers
|
|
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param address
|
|
* The 7-bit MIDI address.
|
|
* Depending on the message type, this can be the MIDI note number,
|
|
* the number of the MIDI Control Change Controller, etc.
|
|
* Must be a number in the range [0, 127].
|
|
* @param cableNumber
|
|
* The MIDI USB cable number.
|
|
* Use the constants @ref Cable_1 through @ref Cable_16.
|
|
*/
|
|
constexpr MIDIAddress(int address, Cable cableNumber)
|
|
: addresses {
|
|
1,
|
|
static_cast<uint8_t>(address),
|
|
0,
|
|
cableNumber.getRaw(),
|
|
} {} // Deliberate overflow for negative numbers
|
|
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param channel
|
|
* The MIDI Channel.
|
|
* Use the constants @ref Channel_1 through @ref Channel_16.
|
|
* @param cableNumber
|
|
* The MIDI USB cable number.
|
|
* Use the constants @ref Cable_1 through @ref Cable_16.
|
|
*/
|
|
constexpr MIDIAddress(Channel channel, Cable cableNumber = Cable_1)
|
|
: addresses {
|
|
1,
|
|
0,
|
|
channel.getRaw(),
|
|
cableNumber.getRaw(),
|
|
} {}
|
|
|
|
/**
|
|
* @brief Constructor.
|
|
*
|
|
* @param address
|
|
* The MIDI Channel and the MIDI USB cable number.
|
|
*/
|
|
constexpr MIDIAddress(MIDIChannelCable address)
|
|
: addresses(address.addresses) {}
|
|
|
|
/// Return an invalid address.
|
|
constexpr static MIDIAddress invalid() { return {}; }
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Adding/subtracting offsets
|
|
/// @{
|
|
|
|
/// Add a relative offset to this address.
|
|
MIDIAddress &operator+=(RelativeMIDIAddress rhs);
|
|
|
|
/// Subtract a relative offset from this address.
|
|
MIDIAddress &operator-=(RelativeMIDIAddress rhs);
|
|
|
|
/// Add a relative offset.
|
|
MIDIAddress operator+(RelativeMIDIAddress rhs) const;
|
|
|
|
/// Subtract a relative offset.
|
|
MIDIAddress operator-(RelativeMIDIAddress rhs) const;
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Member access
|
|
/// @{
|
|
|
|
/// Get the address [0, 127].
|
|
constexpr uint8_t getAddress() const { return addresses.address; }
|
|
|
|
/// Get the channel [Channel_1, Channel_16]
|
|
constexpr Channel getChannel() const { return Channel {addresses.channel}; }
|
|
/// Get the channel as an integer [0, 15]
|
|
constexpr uint8_t getRawChannel() const { return addresses.channel; }
|
|
|
|
/// Get the cable number [Cable_1, Cable_16].
|
|
constexpr Cable getCableNumber() const {
|
|
return Cable(addresses.cableNumber);
|
|
}
|
|
/// Get the cable number as an integer [0, 15].
|
|
constexpr uint8_t getRawCableNumber() const {
|
|
return addresses.cableNumber;
|
|
}
|
|
/// Get the channel and cable number.
|
|
constexpr MIDIChannelCable getChannelCable() const { return {addresses}; }
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Checks
|
|
/// @{
|
|
|
|
/// Check for equality: two addresses are equal if and only if they are both
|
|
/// valid addresses and the MIDI address, MIDI channel and MIDI USB cable
|
|
/// number are equal.
|
|
/// @note Invalid addresses are not equal nor inequal.
|
|
constexpr bool operator==(MIDIAddress rhs) const {
|
|
return this->addresses.valid && rhs.addresses.valid &&
|
|
this->addresses.address == rhs.addresses.address &&
|
|
this->addresses.channel == rhs.addresses.channel &&
|
|
this->addresses.cableNumber == rhs.addresses.cableNumber;
|
|
}
|
|
|
|
/// Check for inequality: two addresses are not equal if and only if they
|
|
/// are both valid addresses and have a MIDI address, MIDI channel or MIDI
|
|
/// USB cable number that differs.
|
|
/// @note Invalid addresses are not equal nor inequal.
|
|
constexpr bool operator!=(MIDIAddress rhs) const {
|
|
return this->addresses.valid && rhs.addresses.valid &&
|
|
!(this->addresses.address == rhs.addresses.address &&
|
|
this->addresses.channel == rhs.addresses.channel &&
|
|
this->addresses.cableNumber == rhs.addresses.cableNumber);
|
|
}
|
|
|
|
/// Check if the MIDI address is valid.
|
|
constexpr bool isValid() const { return addresses.valid; }
|
|
|
|
/// Check if the MIDI address is valid.
|
|
/// @see isValid
|
|
constexpr explicit operator bool() const { return isValid(); }
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// @name Base functions for address pattern matching.
|
|
/// @{
|
|
|
|
/// Check if two addresses match (are equal).
|
|
static bool matchSingle(MIDIAddress toMatch, MIDIAddress base) {
|
|
return base == toMatch;
|
|
}
|
|
|
|
/// Check if an address falls within a range of MIDI addresses, starting
|
|
/// with address `base`, with a given length.
|
|
/// @retval true
|
|
/// `toMatch` and `base` are both valid addresses,
|
|
/// the MIDI address is within the given range, and the MIDI Channel
|
|
/// and MIDI USB Cable Number of `toMatch` and `base` are the same.
|
|
/// @retval false
|
|
/// Otherwise.
|
|
static bool matchAddressInRange(MIDIAddress toMatch, MIDIAddress base,
|
|
uint8_t length);
|
|
|
|
/// @}
|
|
|
|
private:
|
|
RawMIDIAddress addresses;
|
|
};
|
|
|
|
END_CS_NAMESPACE |