1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/MIDITypes.h"
8 #include "mozilla/dom/MIDIUtils.h"
9 #include "mozilla/UniquePtr.h"
11 // Taken from MIDI IMPLEMENTATION CHART INSTRUCTIONS, MIDI Spec v1.0, Pg. 97
12 static const uint8_t kCommandByte
= 0x80;
13 static const uint8_t kSysexMessageStart
= 0xF0;
14 static const uint8_t kSystemMessage
= 0xF0;
15 static const uint8_t kSysexMessageEnd
= 0xF7;
16 static const uint8_t kSystemRealtimeMessage
= 0xF8;
17 // Represents the length of all possible command messages.
18 // Taken from MIDI Spec, Pg. 101v 1.0, Table 2
19 static const uint8_t kCommandLengths
[] = {3, 3, 3, 3, 2, 2, 3};
20 // Represents the length of all possible system messages. The length of sysex
21 // messages is variable, so we just put zero since it won't be checked anyways.
22 // Taken from MIDI Spec v1.0, Pg. 105, Table 5
23 static const uint8_t kSystemLengths
[] = {0, 2, 3, 2, 1, 1, 1, 1};
25 namespace mozilla::dom::MIDIUtils
{
27 // Checks validity of MIDIMessage passed to it. Throws debug warnings and
28 // returns false if message is not valid.
29 bool IsValidMessage(const MIDIMessage
* aMsg
) {
30 if (NS_WARN_IF(!aMsg
)) {
33 // Assert on parser problems
34 MOZ_ASSERT(aMsg
->data().Length() > 0,
35 "Created a MIDI Message of Length 0. This should never happen!");
36 uint8_t cmd
= aMsg
->data()[0];
37 // If first byte isn't a command, something is definitely wrong.
38 MOZ_ASSERT((cmd
& kCommandByte
) == kCommandByte
,
39 "Constructed a MIDI packet where first byte is not command!");
40 if (cmd
== kSysexMessageStart
) {
41 // All we can do with sysex is make sure it starts and ends with the
42 // correct command bytes.
43 if (aMsg
->data()[aMsg
->data().Length() - 1] != kSysexMessageEnd
) {
44 NS_WARNING("Last byte of Sysex Message not 0xF7!");
49 // For system realtime messages, the length should always be 1.
50 if ((cmd
& kSystemRealtimeMessage
) == kSystemRealtimeMessage
) {
51 return aMsg
->data().Length() == 1;
53 // Otherwise, just use the correct array for testing lengths. We can't tell
54 // much about message validity other than that.
55 if ((cmd
& kSystemMessage
) == kSystemMessage
) {
56 if (cmd
- kSystemMessage
>=
57 static_cast<uint8_t>(ArrayLength(kSystemLengths
))) {
58 NS_WARNING("System Message Command byte not valid!");
61 return aMsg
->data().Length() == kSystemLengths
[cmd
- kSystemMessage
];
63 // For non system commands, we only care about differences in the high nibble
64 // of the first byte. Shift this down to give the index of the expected packet
66 uint8_t cmdIndex
= (cmd
- kCommandByte
) >> 4;
67 if (cmdIndex
>= ArrayLength(kCommandLengths
)) {
68 // If our index is bigger than our array length, command byte is unknown;
69 NS_WARNING("Unknown MIDI command!");
72 return aMsg
->data().Length() == kCommandLengths
[cmdIndex
];
75 uint32_t ParseMessages(const nsTArray
<uint8_t>& aByteBuffer
,
76 const TimeStamp
& aTimestamp
,
77 nsTArray
<MIDIMessage
>& aMsgArray
) {
78 uint32_t bytesRead
= 0;
79 bool inSysexMessage
= false;
80 UniquePtr
<MIDIMessage
> currentMsg
;
81 for (auto& byte
: aByteBuffer
) {
83 if ((byte
& kSystemRealtimeMessage
) == kSystemRealtimeMessage
) {
85 rt_msg
.data().AppendElement(byte
);
86 rt_msg
.timestamp() = aTimestamp
;
87 aMsgArray
.AppendElement(rt_msg
);
90 if (byte
== kSysexMessageEnd
) {
91 if (!inSysexMessage
) {
92 MOZ_ASSERT(inSysexMessage
);
94 "Got sysex message end with no sysex message being processed!");
96 inSysexMessage
= false;
97 } else if (byte
& kCommandByte
) {
98 if (currentMsg
&& IsValidMessage(currentMsg
.get())) {
99 aMsgArray
.AppendElement(*currentMsg
);
101 currentMsg
= MakeUnique
<MIDIMessage
>();
102 currentMsg
->timestamp() = aTimestamp
;
104 currentMsg
->data().AppendElement(byte
);
105 if (byte
== kSysexMessageStart
) {
106 inSysexMessage
= true;
109 if (currentMsg
&& IsValidMessage(currentMsg
.get())) {
110 aMsgArray
.AppendElement(*currentMsg
);
115 bool IsSysexMessage(const MIDIMessage
& aMsg
) {
116 if (aMsg
.data().Length() == 0) {
119 if (aMsg
.data()[0] == kSysexMessageStart
) {
124 } // namespace mozilla::dom::MIDIUtils