• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "modules/webmidi/MIDIOutput.h"
33 
34 #include "bindings/core/v8/ExceptionState.h"
35 #include "core/dom/ExceptionCode.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/frame/LocalDOMWindow.h"
38 #include "core/timing/Performance.h"
39 #include "modules/webmidi/MIDIAccess.h"
40 
41 namespace blink {
42 
43 namespace {
44 
now(ExecutionContext * context)45 double now(ExecutionContext* context)
46 {
47     LocalDOMWindow* window = context ? context->executingWindow() : 0;
48     Performance* performance = window ? &window->performance() : 0;
49     return performance ? performance->now() : 0.0;
50 }
51 
52 class MessageValidator {
53 public:
validate(Uint8Array * array,ExceptionState & exceptionState,bool sysexEnabled)54     static bool validate(Uint8Array* array, ExceptionState& exceptionState, bool sysexEnabled)
55     {
56         MessageValidator validator(array);
57         return validator.process(exceptionState, sysexEnabled);
58     }
59 private:
MessageValidator(Uint8Array * array)60     MessageValidator(Uint8Array* array)
61         : m_data(array->data())
62         , m_length(array->length())
63         , m_offset(0) { }
64 
process(ExceptionState & exceptionState,bool sysexEnabled)65     bool process(ExceptionState& exceptionState, bool sysexEnabled)
66     {
67         while (!isEndOfData() && acceptRealTimeMessages()) {
68             if (!isStatusByte()) {
69                 exceptionState.throwTypeError("Running status is not allowed " + getPositionString());
70                 return false;
71             }
72             if (isEndOfSysex()) {
73                 exceptionState.throwTypeError("Unexpected end of system exclusive message " + getPositionString());
74                 return false;
75             }
76             if (isReservedStatusByte()) {
77                 exceptionState.throwTypeError("Reserved status is not allowed " + getPositionString());
78                 return false;
79             }
80             if (isSysex()) {
81                 if (!sysexEnabled) {
82                     exceptionState.throwDOMException(InvalidAccessError, "System exclusive message is not allowed " + getPositionString());
83                     return false;
84                 }
85                 if (!acceptCurrentSysex()) {
86                     if (isEndOfData())
87                         exceptionState.throwTypeError("System exclusive message is not ended by end of system exclusive message.");
88                     else
89                         exceptionState.throwTypeError("System exclusive message contains a status byte " + getPositionString());
90                     return false;
91                 }
92             } else {
93                 if (!acceptCurrentMessage()) {
94                     if (isEndOfData())
95                         exceptionState.throwTypeError("Message is incomplete.");
96                     else
97                         exceptionState.throwTypeError("Unexpected status byte at index " + getPositionString());
98                     return false;
99                 }
100             }
101         }
102         return true;
103     }
104 
105 private:
isEndOfData()106     bool isEndOfData() { return m_offset >= m_length; }
isSysex()107     bool isSysex() { return m_data[m_offset] == 0xf0; }
isSystemMessage()108     bool isSystemMessage() { return m_data[m_offset] >= 0xf0; }
isEndOfSysex()109     bool isEndOfSysex() { return m_data[m_offset] == 0xf7; }
isRealTimeMessage()110     bool isRealTimeMessage() { return m_data[m_offset] >= 0xf8; }
isStatusByte()111     bool isStatusByte() { return m_data[m_offset] & 0x80; }
isReservedStatusByte()112     bool isReservedStatusByte() { return m_data[m_offset] == 0xf4 || m_data[m_offset] == 0xf5 || m_data[m_offset] == 0xf9 || m_data[m_offset] == 0xfd; }
113 
acceptRealTimeMessages()114     bool acceptRealTimeMessages()
115     {
116         for (; !isEndOfData(); m_offset++) {
117             if (isRealTimeMessage() && !isReservedStatusByte())
118                 continue;
119             return true;
120         }
121         return false;
122     }
123 
acceptCurrentSysex()124     bool acceptCurrentSysex()
125     {
126         ASSERT(isSysex());
127         for (m_offset++; !isEndOfData(); m_offset++) {
128             if (isReservedStatusByte())
129                 return false;
130             if (isRealTimeMessage())
131                 continue;
132             if (isEndOfSysex()) {
133                 m_offset++;
134                 return true;
135             }
136             if (isStatusByte())
137                 return false;
138         }
139         return false;
140     }
141 
acceptCurrentMessage()142     bool acceptCurrentMessage()
143     {
144         ASSERT(isStatusByte());
145         ASSERT(!isSysex());
146         ASSERT(!isReservedStatusByte());
147         ASSERT(!isRealTimeMessage());
148         static const int channelMessageLength[7] = { 3, 3, 3, 3, 2, 2, 3 }; // for 0x8*, 0x9*, ..., 0xe*
149         static const int systemMessageLength[7] = { 2, 3, 2, 0, 0, 1, 0 }; // for 0xf1, 0xf2, ..., 0xf7
150         size_t length = isSystemMessage() ? systemMessageLength[m_data[m_offset] - 0xf1] : channelMessageLength[(m_data[m_offset] >> 4) - 8];
151         size_t count = 1;
152         for (m_offset++; !isEndOfData(); m_offset++) {
153             if (isReservedStatusByte())
154                 return false;
155             if (isRealTimeMessage())
156                 continue;
157             if (isStatusByte())
158                 return false;
159             if (++count == length) {
160                 m_offset++;
161                 return true;
162             }
163         }
164         return false;
165     }
166 
getPositionString()167     String getPositionString() { return "at index " + String::number(m_offset) + " (" + String::number(m_data[m_offset]) + ")."; }
168 
169     const unsigned char* m_data;
170     const size_t m_length;
171     size_t m_offset;
172 };
173 
174 } // namespace
175 
create(MIDIAccess * access,unsigned portIndex,const String & id,const String & manufacturer,const String & name,const String & version)176 MIDIOutput* MIDIOutput::create(MIDIAccess* access, unsigned portIndex, const String& id, const String& manufacturer, const String& name, const String& version)
177 {
178     ASSERT(access);
179     return adoptRefCountedGarbageCollectedWillBeNoop(new MIDIOutput(access, portIndex, id, manufacturer, name, version));
180 }
181 
MIDIOutput(MIDIAccess * access,unsigned portIndex,const String & id,const String & manufacturer,const String & name,const String & version)182 MIDIOutput::MIDIOutput(MIDIAccess* access, unsigned portIndex, const String& id, const String& manufacturer, const String& name, const String& version)
183     : MIDIPort(access, id, manufacturer, name, MIDIPortTypeOutput, version)
184     , m_portIndex(portIndex)
185 {
186 }
187 
~MIDIOutput()188 MIDIOutput::~MIDIOutput()
189 {
190 }
191 
send(Uint8Array * array,double timestamp,ExceptionState & exceptionState)192 void MIDIOutput::send(Uint8Array* array, double timestamp, ExceptionState& exceptionState)
193 {
194     if (timestamp == 0.0)
195         timestamp = now(executionContext());
196 
197     if (!array)
198         return;
199 
200     if (MessageValidator::validate(array, exceptionState, midiAccess()->sysexEnabled()))
201         midiAccess()->sendMIDIData(m_portIndex, array->data(), array->length(), timestamp);
202 }
203 
send(Vector<unsigned> unsignedData,double timestamp,ExceptionState & exceptionState)204 void MIDIOutput::send(Vector<unsigned> unsignedData, double timestamp, ExceptionState& exceptionState)
205 {
206     if (timestamp == 0.0)
207         timestamp = now(executionContext());
208 
209     RefPtr<Uint8Array> array = Uint8Array::create(unsignedData.size());
210 
211     for (size_t i = 0; i < unsignedData.size(); ++i) {
212         if (unsignedData[i] > 0xff) {
213             exceptionState.throwTypeError("The value at index " + String::number(i) + " (" + String::number(unsignedData[i]) + ") is greater than 0xFF.");
214             return;
215         }
216         unsigned char value = unsignedData[i] & 0xff;
217         array->set(i, value);
218     }
219 
220     send(array.get(), timestamp, exceptionState);
221 }
222 
send(Uint8Array * data,ExceptionState & exceptionState)223 void MIDIOutput::send(Uint8Array* data, ExceptionState& exceptionState)
224 {
225     send(data, 0.0, exceptionState);
226 }
227 
send(Vector<unsigned> unsignedData,ExceptionState & exceptionState)228 void MIDIOutput::send(Vector<unsigned> unsignedData, ExceptionState& exceptionState)
229 {
230     send(unsignedData, 0.0, exceptionState);
231 }
232 
trace(Visitor * visitor)233 void MIDIOutput::trace(Visitor* visitor)
234 {
235     MIDIPort::trace(visitor);
236 }
237 
238 } // namespace blink
239