Program Listing for File message.hpp¶
↰ Return to documentation for file (include/libremidi/message.hpp)
#pragma once
#if defined(MSC_VER)
# define NOMINMAX 1
# define WIN32_LEAN_AND_MEAN
#endif
#include <algorithm>
#include <cinttypes>
#include <memory>
#include <stdexcept>
#include <vector>
#if __has_include(<boost/container/small_vector.hpp>) && !defined(LIBREMIDI_NO_BOOST)
# include <boost/container/small_vector.hpp>
namespace libremidi
{
using midi_bytes = boost::container::small_vector<unsigned char, 4>;
}
#else
namespace libremidi
{
using midi_bytes = std::vector<unsigned char>;
}
#endif
#if defined(LIBREMIDI_HEADER_ONLY)
# define LIBREMIDI_INLINE inline
#else
# define LIBREMIDI_INLINE
#endif
namespace libremidi
{
enum class message_type : uint8_t
{
INVALID = 0x0,
// Standard Message
NOTE_OFF = 0x80,
NOTE_ON = 0x90,
POLY_PRESSURE = 0xA0,
CONTROL_CHANGE = 0xB0,
PROGRAM_CHANGE = 0xC0,
AFTERTOUCH = 0xD0,
PITCH_BEND = 0xE0,
// System Common Messages
SYSTEM_EXCLUSIVE = 0xF0,
TIME_CODE = 0xF1,
SONG_POS_POINTER = 0xF2,
SONG_SELECT = 0xF3,
RESERVED1 = 0xF4,
RESERVED2 = 0xF5,
TUNE_REQUEST = 0xF6,
EOX = 0xF7,
// System Realtime Messages
TIME_CLOCK = 0xF8,
RESERVED3 = 0xF9,
START = 0xFA,
CONTINUE = 0xFB,
STOP = 0xFC,
RESERVED4 = 0xFD,
ACTIVE_SENSING = 0xFE,
SYSTEM_RESET = 0xFF
};
enum class meta_event_type : uint8_t
{
SEQUENCE_NUMBER = 0x00,
TEXT = 0x01,
COPYRIGHT = 0x02,
TRACK_NAME = 0x03,
INSTRUMENT = 0x04,
LYRIC = 0x05,
MARKER = 0x06,
CUE = 0x07,
PATCH_NAME = 0x08,
DEVICE_NAME = 0x09,
CHANNEL_PREFIX = 0x20,
MIDI_PORT = 0x21,
END_OF_TRACK = 0x2F,
TEMPO_CHANGE = 0x51,
SMPTE_OFFSET = 0x54,
TIME_SIGNATURE = 0x58,
KEY_SIGNATURE = 0x59,
PROPRIETARY = 0x7F,
UNKNOWN = 0xFF
};
constexpr inline uint8_t clamp(uint8_t val, uint8_t min, uint8_t max)
{
return std::max(std::min(val, max), min);
}
struct message
{
midi_bytes bytes;
double timestamp{};
message() noexcept = default;
message(const midi_bytes& src_bytes, double src_timestamp)
: bytes(src_bytes), timestamp(src_timestamp)
{
}
template <typename... Args>
message(Args... args) noexcept : bytes{(uint8_t)args...}
{
}
static uint8_t make_command(const message_type type, const int channel) noexcept
{
return (uint8_t)((uint8_t)type | clamp(channel, 0, channel - 1));
}
static message note_on(uint8_t channel, uint8_t note, uint8_t velocity) noexcept
{
return {make_command(message_type::NOTE_ON, channel), note, velocity};
}
static message note_off(uint8_t channel, uint8_t note, uint8_t velocity) noexcept
{
return {make_command(message_type::NOTE_OFF, channel), note, velocity};
}
static message control_change(uint8_t channel, uint8_t control, uint8_t value) noexcept
{
return {make_command(message_type::CONTROL_CHANGE, channel), control, value};
}
static message program_change(uint8_t channel, uint8_t value) noexcept
{
return {make_command(message_type::PROGRAM_CHANGE, channel), value};
}
static message pitch_bend(uint8_t channel, int value) noexcept
{
return {
make_command(message_type::PITCH_BEND, channel), (unsigned char)(value & 0x7F),
(uint8_t)((value >> 7) & 0x7F)};
}
static message pitch_bend(uint8_t channel, uint8_t lsb, uint8_t msb) noexcept
{
return {make_command(message_type::PITCH_BEND, channel), lsb, msb};
}
static message poly_pressure(uint8_t channel, uint8_t note, uint8_t value) noexcept
{
return {make_command(message_type::POLY_PRESSURE, channel), note, value};
}
static message aftertouch(uint8_t channel, uint8_t value) noexcept
{
return {make_command(message_type::AFTERTOUCH, channel), value};
}
bool uses_channel(int channel) const
{
if (channel <= 0 || channel > 16)
throw std::range_error("message::uses_channel: out of range");
return ((bytes[0] & 0xF) == channel - 1) && ((bytes[0] & 0xF0) != 0xF0);
}
int get_channel() const
{
if ((bytes[0] & 0xF0) != 0xF0)
return (bytes[0] & 0xF) + 1;
return 0;
}
bool is_meta_event() const
{
return bytes[0] == 0xFF;
}
meta_event_type get_meta_event_type() const
{
if (!is_meta_event())
return meta_event_type::UNKNOWN;
return (meta_event_type)bytes[1];
}
message_type get_message_type() const
{
if (bytes[0] >= uint8_t(message_type::SYSTEM_EXCLUSIVE))
{
return (message_type)(bytes[0] & 0xFF);
}
else
{
return (message_type)(bytes[0] & 0xF0);
}
}
bool is_note_on_or_off() const
{
const auto status = get_message_type();
return (status == message_type::NOTE_ON) || (status == message_type::NOTE_OFF);
}
auto size() const
{
return bytes.size();
}
auto& front() const
{
return bytes.front();
}
auto& back() const
{
return bytes.back();
}
auto& operator[](int i) const
{
return bytes[i];
}
auto& front()
{
return bytes.front();
}
auto& back()
{
return bytes.back();
}
auto& operator[](int i)
{
return bytes[i];
}
template <typename... Args>
auto assign(Args&&... args)
{
return bytes.assign(std::forward<Args>(args)...);
}
template <typename... Args>
auto insert(Args&&... args)
{
return bytes.insert(std::forward<Args>(args)...);
}
auto clear()
{
bytes.clear();
}
auto begin() const
{
return bytes.begin();
}
auto end() const
{
return bytes.end();
}
auto begin()
{
return bytes.begin();
}
auto end()
{
return bytes.end();
}
auto cbegin() const
{
return bytes.cbegin();
}
auto cend() const
{
return bytes.cend();
}
auto cbegin()
{
return bytes.cbegin();
}
auto cend()
{
return bytes.cend();
}
auto rbegin() const
{
return bytes.rbegin();
}
auto rend() const
{
return bytes.rend();
}
auto rbegin()
{
return bytes.rbegin();
}
auto rend()
{
return bytes.rend();
}
};
struct meta_events
{
static message end_of_track()
{
return {0xFF, 0x2F, 0};
}
static message channel(int channel)
{
return {0xff, 0x20, 0x01, clamp(0, 0xff, channel - 1)};
}
static message tempo(int mpqn)
{
return {0xff, 81, 3, (uint8_t)(mpqn >> 16), (uint8_t)(mpqn >> 8), (uint8_t)mpqn};
}
static message time_signature(int numerator, int denominator)
{
int n = 1;
int powTwo = 0;
while (n < denominator)
{
n <<= 1;
++powTwo;
}
return {0xff, 0x58, 0x04, (uint8_t)numerator, (uint8_t)powTwo, 1, 96};
}
// Where key index goes from -7 (7 flats, C♭ Major) to +7 (7 sharps, C♯
// Major)
static message key_signature(int keyIndex, bool isMinor)
{
if (keyIndex < -7 || keyIndex > 7)
throw std::range_error("meta_events::key_signature: out of range");
return {0xff, 0x59, 0x02, (uint8_t)keyIndex, isMinor ? (uint8_t)1 : (uint8_t)0};
}
static message song_position(int positionInBeats) noexcept
{
return {0xf2, (uint8_t)(positionInBeats & 127), (uint8_t)((positionInBeats >> 7) & 127)};
}
};
struct track_event
{
int tick = 0;
int track = 0;
message m;
};
typedef std::vector<track_event> midi_track;
}