LCOV - code coverage report
Current view: top level - muse/framework/midi - midievent.h Coverage Total Hit
Test: filtered_coverage.info Lines: 56.2 % 682 383
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #ifndef MUSE_MIDI_MIDIEVENT_H
      23              : #define MUSE_MIDI_MIDIEVENT_H
      24              : 
      25              : #include <cstdint>
      26              : #include <cstring>
      27              : #include <array>
      28              : #include <set>
      29              : #include <cassert>
      30              : #include <string>
      31              : 
      32              : #include "containers.h"
      33              : 
      34              : #ifndef UNUSED
      35              : #define UNUSED(x) (void)x;
      36              : #endif
      37              : 
      38              : #ifndef MU_FALLTHROUGH
      39              : #if __has_cpp_attribute(fallthrough)
      40              : #define MU_FALLTHROUGH() [[fallthrough]]
      41              : #else
      42              : #define MU_FALLTHROUGH() (void)0
      43              : #endif
      44              : #endif
      45              : 
      46              : namespace muse::midi {
      47              : using channel_t = uint8_t;
      48              : using tuning_t = float;
      49              : 
      50              : /*!
      51              :  * MIDI Event stored in Universal MIDI Packet Format Message
      52              :  * @see M2-104-UM
      53              :  * @see midi.org
      54              :  */
      55              : struct Event {
      56              :     enum class MessageType {
      57              :         Utility             = 0x0,
      58              :         SystemRealTime      = 0x1,
      59              :         //! MIDI1.0 voice message
      60              :         ChannelVoice10      = 0x2,
      61              :         SystemExclusiveData = 0x3,
      62              :         //! MIDI2.0 voice message
      63              :         ChannelVoice20      = 0x4,
      64              :         Data                = 0x5
      65              :     };
      66              : 
      67              :     enum class Opcode {
      68              :         RegisteredPerNoteController = 0b0000,
      69              :         AssignablePerNoteController = 0b0001,
      70              :         RegisteredController        = 0b0010,
      71              :         AssignableController        = 0b0011,
      72              :         RelativeRegisteredController= 0b0100,
      73              :         RelativeAssignableController= 0b0101,
      74              :         PerNotePitchBend            = 0b0110,
      75              :         NoteOff                     = 0b1000,
      76              :         NoteOn                      = 0b1001,
      77              :         PolyPressure                = 0b1010,
      78              :         ControlChange               = 0b1011,
      79              :         ProgramChange               = 0b1100,
      80              :         ChannelPressure             = 0b1101,
      81              :         PitchBend                   = 0b1110,
      82              :         PerNoteManagement           = 0b1111
      83              :     };
      84              : 
      85              :     enum class AttributeType {
      86              :         NoData = 0x00,
      87              :         ManufacturerSpecific = 0x01,
      88              :         ProfileSpecific = 0x02,
      89              :         Pitch = 0x03
      90              :     };
      91              : 
      92              :     enum class UtilityStatus {
      93              :         NoOperation = 0x00,
      94              :         JRClock     = 0x01,
      95              :         JRTimestamp = 0x02
      96              :     };
      97              : 
      98           11 :     Event()
      99           11 :         : m_data({ 0, 0, 0, 0 }) {}
     100            9 :     Event(const std::array<uint32_t, 4>& d)
     101            9 :         : m_data(d) {}
     102           16 :     Event(Opcode opcode, MessageType type = MessageType::ChannelVoice20)
     103           16 :     {
     104           16 :         setMessageType(type);
     105           16 :         setOpcode(opcode);
     106           16 :     }
     107              : 
     108              :     //! 4.8.1 NOOP Utility message
     109            6 :     static Event NOOP() { return Event(); }
     110              : 
     111              :     //! from standard MIDI1.0 (specification 1996)
     112            1 :     static Event fromMidi10Package(uint32_t data)
     113              :     {
     114            1 :         Event e;
     115              :         union {
     116              :             unsigned char byte[4];
     117              :             uint32_t full;
     118              :         } u;
     119            1 :         u.full = data;
     120              : 
     121            1 :         if ((u.byte[0] >= 0x80)
     122            1 :             && (u.byte[0] < 0xF0)
     123              :             ) {
     124            1 :             e.m_data[0] = (u.byte[0] << 16) | (u.byte[1] << 8) | u.byte[2];
     125            1 :             e.setMessageType(MessageType::ChannelVoice10);
     126              :         }
     127            1 :         return e;
     128              :     }
     129              : 
     130              :     uint32_t toMidi10Package() const
     131              :     {
     132              :         if (messageType() == MessageType::ChannelVoice10) {
     133              :             union {
     134              :                 unsigned char byte[4];
     135              :                 uint32_t uint32;
     136              :             } p;
     137              :             p.uint32 = m_data[0];
     138              :             std::swap(p.byte[0], p.byte[2]);
     139              :             p.byte[3] = 0;
     140              :             return p.uint32;
     141              :         }
     142              :         return 0;
     143              :     }
     144              : 
     145            2 :     static Event fromMidi10Bytes(const unsigned char* pointer, size_t length)
     146              :     {
     147            2 :         Event e;
     148            2 :         uint32_t val = 0;
     149            2 :         assert(length <= 3);
     150            8 :         for (size_t i = 0; i < length; ++i) {
     151            6 :             uint32_t byteVal = static_cast<uint32_t>(pointer[i]);
     152            6 :             val |= byteVal << ((2 - i) * 8);
     153              :         }
     154            2 :         e.m_data[0] = val;
     155            2 :         e.setMessageType(MessageType::ChannelVoice10);
     156            2 :         return e;
     157              :     }
     158              : 
     159            1 :     size_t toMidi10Bytes(unsigned char* pointer) const
     160              :     {
     161            1 :         if (messageType() == MessageType::ChannelVoice10) {
     162            1 :             auto val = m_data[0];
     163            1 :             size_t c = Event::midi10ByteCountForOpcode(opcode());
     164            1 :             assert(c <= 3);
     165            4 :             for (size_t i = 0; i < c; ++i) {
     166            3 :                 uint32_t byteVal = (val >> ((2 - i) * 8)) & 0xff;
     167            3 :                 pointer[i] = static_cast<unsigned char>(byteVal);
     168              :             }
     169            1 :             return c;
     170              :         }
     171            0 :         return 0;
     172              :     }
     173              : 
     174            2 :     static std::vector<Event> fromMidi10SysExBytes(const uint8_t* data, size_t size)
     175              :     {
     176            2 :         if (!data || size == 0) {
     177            0 :             return {};
     178              :         }
     179              : 
     180              :         // Remove F0/F7 if present
     181            2 :         if (size >= 2 && data[0] == 0xF0 && data[size - 1] == 0xF7) {
     182            1 :             data += 1;
     183            1 :             size -= 2;
     184              :         }
     185              : 
     186            2 :         std::vector<Event> result;
     187            2 :         size_t pos = 0;
     188              : 
     189            7 :         while (pos < size) {
     190            5 :             const size_t chunk = std::min<size_t>(6, size - pos);
     191            5 :             std::array<uint32_t, 4> words = {};
     192              : 
     193              :             uint32_t status;
     194            5 :             if (pos == 0 && chunk == size) {
     195            1 :                 status = 0; // COMPLETE
     196            4 :             } else if (pos == 0) {
     197            1 :                 status = 1; // START
     198            3 :             } else if (pos + chunk >= size) {
     199            1 :                 status = 3; // END
     200              :             } else {
     201            2 :                 status = 2; // CONTINUE
     202              :             }
     203              : 
     204            5 :             uint32_t w0 = 0;
     205              : 
     206              :             // MessageType = SysEx
     207            5 :             w0 |= (static_cast<uint32_t>(MessageType::SystemExclusiveData) << 28);
     208              : 
     209              :             // Group = 0
     210            5 :             w0 |= (0 << 24);
     211              : 
     212              :             // Status
     213            5 :             w0 |= (status << 20);
     214              : 
     215              :             // Byte count
     216            5 :             w0 |= (static_cast<uint32_t>(chunk) << 16);
     217              : 
     218              :             // First 2 bytes
     219            5 :             if (chunk >= 1) {
     220            5 :                 w0 |= static_cast<uint32_t>(data[pos + 0]) << 8;
     221              :             }
     222            5 :             if (chunk >= 2) {
     223            5 :                 w0 |= static_cast<uint32_t>(data[pos + 1]);
     224              :             }
     225              : 
     226              :             // Remaining bytes (up to 4)
     227            5 :             uint32_t w1 = 0;
     228           19 :             for (size_t i = 2; i < chunk; ++i) {
     229           14 :                 w1 |= static_cast<uint32_t>(data[pos + i]) << (24 - (i - 2) * 8);
     230              :             }
     231              : 
     232            5 :             words[0] = w0;
     233            5 :             words[1] = w1;
     234              : 
     235            5 :             result.emplace_back(words);
     236            5 :             pos += chunk;
     237              :         }
     238              : 
     239            2 :         return result;
     240            2 :     }
     241              : 
     242              :     static Event fromMidi20Words(const uint32_t* data, size_t count)
     243              :     {
     244              :         Event e;
     245              :         size_t numBytes = std::min(count, e.m_data.size()) * sizeof(uint32_t);
     246              :         std::memcpy(e.m_data.data(), data, numBytes);
     247              :         return e;
     248              :     }
     249              : 
     250           10 :     const uint32_t* midi20Words() const
     251              :     {
     252           10 :         return m_data.data();
     253              :     }
     254              : 
     255            4 :     size_t midi20WordCount() const
     256              :     {
     257            4 :         return Event::wordCountForMessageType(messageType());
     258              :     }
     259              : 
     260            7 :     bool operator ==(const Event& other) const { return m_data == other.m_data; }
     261            5 :     bool operator !=(const Event& other) const { return !operator==(other); }
     262            4 :     operator bool() const {
     263            4 :         return operator!=(NOOP()) && isValid();
     264              :     }
     265              : 
     266            2 :     bool operator <(const Event& other) const
     267              :     {
     268            2 :         return m_data < other.m_data;
     269              :     }
     270              : 
     271           88 :     bool isChannelVoice() const { return messageType() == MessageType::ChannelVoice10 || messageType() == MessageType::ChannelVoice20; }
     272            0 :     bool isChannelVoice20() const { return messageType() == MessageType::ChannelVoice20; }
     273            9 :     bool isMessageTypeIn(const std::set<MessageType>& types) const { return types.find(messageType()) != types.end(); }
     274           44 :     bool isOpcodeIn(const std::set<Opcode>& opcodes) const { return opcodes.find(opcode()) != opcodes.end(); }
     275              : 
     276              :     //! check UMP for correct structure
     277            3 :     bool isValid() const
     278              :     {
     279            3 :         switch (messageType()) {
     280            0 :         case MessageType::Utility: {
     281            0 :             std::set<UtilityStatus> statuses = { UtilityStatus::NoOperation, UtilityStatus::JRClock, UtilityStatus::JRTimestamp };
     282            0 :             return statuses.find(static_cast<UtilityStatus>(status())) != statuses.end();
     283            0 :         }
     284              : 
     285            0 :         case MessageType::SystemRealTime:
     286              :         case MessageType::SystemExclusiveData:
     287              :         case MessageType::Data:
     288            0 :             return true;
     289              : 
     290            2 :         case MessageType::ChannelVoice10:
     291            8 :             return isOpcodeIn({ Opcode::NoteOff, Opcode::NoteOn, Opcode::PolyPressure, Opcode::ControlChange, Opcode::ProgramChange,
     292            2 :                                 Opcode::ChannelPressure, Opcode::PitchBend });
     293              : 
     294            1 :         case MessageType::ChannelVoice20:
     295            4 :             return isOpcodeIn({ Opcode::RegisteredPerNoteController,
     296              :                                 Opcode::AssignablePerNoteController,
     297              :                                 Opcode::RegisteredController,
     298              :                                 Opcode::AssignableController,
     299              :                                 Opcode::RelativeRegisteredController,
     300              :                                 Opcode::RelativeAssignableController,
     301              :                                 Opcode::PerNotePitchBend,
     302              :                                 Opcode::NoteOff,
     303              :                                 Opcode::NoteOn,
     304              :                                 Opcode::PolyPressure,
     305              :                                 Opcode::ControlChange,
     306              :                                 Opcode::ProgramChange,
     307              :                                 Opcode::ChannelPressure,
     308              :                                 Opcode::PitchBend,
     309              :                                 Opcode::PerNoteManagement
     310            1 :                               });
     311              :         }
     312            0 :         return false;
     313              :     }
     314              : 
     315          202 :     MessageType messageType() const { return static_cast<MessageType>(m_data[0] >> 28); }
     316           19 :     void setMessageType(MessageType type)
     317              :     {
     318           19 :         uint32_t mask = static_cast<uint32_t>(type) << 28;
     319           19 :         m_data[0] &= 0x0FFFFFFF;
     320           19 :         m_data[0] |= mask;
     321           19 :     }
     322              : 
     323            4 :     channel_t group() const { return (m_data[0] >> 24) & 0b00001111; }
     324            4 :     void setGroup(channel_t group)
     325              :     {
     326            4 :         assert(group < 16);
     327            4 :         uint32_t mask = group << 24;
     328            4 :         m_data[0] &= 0xF0FFFFFF;
     329            4 :         m_data[0] |= mask;
     330            4 :     }
     331              : 
     332           63 :     Opcode opcode() const
     333              :     {
     334           63 :         assertChannelVoice();
     335           63 :         return static_cast<Opcode>((m_data[0] >> 20) & 0b00001111);
     336              :     }
     337              : 
     338           16 :     void setOpcode(Opcode code)
     339              :     {
     340           16 :         assertChannelVoice();
     341           16 :         uint32_t mask = static_cast<uint32_t>(code) << 20;
     342           16 :         m_data[0] &= 0xFF0FFFFF;
     343           16 :         m_data[0] |= mask;
     344           16 :     }
     345              : 
     346            0 :     uint8_t status() const
     347              :     {
     348            0 :         assertMessageType({ MessageType::SystemRealTime, MessageType::Utility });
     349              : 
     350            0 :         switch (messageType()) {
     351            0 :         case MessageType::SystemRealTime: return (m_data[0] >> 16) & 0b11111111;
     352            0 :         case MessageType::Utility: return (m_data[0] >> 16) & 0b00001111;
     353            0 :         default: break;
     354              :         }
     355            0 :         return 0;
     356              :     }
     357              : 
     358            4 :     channel_t channel() const
     359              :     {
     360            4 :         assertChannelVoice();
     361            4 :         return (m_data[0] >> 16) & 0b00001111;
     362              :     }
     363              : 
     364            4 :     void setChannel(channel_t channel)
     365              :     {
     366            4 :         assertChannelVoice();
     367            4 :         assert(channel < 16);
     368            4 :         uint32_t mask = channel << 16;
     369            4 :         m_data[0] &= 0xFFF0FFFF;
     370            4 :         m_data[0] |= mask;
     371            4 :     }
     372              : 
     373            5 :     uint8_t note() const
     374              :     {
     375           10 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff, Opcode::PolyPressure,
     376              :                        Opcode::PerNotePitchBend, Opcode::PerNoteManagement,
     377              :                        Opcode::RegisteredPerNoteController, Opcode::AssignablePerNoteController });
     378            5 :         switch (messageType()) {
     379            5 :         case MessageType::ChannelVoice10:
     380            5 :         case MessageType::ChannelVoice20: return (m_data[0] >> 8) & 0x7F;
     381              :             break;
     382            0 :         default: assert(false);
     383              :         }
     384              :         return 0;
     385              :     }
     386              : 
     387            4 :     void setNote(uint8_t value)
     388              :     {
     389            8 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff, Opcode::PolyPressure,
     390              :                        Opcode::PerNotePitchBend, Opcode::PerNoteManagement,
     391              :                        Opcode::RegisteredPerNoteController, Opcode::AssignablePerNoteController });
     392            4 :         assert(value < 128);
     393            4 :         uint32_t mask = value << 8;
     394            4 :         m_data[0] &= 0xFFFF00FF;
     395            4 :         m_data[0] |= mask;
     396            4 :     }
     397              : 
     398              :     //! return note from Pitch attribute (for NoteOn & NoteOff) events) if exists else return note()
     399            1 :     uint8_t pitchNote() const
     400              :     {
     401            2 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     402            1 :         if (attributeType() == AttributeType::Pitch) {
     403            1 :             return static_cast<uint8_t>(attribute() >> 9);
     404              :         }
     405            0 :         return note();
     406              :     }
     407              : 
     408              :     //! return tuning in semitones from Pitch attribute (for NoteOn & NoteOff) events) if exists else return 0.f
     409            2 :     tuning_t pitchTuning() const
     410              :     {
     411            4 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     412            2 :         if (attributeType() == AttributeType::Pitch) {
     413            2 :             return static_cast<tuning_t>((attribute() & 0x1FF) / static_cast<tuning_t>(0x200));
     414              :         }
     415            0 :         return 0.f;
     416              :     }
     417              : 
     418              :     //! return tuning in cents
     419            1 :     tuning_t pitchTuningCents() const
     420              :     {
     421            1 :         return pitchTuning() * 100;
     422              :     }
     423              : 
     424              :     //! 4.2.14.3 @see pitchNote(), pitchTuning()
     425            1 :     void setPitchNote(uint8_t note, tuning_t tuning)
     426              :     {
     427            2 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     428            2 :         assertMessageType({ MessageType::ChannelVoice20 });
     429              : 
     430            1 :         setAttributeType(AttributeType::Pitch);
     431            1 :         uint16_t attribute = static_cast<uint16_t>(tuning * 0x200);
     432            1 :         attribute &= 0x1FF; //for safe clear first 7 bits
     433            1 :         attribute |= static_cast<uint16_t>(note) << 9;
     434            1 :         setAttribute(attribute);
     435            1 :     }
     436              : 
     437            5 :     uint8_t velocity7() const
     438              :     {
     439           10 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     440              : 
     441            5 :         switch (messageType()) {
     442            5 :         case MessageType::ChannelVoice10: return m_data[0] & 0x7F;
     443            0 :         case MessageType::ChannelVoice20: return static_cast<uint8_t>(scaleDown(m_data[1] >> 16, 16, 7));
     444            0 :         default: assert(false);
     445              :         }
     446              :         return 0;
     447              :     }
     448              : 
     449            4 :     void setVelocity7(uint8_t value)
     450              :     {
     451            8 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     452            4 :         assert(value < 128);
     453              : 
     454            4 :         switch (messageType()) {
     455            2 :         case MessageType::ChannelVoice10: {
     456            2 :             uint32_t mask = value & 0x7F;
     457            2 :             m_data[0] &= 0xFFFFFF00;
     458            2 :             m_data[0] |= mask;
     459            2 :         } break;
     460            2 :         case MessageType::ChannelVoice20: {
     461            2 :             uint32_t mask = scaleUp(value, 7, 16) << 16;
     462            2 :             m_data[1] &= 0x0000FFFF;
     463            2 :             m_data[1] |= mask;
     464            2 :         } break;
     465            0 :         default: assert(false);
     466              :         }
     467            4 :     }
     468              : 
     469            1 :     uint16_t velocity16() const
     470              :     {
     471            2 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     472              : 
     473            1 :         switch (messageType()) {
     474            0 :         case MessageType::ChannelVoice10: return static_cast<uint16_t>(scaleUp(m_data[0] & 0x7F, 7, 16));
     475            1 :         case MessageType::ChannelVoice20: return static_cast<uint16_t>(m_data[1] >> 16);
     476            0 :         default: assert(false);
     477              :         }
     478              :         return 0;
     479              :     }
     480              : 
     481            1 :     void setVelocity16(uint16_t value)
     482              :     {
     483            2 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     484              : 
     485            1 :         switch (messageType()) {
     486            0 :         case MessageType::ChannelVoice10: {
     487            0 :             uint32_t mask = scaleDown(value, 16, 7);
     488            0 :             m_data[0] &= 0xFFFFFF00;
     489            0 :             m_data[0] |= mask;
     490            0 :         } break;
     491            1 :         case MessageType::ChannelVoice20: {
     492            1 :             uint32_t mask = value << 16;
     493            1 :             m_data[1] &= 0x0000FFFF;
     494            1 :             m_data[1] |= mask;
     495            1 :         } break;
     496            0 :         default: assert(false);
     497              :         }
     498            1 :     }
     499              : 
     500            3 :     uint32_t data() const
     501              :     {
     502            3 :         switch (messageType()) {
     503            0 :         case MessageType::ChannelVoice10:
     504            0 :             switch (opcode()) {
     505            0 :             case Opcode::PolyPressure:
     506              :             case Opcode::ControlChange:
     507            0 :                 return m_data[0] & 0x7F;
     508            0 :             case Opcode::ChannelPressure:
     509            0 :                 return (m_data[0] >> 8) & 0x7F;
     510            0 :             case Opcode::PitchBend:
     511            0 :                 return ((m_data[0] & 0x7F) << 7) | ((m_data[0] & 0x7F00) >> 8);
     512            0 :             default: assert(false);
     513              :             }
     514              :             MU_FALLTHROUGH();
     515            3 :         case MessageType::ChannelVoice20:
     516            3 :             switch (opcode()) {
     517            3 :             case Opcode::PolyPressure:
     518              :             case Opcode::RegisteredPerNoteController:
     519              :             case Opcode::AssignablePerNoteController:
     520              :             case Opcode::RegisteredController:
     521              :             case Opcode::AssignableController:
     522              :             case Opcode::RelativeRegisteredController:
     523              :             case Opcode::RelativeAssignableController:
     524              :             case Opcode::ControlChange:
     525              :             case Opcode::ChannelPressure:
     526              :             case Opcode::PitchBend:
     527              :             case Opcode::PerNotePitchBend:
     528            3 :                 return m_data[1];
     529            0 :             default: assert(false);
     530              :             }
     531              :             MU_FALLTHROUGH();
     532            0 :         default:;     //TODO
     533              :         }
     534              : 
     535            0 :         return 0;
     536              :     }
     537              : 
     538            2 :     void setData(uint32_t data)
     539              :     {
     540            2 :         switch (messageType()) {
     541            0 :         case MessageType::ChannelVoice10:
     542            0 :             switch (opcode()) {
     543            0 :             case Opcode::PolyPressure:
     544              :             case Opcode::ControlChange: {
     545            0 :                 assert(data < 128);
     546            0 :                 uint32_t mask = data & 0x7F;
     547            0 :                 m_data[0] &= 0xFFFFFF00;
     548            0 :                 m_data[0] |= mask;
     549            0 :                 break;
     550              :             }
     551            0 :             case Opcode::ChannelPressure: {
     552            0 :                 assert(data < 128);
     553            0 :                 uint32_t mask = (data & 0x7F) << 8;
     554            0 :                 m_data[0] &= 0xFFFF00FF;
     555            0 :                 m_data[0] |= mask;
     556            0 :                 break;
     557              :             }
     558            0 :             case Opcode::PitchBend: {
     559            0 :                 data &= 0x3FFF;
     560            0 :                 m_data[0] &= 0xFFFF0000;
     561              :                 //3d byte: r,lsb 4th: r,msb
     562            0 :                 m_data[0] |= ((data >> 7) & 0x7F) | ((data & 0x7F) << 8);
     563            0 :                 break;
     564              :             }
     565            0 :             default: assert(false);
     566              :             }
     567            0 :             break;
     568              : 
     569            2 :         case MessageType::ChannelVoice20:
     570            2 :             switch (opcode()) {
     571            2 :             case Opcode::PolyPressure:
     572              :             case Opcode::RegisteredPerNoteController:
     573              :             case Opcode::AssignablePerNoteController:
     574              :             case Opcode::RegisteredController:
     575              :             case Opcode::AssignableController:
     576              :             case Opcode::RelativeRegisteredController:
     577              :             case Opcode::RelativeAssignableController:
     578              :             case Opcode::ControlChange:
     579              :             case Opcode::ChannelPressure:
     580              :             case Opcode::PitchBend:
     581              :             case Opcode::PerNotePitchBend:
     582            2 :                 m_data[1] = data;
     583            2 :                 break;
     584            0 :             default: assert(false);
     585              :             }
     586            2 :             break;
     587            0 :         default: assert(false);
     588              :         }
     589            2 :     }
     590              : 
     591            0 :     uint8_t data7() const
     592              :     {
     593            0 :         uint32_t val = data();
     594            0 :         if (messageType() == MessageType::ChannelVoice20) {
     595            0 :             val = scaleDown(val, 32, 7);
     596            0 :         } else if (messageType() == MessageType::ChannelVoice10 && opcode() == Opcode::PitchBend) {
     597            0 :             val = scaleDown(val, 14, 7);
     598              :         }
     599            0 :         return val & 0x7F;
     600              :     }
     601              : 
     602            2 :     uint32_t data14() const
     603              :     {
     604            2 :         uint32_t val = data();
     605            2 :         if (messageType() == MessageType::ChannelVoice20) {
     606            2 :             return scaleDown(val, 32, 14);
     607              :         }
     608            0 :         return val;
     609              :     }
     610              : 
     611            1 :     uint32_t pitchBend14() const
     612              :     {
     613            1 :         assert(isChannelVoice() && opcode() == Opcode::PitchBend);
     614            1 :         return data14();
     615              :     }
     616              : 
     617              :     /*! return signed value:
     618              :      *  float value for Pitch attribute of NoteOn
     619              :      *  @see: data() can return pitch centered unsigned raw value depenned on MIDI version
     620              :      */
     621            0 :     float pitch() const
     622              :     {
     623            0 :         assertOpcode({ Opcode::PitchBend });
     624            0 :         switch (messageType()) {
     625            0 :         case MessageType::ChannelVoice20: return (data() - 0x80000000) / static_cast<float>(0xFFFFFFFF);
     626            0 :         default: /* silence */ break;
     627              :         }
     628            0 :         return (data() - 8192) / static_cast<float>(0x3FFF);//MIDI1.0
     629              :     }
     630              : 
     631            1 :     uint8_t index() const
     632              :     {
     633            2 :         assertOpcode({ Opcode::ControlChange, Opcode::RegisteredPerNoteController, Opcode::AssignablePerNoteController,
     634              :                        Opcode::RegisteredController, Opcode::AssignableController, Opcode::RelativeRegisteredController,
     635              :                        Opcode::RelativeAssignableController });
     636            1 :         switch (opcode()) {
     637            1 :         case Opcode::ControlChange:
     638              :         case Opcode::RegisteredController:
     639              :         case Opcode::AssignableController:
     640              :         case Opcode::RelativeRegisteredController:
     641              :         case Opcode::RelativeAssignableController:
     642            1 :             return (m_data[0] >> 8) & 0x7F;
     643              : 
     644            0 :         case Opcode::RegisteredPerNoteController:
     645              :         case Opcode::AssignablePerNoteController:
     646            0 :             return m_data[0] & 0xFF;
     647              : 
     648            0 :         default: assert(false);
     649              :         }
     650              :         return 0;
     651              :     }
     652              : 
     653            1 :     void setIndex(uint8_t value)
     654              :     {
     655            2 :         assertOpcode({ Opcode::ControlChange, Opcode::RegisteredPerNoteController, Opcode::AssignablePerNoteController,
     656              :                        Opcode::RegisteredController, Opcode::AssignableController, Opcode::RelativeRegisteredController,
     657              :                        Opcode::RelativeAssignableController });
     658              : 
     659            1 :         switch (opcode()) {
     660            1 :         case Opcode::ControlChange:
     661              :         case Opcode::RegisteredController:
     662              :         case Opcode::AssignableController:
     663              :         case Opcode::RelativeRegisteredController:
     664              :         case Opcode::RelativeAssignableController: {
     665            1 :             assert(value < 128);
     666            1 :             uint32_t mask = value << 8;
     667            1 :             m_data[0] &= 0xFFFF00FF;
     668            1 :             m_data[0] |= mask;
     669            1 :             break;
     670              :         }
     671            0 :         case Opcode::RegisteredPerNoteController:
     672              :         case Opcode::AssignablePerNoteController: {
     673            0 :             m_data[0] &= 0xFFFFFF00;
     674            0 :             m_data[0] |= value;
     675            0 :             break;
     676              :         }
     677            0 :         default: assert(false);
     678              :         }
     679            1 :     }
     680              : 
     681            2 :     uint8_t program() const
     682              :     {
     683            4 :         assertOpcode({ Opcode::ProgramChange });
     684            2 :         switch (messageType()) {
     685            1 :         case MessageType::ChannelVoice10: return (m_data[0] >> 8) & 0x7F;
     686              :             break;
     687            1 :         case MessageType::ChannelVoice20: return (m_data[1] >> 24) & 0x7F;
     688              :             break;
     689            0 :         default: assert(false);
     690              :         }
     691              :         return 0;
     692              :     }
     693              : 
     694            2 :     void setProgram(uint8_t value)
     695              :     {
     696            4 :         assertOpcode({ Opcode::ProgramChange });
     697            2 :         assert(value < 128);
     698            2 :         switch (messageType()) {
     699            1 :         case MessageType::ChannelVoice10: {
     700            1 :             uint32_t mask = value << 8;
     701            1 :             m_data[0] &= 0xFFFF00FF;
     702            1 :             m_data[0] |= mask;
     703            1 :             break;
     704              :         }
     705            1 :         case MessageType::ChannelVoice20: {
     706            1 :             uint32_t mask = value << 24;
     707            1 :             m_data[1] &= 0x00FFFFFF;
     708            1 :             m_data[1] |= mask;
     709            1 :             break;
     710              :         }
     711            0 :         default: assert(false);
     712              :         }
     713            2 :     }
     714              : 
     715            1 :     uint16_t bank() const
     716              :     {
     717            2 :         assertOpcode({ Opcode::ProgramChange, Opcode::RegisteredController, Opcode::AssignableController,
     718              :                        Opcode::RelativeRegisteredController, Opcode::RelativeAssignableController });
     719            2 :         assertMessageType({ MessageType::ChannelVoice20 });
     720            1 :         if (opcode() == Opcode::ProgramChange) {
     721            1 :             return ((m_data[1] & 0x7F00) >> 1) | (m_data[1] & 0x7F);
     722              :         }
     723            0 :         return (m_data[0] >> 8) & 0x7F;
     724              :     }
     725              : 
     726            1 :     void setBank(uint16_t bank)
     727              :     {
     728            2 :         assertOpcode({ Opcode::ProgramChange, Opcode::RegisteredController, Opcode::AssignableController,
     729              :                        Opcode::RelativeRegisteredController, Opcode::RelativeAssignableController });
     730            2 :         assertMessageType({ MessageType::ChannelVoice20 });
     731            1 :         if (opcode() == Opcode::ProgramChange) {
     732            1 :             m_data[0] |= 0x01; //set BankValid bit
     733            1 :             uint32_t mask = (bank & 0x3F80) << 8 | (bank & 0x7F);
     734            1 :             m_data[1] &= 0xFFFF0000;
     735            1 :             m_data[1] |= mask;
     736            1 :             return;
     737              :         }
     738            0 :         assert(bank < 128);
     739            0 :         uint32_t mask = bank << 8;
     740            0 :         m_data[0] &= 0xFFFF00FF;
     741            0 :         m_data[0] |= mask;
     742              :     }
     743              : 
     744            1 :     bool isBankValid() const
     745              :     {
     746            2 :         assertOpcode({ Opcode::ProgramChange });
     747            2 :         assertMessageType({ MessageType::ChannelVoice20 });
     748            1 :         return m_data[0] & 0x01;
     749              :     }
     750              : 
     751            3 :     AttributeType attributeType() const
     752              :     {
     753            6 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     754            6 :         assertMessageType({ MessageType::ChannelVoice20 });
     755            3 :         return static_cast<AttributeType>(m_data[0] & 0xFF);
     756              :     }
     757              : 
     758            1 :     void setAttributeType(AttributeType type)
     759              :     {
     760            2 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     761            2 :         assertMessageType({ MessageType::ChannelVoice20 });
     762            1 :         m_data[0] &= 0xFFFFFF00;
     763            1 :         m_data[0] |= static_cast<uint32_t>(type);
     764            1 :     }
     765              : 
     766              :     void setAttributeType(uint8_t type) { setAttributeType(static_cast<AttributeType>(type)); }
     767              : 
     768            3 :     uint16_t attribute() const
     769              :     {
     770            6 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     771            3 :         if (messageType() == MessageType::ChannelVoice20) {
     772            3 :             return m_data[1] & 0xFFFF;
     773              :         }
     774            0 :         return 0x00;
     775              :     }
     776              : 
     777            1 :     void setAttribute(uint16_t value)
     778              :     {
     779            2 :         assertOpcode({ Opcode::NoteOn, Opcode::NoteOff });
     780            2 :         assertMessageType({ MessageType::ChannelVoice20 });
     781            1 :         m_data[1] &= 0xFFFF0000;
     782            1 :         m_data[1] |= value;
     783            1 :     }
     784              : 
     785              :     void setPerNoteDetach(bool value)
     786              :     {
     787              :         assertOpcode({ Opcode::PerNoteManagement });
     788              :         assertMessageType({ MessageType::ChannelVoice20 });
     789              :         m_data[0] &= 0xFFFFFFFD;
     790              :         m_data[0] |= static_cast<uint32_t>(value);
     791              :     }
     792              : 
     793              :     void setPerNoteReset(bool value)
     794              :     {
     795              :         assertOpcode({ Opcode::PerNoteManagement });
     796              :         assertMessageType({ MessageType::ChannelVoice20 });
     797              :         m_data[0] &= 0xFFFFFFFE;
     798              :         m_data[0] |= static_cast<uint32_t>(value);
     799              :     }
     800              : 
     801              :     //!convert ChannelVoice from MIDI2.0 to MIDI1.0
     802            1 :     std::vector<Event> toMIDI10() const
     803              :     {
     804            1 :         std::vector<Event> events;
     805            1 :         switch (messageType()) {
     806            0 :         case MessageType::ChannelVoice10: events.push_back(*this);
     807            0 :             break;
     808            1 :         case MessageType::ChannelVoice20: {
     809            1 :             auto basic10Event = Event(opcode(), MessageType::ChannelVoice10);
     810            1 :             basic10Event.setChannel(channel());
     811            1 :             basic10Event.setGroup(group());
     812            1 :             switch (opcode()) {
     813              :             //D2.1
     814            1 :             case Opcode::PolyPressure:
     815              :             case Opcode::ControlChange:
     816              :             case Opcode::NoteOn:
     817              :             case Opcode::NoteOff: {
     818            1 :                 auto e = basic10Event;
     819            1 :                 auto v1 = (m_data[0] & 0x7F00);
     820            1 :                 auto v2 = m_data[1] >> 25;
     821            1 :                 if (opcode() == Opcode::NoteOn && v2 == 0) {
     822              :                     //4.2.2 velocity comment
     823            0 :                     v2 = 1;
     824              :                 }
     825            1 :                 e.m_data[0] |= v1 | v2;
     826            1 :                 events.push_back(e);
     827            1 :                 break;
     828              :             }
     829              : 
     830              :             //D2.2
     831            0 :             case Opcode::ChannelPressure: {
     832            0 :                 auto e = basic10Event;
     833            0 :                 e.setData(scaleDown(data(), 32, 7));
     834            0 :                 events.push_back(e);
     835            0 :                 break;
     836              :             }
     837              : 
     838              :             //D2.3
     839            0 :             case Opcode::AssignableController:
     840              :             case Opcode::RegisteredController: {
     841              :                 std::vector<std::pair<uint8_t, uint32_t> > controlChanges = {
     842            0 :                     { (opcode() == Opcode::RegisteredController ? 101 : 99), bank() },
     843            0 :                     { (opcode() == Opcode::RegisteredController ? 100 : 98), index() },
     844            0 :                     { 6,  data() >> 25 }, // first 7 bits
     845            0 :                     { 38, (data() & 0x1FC0000) >> 18 } // second 7 bits
     846            0 :                 };
     847            0 :                 for (auto& c : controlChanges) {
     848            0 :                     auto e = basic10Event;
     849            0 :                     e.setOpcode(Opcode::ControlChange);
     850            0 :                     e.setIndex(c.first);
     851            0 :                     e.setData(c.second);
     852            0 :                     events.push_back(e);
     853              :                 }
     854            0 :                 break;
     855            0 :             }
     856              : 
     857              :             //D2.4
     858            0 :             case Opcode::ProgramChange: {
     859            0 :                 if (isBankValid()) {
     860            0 :                     auto e = basic10Event;
     861            0 :                     e.setOpcode(Opcode::ControlChange);
     862            0 :                     e.setIndex(0);
     863            0 :                     e.setData((m_data[1] & 0x7F00) >> 8);
     864            0 :                     events.push_back(e);
     865            0 :                     e.setIndex(0);
     866            0 :                     e.setData(m_data[1] & 0x7F);
     867            0 :                     events.push_back(e);
     868              :                 }
     869            0 :                 auto e = basic10Event;
     870            0 :                 e.setProgram(program());
     871            0 :                 events.push_back(e);
     872            0 :                 break;
     873              :             }
     874              :             //D2.5
     875            0 :             case Opcode::PitchBend: {
     876            0 :                 auto e = basic10Event;
     877            0 :                 e.setData(pitchBend14());
     878            0 :                 events.push_back(e);
     879            0 :                 break;
     880              :             }
     881            0 :             default: break;
     882              :             }
     883            1 :             break;
     884              :         }
     885            0 :         default: break;
     886              :         }
     887            1 :         return events;
     888            0 :     }
     889              : 
     890              :     //!convert ChannelVoice from MIDI1.0 to MIDI2.0
     891            1 :     Event toMIDI20(Event chain = NOOP()) const
     892              :     {
     893            1 :         Event event;
     894            1 :         switch (messageType()) {
     895            0 :         case MessageType::ChannelVoice20: return *this;
     896              :             break;
     897            1 :         case MessageType::ChannelVoice10: {
     898            1 :             if (chain) {
     899            0 :                 event = chain;
     900              :             } else {
     901            1 :                 event = Event(opcode(), MessageType::ChannelVoice20);
     902            1 :                 event.setChannel(channel());
     903            1 :                 event.setGroup(group());
     904              :             }
     905            1 :             switch (opcode()) {
     906              :             //D3.1
     907            1 :             case Opcode::NoteOn:
     908              :             case Opcode::NoteOff:
     909            1 :                 event.setNote(note());
     910            1 :                 event.setVelocity7(velocity7());
     911            1 :                 if (velocity7() == 0) {
     912            0 :                     event.setOpcode(Opcode::NoteOff);
     913              :                 }
     914            1 :                 break;
     915              :             //D3.2
     916            0 :             case Opcode::PolyPressure:
     917            0 :                 event.setNote(note());
     918            0 :                 event.setData(scaleUp(data(), 7, 32));
     919            0 :                 break;
     920              :             //D3.3
     921            0 :             case Opcode::ControlChange: {
     922            0 :                 switch (index()) {
     923            0 :                 default:
     924            0 :                     event.setIndex(index());
     925            0 :                     event.setData(scaleUp(data(), 7, 32));
     926            0 :                     break;
     927              :                 // RPN and NPRN controller messages are no ordenary conrollers
     928              :                 // and need special handling in MIDI 2.0
     929            0 :                 case 99:
     930            0 :                     event.setOpcode(Opcode::AssignableController);
     931            0 :                     event.setBank(static_cast<uint16_t>(data()));
     932            0 :                     break;
     933            0 :                 case 101:
     934            0 :                     event.setOpcode(Opcode::RegisteredController);
     935            0 :                     event.setBank(static_cast<uint16_t>(data()));
     936            0 :                     break;
     937            0 :                 case 98:
     938              :                 case 100:
     939            0 :                     event.setIndex(static_cast<uint8_t>(data()));
     940            0 :                     break;
     941            0 :                 case 6:
     942            0 :                     event.m_data[0] &= 0x1FFFFFF;
     943            0 :                     event.m_data[0] |= (data() & 0x7F) << 25;
     944            0 :                     break;
     945            0 :                 case 38:
     946            0 :                     event.m_data[0] &= 0xFE03FFFF;
     947            0 :                     event.m_data[0] |= (data() & 0x7F) << 18;
     948            0 :                     break;
     949              :                 }
     950            0 :                 break;
     951              :             }
     952              : 
     953              :             //D3.4
     954            0 :             case Opcode::ProgramChange:
     955            0 :                 event.setProgram(program());
     956            0 :                 break;
     957              : 
     958              :             //D3.5
     959            0 :             case Opcode::ChannelPressure:
     960            0 :                 event.setData(scaleUp(data(), 7, 32));
     961            0 :                 break;
     962              : 
     963              :             //D3.6
     964            0 :             case Opcode::PitchBend:
     965            0 :                 event.setData(scaleUp(data(), 14, 32));
     966            0 :                 break;
     967              : 
     968            0 :             default: break;
     969              :             }
     970            1 :             break;
     971              :         }
     972            0 :         default: break;
     973              :         }
     974            1 :         return event;
     975              :     }
     976              : 
     977            1 :     std::string opcodeString() const
     978              :     {
     979              :     #define opcodeValueMap(o) { o, std::string(#o) \
     980              : }
     981              : 
     982              :         static const std::map<Opcode, std::string> m = {
     983            1 :             opcodeValueMap(Opcode::RegisteredPerNoteController),
     984            1 :             opcodeValueMap(Opcode::AssignablePerNoteController),
     985            1 :             opcodeValueMap(Opcode::RegisteredController),
     986            1 :             opcodeValueMap(Opcode::AssignableController),
     987            1 :             opcodeValueMap(Opcode::RelativeRegisteredController),
     988            1 :             opcodeValueMap(Opcode::RelativeAssignableController),
     989            1 :             opcodeValueMap(Opcode::PerNotePitchBend),
     990            1 :             opcodeValueMap(Opcode::NoteOff),
     991            1 :             opcodeValueMap(Opcode::NoteOn),
     992            1 :             opcodeValueMap(Opcode::PolyPressure),
     993            1 :             opcodeValueMap(Opcode::ControlChange),
     994            1 :             opcodeValueMap(Opcode::ProgramChange),
     995            1 :             opcodeValueMap(Opcode::ChannelPressure),
     996            1 :             opcodeValueMap(Opcode::PitchBend),
     997            1 :             opcodeValueMap(Opcode::PerNoteManagement)
     998           19 :         };
     999              :     #undef opcodeValueMap
    1000            1 :         return muse::value(m, opcode());
    1001            2 :     }
    1002              : 
    1003            1 :     std::string to_string() const
    1004              :     {
    1005            1 :         std::string str;
    1006            0 :         auto dataToStr = [&str, this]() {
    1007            0 :             str += " {";
    1008            0 :             for (auto& d : m_data) {
    1009            0 :                 str += " " + std::to_string(d);
    1010              :             }
    1011            0 :             str += "}";
    1012            0 :         };
    1013              : 
    1014            1 :         switch (messageType()) {
    1015            1 :         case MessageType::ChannelVoice10: {
    1016            1 :             str += "MIDI1.0 " + opcodeString();
    1017            1 :             str += " group: " + std::to_string(group());
    1018            1 :             str += " channel: " + std::to_string(channel());
    1019            1 :             switch (opcode()) {
    1020            1 :             case Opcode::NoteOn:
    1021              :             case Opcode::NoteOff:
    1022            2 :                 str += " note: " + std::to_string(note())
    1023            3 :                        + " velocity: " + std::to_string(velocity7());
    1024            1 :                 break;
    1025            0 :             case Opcode::PolyPressure:
    1026            0 :                 str += " note: " + std::to_string(note())
    1027            0 :                        + " data: " + std::to_string(data());
    1028            0 :                 break;
    1029            0 :             case Opcode::ControlChange:
    1030            0 :                 str += " index: " + std::to_string(index())
    1031            0 :                        + " data: " + std::to_string(data());
    1032            0 :                 break;
    1033            0 :             case Opcode::ProgramChange:
    1034            0 :                 str += " program: " + std::to_string(program());
    1035            0 :                 break;
    1036            0 :             case Opcode::ChannelPressure:
    1037            0 :                 str += " data: " + std::to_string(data());
    1038            0 :                 break;
    1039            0 :             case Opcode::PitchBend:
    1040            0 :                 str += " value: " + std::to_string(pitch());
    1041            0 :                 break;
    1042            0 :             default: /* silence warning */ break;
    1043              :             }
    1044            1 :             break;
    1045              :         }
    1046            0 :         case MessageType::ChannelVoice20: {
    1047            0 :             str += "MIDI2.0 " + opcodeString();
    1048            0 :             str += " group: " + std::to_string(group());
    1049            0 :             str += " channel: " + std::to_string(channel());
    1050            0 :             switch (opcode()) {
    1051            0 :             case Opcode::NoteOff:
    1052              :             case Opcode::NoteOn:
    1053            0 :                 str += " note: " + std::to_string(note())
    1054            0 :                        + " velocity: " + std::to_string(velocity16())
    1055            0 :                        + " attr type: " + std::to_string(static_cast<uint32_t>(attributeType()))
    1056            0 :                        + " attr value: " + std::to_string(attribute());
    1057            0 :                 if (attributeType() == AttributeType::Pitch) {
    1058            0 :                     str += "pitch: note:" + std::to_string(pitchNote()) + " " + std::to_string(pitchTuning()) + " semitone";
    1059              :                 }
    1060            0 :                 break;
    1061            0 :             case Opcode::PolyPressure:
    1062              :             case Opcode::PerNotePitchBend:
    1063            0 :                 str += " note: " + std::to_string(note())
    1064            0 :                        + " data: " + std::to_string(data());
    1065            0 :                 break;
    1066            0 :             case Opcode::RegisteredPerNoteController:
    1067              :             case Opcode::AssignablePerNoteController:
    1068            0 :                 str += " note: " + std::to_string(note())
    1069            0 :                        + " index: " + std::to_string(index())
    1070            0 :                        + " data: " + std::to_string(data());
    1071            0 :                 break;
    1072            0 :             case Opcode::PerNoteManagement:
    1073            0 :                 str += " note: " + std::to_string(note())
    1074            0 :                        + (m_data[0] & 0b10 ? " detach controller" : "")
    1075            0 :                        + (m_data[0] & 0b01 ? " reset controller" : "");
    1076            0 :                 break;
    1077            0 :             case Opcode::ControlChange:
    1078            0 :                 str += " index: " + std::to_string(index())
    1079            0 :                        + " data: " + std::to_string(data());
    1080            0 :                 break;
    1081            0 :             case Opcode::ProgramChange:
    1082            0 :                 str += " program: " + std::to_string(program())
    1083            0 :                        + (isBankValid() ? " set bank: " + std::to_string(bank()) : "");
    1084            0 :                 break;
    1085            0 :             case Opcode::RegisteredController:
    1086              :             case Opcode::AssignableController:
    1087              :             case Opcode::RelativeRegisteredController:
    1088              :             case Opcode::RelativeAssignableController:
    1089            0 :                 str += " bank: " + std::to_string(bank())
    1090            0 :                        + " index: " + std::to_string(index())
    1091            0 :                        + " data: " + std::to_string(data());
    1092            0 :                 break;
    1093            0 :             case Opcode::ChannelPressure:
    1094            0 :                 str += " data: " + std::to_string(data());
    1095            0 :                 break;
    1096            0 :             case Opcode::PitchBend:
    1097            0 :                 str += " value: " + std::to_string(pitch());
    1098            0 :                 break;
    1099              :             }
    1100              : 
    1101            0 :             break;
    1102              :         }
    1103            0 :         case MessageType::Utility:
    1104            0 :             str += "MIDI2.0 Utility ";
    1105            0 :             if (m_data[0] == 0) {
    1106            0 :                 str += " NOOP";
    1107            0 :                 break;
    1108              :             }
    1109            0 :             dataToStr();
    1110            0 :             break;
    1111            0 :         case MessageType::SystemRealTime:
    1112            0 :             str += "MIDI System";
    1113            0 :             dataToStr();
    1114            0 :             break;
    1115            0 :         case MessageType::SystemExclusiveData:
    1116            0 :             str += "MIDI System Exclusive";
    1117            0 :             dataToStr();
    1118            0 :             break;
    1119            0 :         case MessageType::Data:
    1120            0 :             str += "MIDI2.0 Data";
    1121            0 :             dataToStr();
    1122            0 :             break;
    1123              :         }
    1124            1 :         return str;
    1125            0 :     }
    1126              : 
    1127            6 :     static size_t wordCountForMessageType(MessageType messageType)
    1128              :     {
    1129              :         // MIDI Association Document: M2-104-UM
    1130              :         // Document Version 1.1.2
    1131              :         // Draft Date 2023-10-27
    1132              :         // Published 2023-11-10
    1133              :         // Section 2.1.4
    1134            6 :         switch (messageType) {
    1135            0 :         case MessageType::Utility: return 1;
    1136            0 :         case MessageType::SystemRealTime: return 1;
    1137            1 :         case MessageType::ChannelVoice10: return 1;
    1138            4 :         case MessageType::SystemExclusiveData: return 2;
    1139            1 :         case MessageType::ChannelVoice20: return 2;
    1140            0 :         case MessageType::Data: return 4;
    1141              :         }
    1142              : 
    1143              :         // reserved
    1144            0 :         switch (uint32_t(messageType)) {
    1145            0 :         case 0x6: return 1;
    1146            0 :         case 0x7: return 1;
    1147            0 :         case 0x8: return 2;
    1148            0 :         case 0x9: return 2;
    1149            0 :         case 0xa: return 2;
    1150            0 :         case 0xb: return 3;
    1151            0 :         case 0xc: return 3;
    1152            0 :         case 0xd: return 4;
    1153            0 :         case 0xe: return 4;
    1154            0 :         case 0xf: return 4;
    1155              :         }
    1156              : 
    1157            0 :         return 0;
    1158              :     }
    1159              : 
    1160            3 :     static size_t midi10ByteCountForOpcode(Opcode opcode)
    1161              :     {
    1162            3 :         switch (opcode) {
    1163            0 :         case Opcode::RegisteredPerNoteController:
    1164              :         case Opcode::AssignablePerNoteController:
    1165              :         case Opcode::RelativeRegisteredController:
    1166              :         case Opcode::RelativeAssignableController:
    1167              :         case Opcode::PerNotePitchBend:
    1168              :         case Opcode::PerNoteManagement:
    1169              :             //D2.8 cannot be translated to Midi 1.0
    1170            0 :             assert(false);
    1171            0 :         case Opcode::RegisteredController:
    1172              :         case Opcode::AssignableController:
    1173              :             // Already translated to a sequence of CCs.
    1174            0 :             assert(false);
    1175            2 :         case Opcode::NoteOff:
    1176              :         case Opcode::NoteOn:
    1177              :         case Opcode::PolyPressure:
    1178              :         case Opcode::ControlChange:
    1179              :         case Opcode::PitchBend:
    1180            2 :             return 3;
    1181            1 :         case Opcode::ProgramChange:
    1182              :         case Opcode::ChannelPressure:
    1183            1 :             return 2;
    1184              :         }
    1185            0 :         assert(false);
    1186              :         return 0;
    1187              :     }
    1188              : 
    1189              : private:
    1190            9 :     void assertMessageType(const std::set<MessageType>& supportedTypes) const
    1191              :     {
    1192              : #ifdef NDEBUG
    1193              :         UNUSED(supportedTypes);
    1194              : #else
    1195            9 :         assert(isMessageTypeIn(supportedTypes));
    1196              : #endif
    1197            9 :     }
    1198              : 
    1199           87 :     void assertChannelVoice() const { assert(isChannelVoice()); }
    1200              : 
    1201           41 :     void assertOpcode(const std::set<Opcode>& supportedOpcodes) const
    1202              :     {
    1203              : #ifdef NDEBUG
    1204              :         UNUSED(supportedOpcodes);
    1205              : #else
    1206           41 :         assert(isOpcodeIn(supportedOpcodes));
    1207              : #endif
    1208           41 :     }
    1209              : 
    1210            2 :     static uint32_t scaleUp(uint32_t srcVal, size_t srcBits, size_t dstBits)
    1211              :     {
    1212              :         // simple bit shift
    1213            2 :         size_t scaleBits = dstBits - srcBits;
    1214            2 :         uint32_t bitShiftedValue = srcVal << scaleBits;
    1215            2 :         uint32_t srcCenter = static_cast<uint32_t>(2 ^ (srcBits - 1));
    1216            2 :         if (srcVal <= srcCenter) {
    1217            0 :             return bitShiftedValue;
    1218              :         }
    1219              :         // expanded bit repeat scheme
    1220            2 :         size_t repeatBits = srcBits - 1;
    1221            2 :         uint32_t repeatMask = static_cast<uint32_t>((2 ^ repeatBits) - 1);
    1222            2 :         uint32_t repeatValue = srcVal & repeatMask;
    1223            2 :         if (scaleBits > repeatBits) {
    1224            2 :             repeatValue <<= scaleBits - repeatBits;
    1225              :         } else {
    1226            0 :             repeatValue >>= repeatBits - scaleBits;
    1227              :         }
    1228            2 :         while (repeatValue != 0) {
    1229            0 :             bitShiftedValue |= repeatValue;
    1230            0 :             repeatValue >>= repeatBits;
    1231              :         }
    1232            2 :         return bitShiftedValue;
    1233              :     }
    1234              : 
    1235            2 :     static uint32_t scaleDown(uint32_t srcVal, size_t srcBits, size_t dstBits)
    1236              :     {
    1237            2 :         size_t scaleBits = (srcBits - dstBits);
    1238            2 :         return srcVal >> scaleBits;
    1239              :     }
    1240              : 
    1241              :     std::array<uint32_t, 4> m_data = { { 0, 0, 0, 0 } };
    1242              : };
    1243              : }
    1244              : 
    1245              : #endif // MUSE_MIDI_MIDIEVENT_H
        

Generated by: LCOV version 2.0-1