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
|