//----------------------------------------------------------------------------- // Project : VST SDK // Flags : clang-format SMTGSequencer // Category : Helpers // Filename : public.sdk/source/vst/utility/ump.h // Created by : Steinberg, 12/2023 // Description : a single c++17 header UMP parser implementation without other dependencies than // to std::array // //----------------------------------------------------------------------------- // This file is part of a Steinberg SDK. It is subject to the license terms // in the LICENSE file found in the top-level directory of this distribution // and at www.steinberg.net/sdklicenses. // No part of the SDK, including this file, may be copied, modified, propagated, // or distributed except according to the terms contained in the LICENSE file. //----------------------------------------------------------------------------- #pragma once #ifndef SMTG_ALWAYS_INLINE #if _MSVC #define SMTG_ALWAYS_INLINE __forceinline #else #define SMTG_ALWAYS_INLINE __inline__ __attribute__ ((__always_inline__)) #endif #endif #include #include #include //------------------------------------------------------------------------ namespace Steinberg::Vst::UMP { struct IUniversalMidiPacketHandler; enum class ParsingAction : uint32_t; //------------------------------------------------------------------------ /** the parse sections control which messages to process of UMP * * the generated code is smaller and runtime is faster if only the needed sections are enabled. */ enum ParseSections : uint8_t { Utility = 1 << 0, System = 1 << 1, ChannelVoice1 = 1 << 2, SysEx = 1 << 3, ChannelVoice2 = 1 << 3, Data128 = 1 << 4, All = 0xff }; //------------------------------------------------------------------------ /** stateless parsing universal MIDI packets * * @tparam sections which sections to parse * @param numWords number of 32-bit words * @param words pointer to a words array * @param handler callback handler * @return number of successfully processed words */ template size_t parsePackets (const size_t numWords, const uint32_t* words, const IUniversalMidiPacketHandler& handler); //------------------------------------------------------------------------ struct IUniversalMidiPacketHandler { using Group = uint8_t; using Channel = uint8_t; using Index = uint8_t; using NoteNumber = uint8_t; using BankNumber = uint8_t; using ControllerNumber = uint8_t; using Velocity8 = uint8_t; using Velocity16 = uint16_t; using AttributeType = uint8_t; using AttributeValue = uint16_t; using OptionFlags = uint8_t; using Data8 = uint8_t; using Data32 = uint32_t; using Program = uint8_t; using BankMSB = uint8_t; using BankLSB = uint8_t; using Timestamp = uint16_t; using Timecode = uint8_t; using StreamID = uint8_t; using SysEx6ByteData = const std::array&; using SysEx13ByteData = const std::array&; using MixedData = const std::array&; // UTILITY virtual void onNoop (Group group) const = 0; virtual void onJitterClock (Group group, Timestamp time) const = 0; virtual void onJitterTimestamp (Group group, Timestamp time) const = 0; // SYSTEM COMMON virtual void onMIDITimeCode (Group group, Timecode timecode) const = 0; virtual void onSongPositionPointer (Group group, uint8_t posLSB, uint8_t posMSB) const = 0; virtual void onSongSelect (Group group, uint8_t songIndex) const = 0; virtual void onTuneRequest (Group group) const = 0; // SYSTEM REALTIME enum class SystemRealtime { TimingClock, Start, Continue, Stop, ActiveSensing, Reset }; virtual void onSystemRealtime (Group group, SystemRealtime which) const = 0; // MIDI 1.0 CHANNEL VOICE Messages virtual void onMidi1NoteOff (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const = 0; virtual void onMidi1NoteOn (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const = 0; virtual void onMidi1PolyPressure (Group group, Channel channel, NoteNumber note, Data8 data) const = 0; virtual void onMidi1ControlChange (Group group, Channel channel, ControllerNumber controller, Data8 value) const = 0; virtual void onMidi1ProgramChange (Group group, Channel channel, Program program) const = 0; virtual void onMidi1ChannelPressure (Group group, Channel channel, Data8 pressure) const = 0; virtual void onMidi1PitchBend (Group group, Channel channel, Data8 valueLSB, Data8 valueMSB) const = 0; // DATA 64 BIT virtual void onSysExPacket (Group group, SysEx6ByteData data) const = 0; virtual void onSysExStart (Group group, SysEx6ByteData data) const = 0; virtual void onSysExContinue (Group group, SysEx6ByteData data) const = 0; virtual void onSysExEnd (Group group, SysEx6ByteData data) const = 0; // MIDI 2.0 CHANNEL VOICE Messages virtual void onRegisteredPerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const = 0; virtual void onAssignablePerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const = 0; virtual void onRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onRelativeRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onRelativeAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const = 0; virtual void onPerNotePitchBend (Group group, Channel channel, NoteNumber note, Data32 data) const = 0; virtual void onNoteOff (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const = 0; virtual void onNoteOn (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const = 0; virtual void onPolyPressure (Group group, Channel channel, NoteNumber note, Data32 data) const = 0; virtual void onControlChange (Group group, Channel channel, ControllerNumber controller, Data32 data) const = 0; virtual void onProgramChange (Group group, Channel channel, OptionFlags options, Program program, BankMSB bankMSB, BankLSB bankLSB) const = 0; virtual void onChannelPressure (Group group, Channel channel, Data32 data) const = 0; virtual void onPitchBend (Group group, Channel channel, Data32 data) const = 0; virtual void onPerNoteManagement (Group group, Channel channel, NoteNumber note, OptionFlags options) const = 0; // DATA 128 BIT virtual void onSysEx8Packet (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onSysEx8Start (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onSysEx8Continue (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onSysEx8End (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const = 0; virtual void onMixedDataSetHeader (Group group, Index mdsID, MixedData data) const = 0; virtual void onMixedDataSetPayload (Group group, Index mdsID, MixedData data) const = 0; // invalid input data virtual ParsingAction onInvalidInputData (size_t index) const = 0; // insufficient input data virtual void onInsufficentInputData (size_t index, size_t numMissingWords) const = 0; }; //------------------------------------------------------------------------ enum class ParsingAction : uint32_t { Break, Continue }; namespace Detail { //------------------------------------------------------------------------ enum class MessageType : uint8_t { Utility = 0x0, // 1 word System = 0x1, // 1 word ChannelVoice1 = 0x2, // 1 word SysEx = 0x3, // 2 words ChannelVoice2 = 0x4, // 2 words Data128 = 0x5, // 4 words Unknown6 = 0x6, // 1 word Unknown7 = 0x7, // 1 word Unknown8 = 0x8, // 2 words Unknown9 = 0x9, // 2 words UnknownA = 0xa, // 2 words UnknownB = 0xb, // 3 words UnknownC = 0xc, // 3 words UnknownD = 0xd, // 4 words UnknownE = 0xe, // 4 words UnknownF = 0xf, // 4 words }; //------------------------------------------------------------------------ struct UMPMessage { uint32_t data; SMTG_ALWAYS_INLINE constexpr MessageType type () const { auto val = (data & 0xf0000000ul) >> 28; return static_cast (val); } SMTG_ALWAYS_INLINE constexpr size_t messageWordCount () const { auto index = static_cast (type ()); return wordCounts[index]; } SMTG_ALWAYS_INLINE constexpr uint8_t group () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte3_7bits () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte4_7bits () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte1 () const { return value (data); } SMTG_ALWAYS_INLINE constexpr uint8_t byte2 () const { return value (data); } SMTG_ALWAYS_INLINE constexpr uint8_t byte3 () const { return value (data); } SMTG_ALWAYS_INLINE constexpr uint8_t byte4 () const { return value (data); } protected: static constexpr std::array wordCounts = {1u, 1u, 1u, 2u, 2u, 4u, 1u, 1u, 2u, 2u, 2u, 3u, 3u, 4u, 4u, 4u}; template SMTG_ALWAYS_INLINE constexpr T bitMask () const { return ((static_cast (1) << bits) - 1) << (((sizeof (T) * 8) - bits) - pos); } template SMTG_ALWAYS_INLINE constexpr T value (T data) const { return (data & bitMask ()) >> (((sizeof (T) * 8) - bits) - pos); } }; //------------------------------------------------------------------------ struct UMPMessage2 : UMPMessage { uint32_t data2; SMTG_ALWAYS_INLINE constexpr uint8_t byte5_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte6_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte7_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte8_7bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint16_t byte5_16bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint16_t byte7_16bits () const { return static_cast (value (data2)); } SMTG_ALWAYS_INLINE constexpr uint8_t byte5 () const { return value (data2); } SMTG_ALWAYS_INLINE constexpr uint8_t byte6 () const { return value (data2); } SMTG_ALWAYS_INLINE constexpr uint8_t byte7 () const { return value (data2); } SMTG_ALWAYS_INLINE constexpr uint8_t byte8 () const { return value (data2); } #if SMTG_OS_MACOS // TO REMOVE: We had to add a duplicate this function from UMPMessage // for older XCode than 15 to fix a linker issue SMTG_ALWAYS_INLINE constexpr uint8_t group () const { return static_cast (value (data)); } #endif }; //------------------------------------------------------------------------ struct UMPMessage4 : UMPMessage2 { uint32_t data3; uint32_t data4; SMTG_ALWAYS_INLINE constexpr uint8_t byte9 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte10 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte11 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte12 () const { return value (data3); } SMTG_ALWAYS_INLINE constexpr uint8_t byte13 () const { return value (data4); } SMTG_ALWAYS_INLINE constexpr uint8_t byte14 () const { return value (data4); } SMTG_ALWAYS_INLINE constexpr uint8_t byte15 () const { return value (data4); } SMTG_ALWAYS_INLINE constexpr uint8_t byte16 () const { return value (data4); } }; static_assert (sizeof (UMPMessage) == sizeof (uint32_t), ""); static_assert (sizeof (UMPMessage2) == sizeof (uint32_t) * 2, ""); static_assert (sizeof (UMPMessage4) == sizeof (uint32_t) * 4, ""); //------------------------------------------------------------------------ struct UMPMessageUtility : UMPMessage { enum class Status { Noop = 0x0, JrClock = 0x1, JrTimestamp = 0x2, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr IUniversalMidiPacketHandler::Timestamp timestamp () const { return static_cast (value (data)); } }; //------------------------------------------------------------------------ struct UMPMessageSystem : UMPMessage { enum class Status { Reserved = 0xf0, MIDITimeCode = 0xf1, SongPositionPointer = 0xf2, SongSelect = 0xf3, Reserved2 = 0xf4, Reserved3 = 0xf5, TuneRequest = 0xf6, Reserved4 = 0xf7, TimingClock = 0xf8, Reserved5 = 0xf9, Start = 0xfa, Continue = 0xfb, Stop = 0xfc, Reserved6 = 0xfd, ActiveSensing = 0xfe, Reset = 0xff, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } }; //------------------------------------------------------------------------ struct UMPMessageChannelVoice1 : UMPMessage { enum class Status { NoteOff = 0x8, NoteOn = 0x9, PolyPressure = 0xa, ControlChange = 0xb, ProgramChange = 0xc, ChannelPressure = 0xd, PitchBend = 0xe, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint32_t channel () const { return value (data); } }; //------------------------------------------------------------------------ struct UMPMessageSysEx : UMPMessage2 { enum class Status { Packet = 0x0, Start = 0x1, Continue = 0x2, End = 0x3, }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } }; //------------------------------------------------------------------------ struct UMPMessageChannelVoice2 : UMPMessage2 { enum class Status { RegisteredPerNoteController = 0x0, AssignablePerNoteController = 0x1, RegisteredController = 0x2, AssignableController = 0x3, RelativeRegisteredController = 0x4, RelativeAssignableController = 0x5, PerNotePitchBend = 0x6, NoteOff = 0x8, NoteOn = 0x9, PolyPressure = 0xa, ControlChange = 0xb, ProgramChange = 0xc, ChannelPressure = 0xd, PitchBend = 0xe, PerNoteManagement = 0xf }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint32_t channel () const { return value (data); } }; //------------------------------------------------------------------------ struct UMPMessageData128 : UMPMessage4 { enum class Status { Packet = 0x0, Start = 0x1, Continue = 0x2, End = 0x3, MixedHeader = 0x8, MixedPayload = 0x9 }; SMTG_ALWAYS_INLINE constexpr Status status () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t numBytes () const { return static_cast (value (data)); } SMTG_ALWAYS_INLINE constexpr uint8_t mdsId () const { return numBytes (); } }; //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onUtilityMessage (const UMPMessageUtility& msg, const IUniversalMidiPacketHandler& handler) { auto status = msg.status (); using Status = UMPMessageUtility::Status; switch (status) { case Status::Noop: { handler.onNoop (msg.group ()); break; } case Status::JrClock: { handler.onJitterClock (msg.group (), msg.timestamp ()); break; } case Status::JrTimestamp: { handler.onJitterTimestamp (msg.group (), msg.timestamp ()); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onSystemMessage (const UMPMessageSystem& msg, const IUniversalMidiPacketHandler& handler) { auto status = msg.status (); using Status = UMPMessageSystem::Status; using SystemRealtime = IUniversalMidiPacketHandler::SystemRealtime; switch (status) { case Status::MIDITimeCode: { handler.onMIDITimeCode (msg.group (), msg.byte3_7bits ()); break; } case Status::SongPositionPointer: { handler.onSongPositionPointer (msg.group (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::SongSelect: { handler.onSongSelect (msg.group (), msg.byte3_7bits ()); break; } case Status::TuneRequest: { handler.onTuneRequest (msg.group ()); break; } case Status::TimingClock: { handler.onSystemRealtime (msg.group (), SystemRealtime::TimingClock); break; } case Status::Start: { handler.onSystemRealtime (msg.group (), SystemRealtime::Start); break; } case Status::Continue: { handler.onSystemRealtime (msg.group (), SystemRealtime::Continue); break; } case Status::Stop: { handler.onSystemRealtime (msg.group (), SystemRealtime::Stop); break; } case Status::ActiveSensing: { handler.onSystemRealtime (msg.group (), SystemRealtime::ActiveSensing); break; } case Status::Reset: { handler.onSystemRealtime (msg.group (), SystemRealtime::Reset); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onChannelVoice1Message (const UMPMessageChannelVoice1& msg, const IUniversalMidiPacketHandler& handler) { using Status = UMPMessageChannelVoice1::Status; switch (msg.status ()) { case Status::NoteOff: { handler.onMidi1NoteOff (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::NoteOn: { handler.onMidi1NoteOn (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::PolyPressure: { handler.onMidi1PolyPressure (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::ControlChange: { handler.onMidi1ControlChange (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } case Status::ProgramChange: { handler.onMidi1ProgramChange (msg.group (), msg.channel (), msg.byte3_7bits ()); break; } case Status::ChannelPressure: { handler.onMidi1ChannelPressure (msg.group (), msg.channel (), msg.byte3_7bits ()); break; } case Status::PitchBend: { handler.onMidi1PitchBend (msg.group (), msg.channel (), msg.byte3_7bits (), msg.byte4_7bits ()); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onSysExMessage (const UMPMessageSysEx& msg, const IUniversalMidiPacketHandler& handler) { using Status = UMPMessageSysEx::Status; switch (msg.status ()) { case Status::Packet: { handler.onSysExPacket (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } case Status::Start: { handler.onSysExStart (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } case Status::Continue: { handler.onSysExContinue (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } case Status::End: { handler.onSysExEnd (msg.group (), {msg.byte3_7bits (), msg.byte4_7bits (), msg.byte5_7bits (), msg.byte6_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()}); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onChannelVoice2Message (const UMPMessageChannelVoice2& msg, const IUniversalMidiPacketHandler& handler) { auto group = msg.group (); auto status = msg.status (); auto channel = msg.channel (); using Status = UMPMessageChannelVoice2::Status; switch (status) { case Status::RegisteredPerNoteController: { handler.onRegisteredPerNoteController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::AssignablePerNoteController: { handler.onAssignablePerNoteController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::RegisteredController: { handler.onRegisteredController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::AssignableController: { handler.onAssignableController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::RelativeRegisteredController: { handler.onRelativeRegisteredController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::RelativeAssignableController: { handler.onRelativeAssignableController (group, channel, msg.byte3_7bits (), msg.byte4_7bits (), msg.data2); break; } case Status::PerNotePitchBend: { handler.onPerNotePitchBend (group, channel, msg.byte3_7bits (), msg.data2); break; } case Status::NoteOn: { handler.onNoteOn (group, channel, msg.byte3_7bits (), msg.byte5_16bits (), msg.byte4_7bits (), msg.byte7_16bits ()); break; } case Status::NoteOff: { handler.onNoteOff (group, channel, msg.byte3_7bits (), msg.byte5_16bits (), msg.byte4_7bits (), msg.byte7_16bits ()); break; } case Status::PolyPressure: { handler.onPolyPressure (group, channel, msg.byte3_7bits (), msg.data2); break; } case Status::ControlChange: { handler.onControlChange (group, channel, msg.byte3_7bits (), msg.data2); break; } case Status::ProgramChange: { handler.onProgramChange (group, channel, msg.byte4_7bits (), msg.byte5_7bits (), msg.byte7_7bits (), msg.byte8_7bits ()); break; } case Status::ChannelPressure: { handler.onChannelPressure (group, channel, msg.data2); break; } case Status::PitchBend: { handler.onPitchBend (group, channel, msg.data2); break; } case Status::PerNoteManagement: { handler.onPerNoteManagement (group, channel, msg.byte3_7bits (), msg.byte4_7bits ()); break; } default: return false; } return true; } //------------------------------------------------------------------------ SMTG_ALWAYS_INLINE bool onData128Message (const UMPMessageData128& msg, const IUniversalMidiPacketHandler& handler) { using Status = UMPMessageData128::Status; switch (msg.status ()) { case Status::Packet: { handler.onSysEx8Packet (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::Start: { handler.onSysEx8Start (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::Continue: { handler.onSysEx8Continue (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::End: { handler.onSysEx8End (msg.group (), msg.numBytes (), msg.byte3 (), {msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::MixedHeader: { handler.onMixedDataSetHeader (msg.group (), msg.mdsId (), {msg.byte3 (), msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } case Status::MixedPayload: { handler.onMixedDataSetPayload (msg.group (), msg.mdsId (), {msg.byte3 (), msg.byte4 (), msg.byte5 (), msg.byte6 (), msg.byte7 (), msg.byte8 (), msg.byte9 (), msg.byte10 (), msg.byte12 (), msg.byte13 (), msg.byte14 (), msg.byte15 (), msg.byte16 ()}); break; } default: return false; } return true; } //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE size_t parse (const size_t numWords, const uint32_t* words, const IUniversalMidiPacketHandler& handler) { auto msg = reinterpret_cast (words); for (size_t index = 0; index < numWords;) { auto numMsgWords = msg->messageWordCount (); if (index + numMsgWords > numWords) { handler.onInsufficentInputData (index, (index + numMsgWords) - numWords); return index; } switch (msg->type ()) { case MessageType::Utility: { if constexpr ((Sections & ParseSections::Utility) != 0) { auto utilityMsg = reinterpret_cast (msg); if (!onUtilityMessage (*utilityMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::System: { if constexpr ((Sections & ParseSections::System) != 0) { auto systemMsg = reinterpret_cast (msg); if (!onSystemMessage (*systemMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::ChannelVoice1: { if constexpr ((Sections & ParseSections::ChannelVoice1) != 0) { auto channelMsg = reinterpret_cast (msg); if (!onChannelVoice1Message (*channelMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::SysEx: { if constexpr ((Sections & ParseSections::SysEx) != 0) { assert (index < numWords - 1); auto sysExMsg = reinterpret_cast (msg); if (!onSysExMessage (*sysExMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::ChannelVoice2: { if constexpr ((Sections & ParseSections::ChannelVoice2) != 0) { assert (index < numWords - 1); auto channelMsg = reinterpret_cast (msg); if (!onChannelVoice2Message (*channelMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } case MessageType::Data128: { if constexpr ((Sections & ParseSections::Data128) != 0) { assert (index < numWords - 3); auto dataMsg = reinterpret_cast (msg); if (!onData128Message (*dataMsg, handler)) { if (handler.onInvalidInputData (index) == ParsingAction::Break) return index; } } break; } } index += numMsgWords; msg += numMsgWords; } return numWords; } //------------------------------------------------------------------------ } // Detail //------------------------------------------------------------------------ template SMTG_ALWAYS_INLINE size_t parsePackets (const size_t numWords, const uint32_t* words, const IUniversalMidiPacketHandler& handler) { return Detail::parse (numWords, words, handler); } //------------------------------------------------------------------------ struct UniversalMidiPacketHandlerAdapter : IUniversalMidiPacketHandler { void onNoop (Group group) const override {} void onJitterClock (Group group, Timestamp time) const override {} void onJitterTimestamp (Group group, Timestamp time) const override {} void onMIDITimeCode (Group group, Timecode timecode) const override {} void onSongPositionPointer (Group group, uint8_t posLSB, uint8_t posMSB) const override {} void onSongSelect (Group group, uint8_t songIndex) const override {} void onTuneRequest (Group group) const override {} void onSystemRealtime (Group group, SystemRealtime which) const override {} void onMidi1NoteOff (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const override { } void onMidi1NoteOn (Group group, Channel channel, NoteNumber note, Velocity8 velocity) const override { } void onMidi1PolyPressure (Group group, Channel channel, NoteNumber note, Data8 data) const override { } void onMidi1ControlChange (Group group, Channel channel, ControllerNumber controller, Data8 value) const override { } void onMidi1ProgramChange (Group group, Channel channel, Program program) const override {} void onMidi1ChannelPressure (Group group, Channel channel, Data8 pressure) const override {} void onMidi1PitchBend (Group group, Channel channel, Data8 valueLSB, Data8 valueMSB) const override { } void onSysExPacket (Group group, SysEx6ByteData data) const override {} void onSysExStart (Group group, SysEx6ByteData data) const override {} void onSysExContinue (Group group, SysEx6ByteData data) const override {} void onSysExEnd (Group group, SysEx6ByteData data) const override {} void onRegisteredPerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const override { } void onAssignablePerNoteController (Group group, Channel channel, NoteNumber note, ControllerNumber controller, Data32 data) const override { } void onRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onRelativeRegisteredController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onRelativeAssignableController (Group group, Channel channel, BankNumber bank, Index index, Data32 data) const override { } void onPerNotePitchBend (Group group, Channel channel, NoteNumber note, Data32 data) const override { } void onNoteOff (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const override { } void onNoteOn (Group group, Channel channel, NoteNumber note, Velocity16 velocity, AttributeType attr, AttributeValue attrValue) const override { } void onPolyPressure (Group group, Channel channel, NoteNumber note, Data32 data) const override { } void onControlChange (Group group, Channel channel, ControllerNumber controller, Data32 data) const override { } void onProgramChange (Group group, Channel channel, OptionFlags options, Program program, BankMSB bankMSB, BankLSB bankLSB) const override { } void onChannelPressure (Group group, Channel channel, Data32 data) const override {} void onPitchBend (Group group, Channel channel, Data32 data) const override {} void onPerNoteManagement (Group group, Channel channel, NoteNumber note, OptionFlags options) const override { } void onSysEx8Packet (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onSysEx8Start (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onSysEx8Continue (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onSysEx8End (Group group, Data8 numBytes, Index streamID, SysEx13ByteData data) const override { } void onMixedDataSetHeader (Group group, Index mdsID, MixedData data) const override {} void onMixedDataSetPayload (Group group, Index mdsID, MixedData data) const override {} ParsingAction onInvalidInputData (size_t index) const override { return ParsingAction::Continue; } void onInsufficentInputData (size_t index, size_t numMissingWords) const override {} }; //------------------------------------------------------------------------ } // Steinberg::Vst::EditorHost