/src/mozilla-central/dom/midi/MIDIUtils.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "mozilla/dom/MIDITypes.h" |
8 | | #include "mozilla/dom/MIDIUtils.h" |
9 | | |
10 | | // Taken from MIDI IMPLEMENTATION CHART INSTRUCTIONS, MIDI Spec v1.0, Pg. 97 |
11 | | static const uint8_t kCommandByte = 0x80; |
12 | | static const uint8_t kSysexMessageStart = 0xF0; |
13 | | static const uint8_t kSystemMessage = 0xF0; |
14 | | static const uint8_t kSysexMessageEnd = 0xF7; |
15 | | static const uint8_t kSystemRealtimeMessage = 0xF8; |
16 | | // Represents the length of all possible command messages. |
17 | | // Taken from MIDI Spec, Pg. 101v 1.0, Table 2 |
18 | | static const uint8_t kCommandLengths[] = {3, 3, 3, 3, 2, 2, 3}; |
19 | | // Represents the length of all possible system messages. The length of sysex |
20 | | // messages is variable, so we just put zero since it won't be checked anyways. |
21 | | // Taken from MIDI Spec v1.0, Pg. 105, Table 5 |
22 | | static const uint8_t kSystemLengths[] = {0, 2, 3, 2, 1, 1, 1, 1}; |
23 | | |
24 | | namespace mozilla { |
25 | | namespace dom { |
26 | | namespace MIDIUtils { |
27 | | |
28 | | // Checks validity of MIDIMessage passed to it. Throws debug warnings and |
29 | | // returns false if message is not valid. |
30 | | bool |
31 | | IsValidMessage(const MIDIMessage* aMsg) |
32 | 0 | { |
33 | 0 | if (NS_WARN_IF(!aMsg)) { |
34 | 0 | return false; |
35 | 0 | } |
36 | 0 | // Assert on parser problems |
37 | 0 | MOZ_ASSERT(aMsg->data().Length() > 0, |
38 | 0 | "Created a MIDI Message of Length 0. This should never happen!"); |
39 | 0 | uint8_t cmd = aMsg->data()[0]; |
40 | 0 | // If first byte isn't a command, something is definitely wrong. |
41 | 0 | MOZ_ASSERT((cmd & kCommandByte) == kCommandByte, |
42 | 0 | "Constructed a MIDI packet where first byte is not command!"); |
43 | 0 | if (cmd == kSysexMessageStart) { |
44 | 0 | // All we can do with sysex is make sure it starts and ends with the correct |
45 | 0 | // command bytes. |
46 | 0 | if (aMsg->data()[aMsg->data().Length() - 1] != kSysexMessageEnd) { |
47 | 0 | NS_WARNING("Last byte of Sysex Message not 0xF7!"); |
48 | 0 | return false; |
49 | 0 | } |
50 | 0 | return true; |
51 | 0 | } |
52 | 0 | // For system realtime messages, the length should always be 1. |
53 | 0 | if ((cmd & kSystemRealtimeMessage) == kSystemRealtimeMessage) { |
54 | 0 | return aMsg->data().Length() == 1; |
55 | 0 | } |
56 | 0 | // Otherwise, just use the correct array for testing lengths. We can't tell |
57 | 0 | // much about message validity other than that. |
58 | 0 | if ((cmd & kSystemMessage) == kSystemMessage) { |
59 | 0 | if (cmd - kSystemMessage >= static_cast<uint8_t>(ArrayLength(kSystemLengths))) { |
60 | 0 | NS_WARNING("System Message Command byte not valid!"); |
61 | 0 | return false; |
62 | 0 | } |
63 | 0 | return aMsg->data().Length() == kSystemLengths[cmd - kSystemMessage]; |
64 | 0 | } |
65 | 0 | // For non system commands, we only care about differences in the high nibble |
66 | 0 | // of the first byte. Shift this down to give the index of the expected packet |
67 | 0 | // length. |
68 | 0 | uint8_t cmdIndex = (cmd - kCommandByte) >> 4; |
69 | 0 | if (cmdIndex >= ArrayLength(kCommandLengths)) { |
70 | 0 | // If our index is bigger than our array length, command byte is unknown; |
71 | 0 | NS_WARNING("Unknown MIDI command!"); |
72 | 0 | return false; |
73 | 0 | } |
74 | 0 | return aMsg->data().Length() == kCommandLengths[cmdIndex]; |
75 | 0 | } |
76 | | |
77 | | uint32_t |
78 | | ParseMessages(const nsTArray<uint8_t>& aByteBuffer, |
79 | | const TimeStamp& aTimestamp, |
80 | | nsTArray<MIDIMessage>& aMsgArray) |
81 | 0 | { |
82 | 0 | uint32_t bytesRead = 0; |
83 | 0 | bool inSysexMessage = false; |
84 | 0 | nsAutoPtr<MIDIMessage> currentMsg; |
85 | 0 | for (auto& byte : aByteBuffer) { |
86 | 0 | bytesRead++; |
87 | 0 | if ((byte & kSystemRealtimeMessage) == kSystemRealtimeMessage) { |
88 | 0 | MIDIMessage rt_msg; |
89 | 0 | rt_msg.data().AppendElement(byte); |
90 | 0 | rt_msg.timestamp() = aTimestamp; |
91 | 0 | aMsgArray.AppendElement(rt_msg); |
92 | 0 | continue; |
93 | 0 | } |
94 | 0 | if (byte == kSysexMessageEnd) { |
95 | 0 | if (!inSysexMessage) { |
96 | 0 | MOZ_ASSERT(inSysexMessage); |
97 | 0 | NS_WARNING("Got sysex message end with no sysex message being processed!"); |
98 | 0 | } |
99 | 0 | inSysexMessage = false; |
100 | 0 | } else if (byte & kCommandByte) { |
101 | 0 | if (currentMsg && IsValidMessage(currentMsg)) { |
102 | 0 | aMsgArray.AppendElement(*currentMsg); |
103 | 0 | } |
104 | 0 | currentMsg = new MIDIMessage(); |
105 | 0 | currentMsg->timestamp() = aTimestamp; |
106 | 0 | } |
107 | 0 | currentMsg->data().AppendElement(byte); |
108 | 0 | if (byte == kSysexMessageStart) { |
109 | 0 | inSysexMessage = true; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | if (currentMsg && IsValidMessage(currentMsg)) { |
113 | 0 | aMsgArray.AppendElement(*currentMsg); |
114 | 0 | } |
115 | 0 | return bytesRead; |
116 | 0 | } |
117 | | |
118 | | bool |
119 | | IsSysexMessage(const MIDIMessage& aMsg) |
120 | 0 | { |
121 | 0 | if (aMsg.data().Length() == 0) { |
122 | 0 | return false; |
123 | 0 | } |
124 | 0 | if (aMsg.data()[0] == kSysexMessageStart) { |
125 | 0 | return true; |
126 | 0 | } |
127 | 0 | return false; |
128 | 0 | } |
129 | | } |
130 | | } // namespace dom |
131 | | } // namespace mozilla |