This source file includes following definitions.
- now
- validate
- m_offset
- process
- isEndOfData
- isSysex
- isSystemMessage
- isEndOfSysex
- isRealTimeMessage
- isStatusByte
- isReservedStatusByte
- acceptRealTimeMessages
- acceptCurrentSysex
- acceptCurrentMessage
- getPositionString
- create
- m_portIndex
- send
- send
- send
- send
- trace
#include "config.h"
#include "modules/webmidi/MIDIOutput.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContext.h"
#include "core/frame/DOMWindow.h"
#include "core/timing/Performance.h"
#include "modules/webmidi/MIDIAccess.h"
namespace WebCore {
namespace {
double now(ExecutionContext* context)
{
DOMWindow* window = context ? context->executingWindow() : 0;
Performance* performance = window ? &window->performance() : 0;
return performance ? performance->now() : 0.0;
}
class MessageValidator {
public:
static bool validate(Uint8Array* array, ExceptionState& exceptionState, bool sysexEnabled)
{
MessageValidator validator(array);
return validator.process(exceptionState, sysexEnabled);
}
private:
MessageValidator(Uint8Array* array)
: m_data(array->data())
, m_length(array->length())
, m_offset(0) { }
bool process(ExceptionState& exceptionState, bool sysexEnabled)
{
while (!isEndOfData() && acceptRealTimeMessages()) {
if (!isStatusByte()) {
exceptionState.throwTypeError("Running status is not allowed " + getPositionString());
return false;
}
if (isEndOfSysex()) {
exceptionState.throwTypeError("Unexpected end of system exclusive message " + getPositionString());
return false;
}
if (isReservedStatusByte()) {
exceptionState.throwTypeError("Reserved status is not allowed " + getPositionString());
return false;
}
if (isSysex()) {
if (!sysexEnabled) {
exceptionState.throwDOMException(InvalidAccessError, "System exclusive message is not allowed " + getPositionString());
return false;
}
if (!acceptCurrentSysex()) {
if (isEndOfData())
exceptionState.throwTypeError("System exclusive message is not ended by end of system exclusive message.");
else
exceptionState.throwTypeError("System exclusive message contains a status byte " + getPositionString());
return false;
}
} else {
if (!acceptCurrentMessage()) {
if (isEndOfData())
exceptionState.throwTypeError("Message is incomplete.");
else
exceptionState.throwTypeError("Unexpected status byte at index " + getPositionString());
return false;
}
}
}
return true;
}
private:
bool isEndOfData() { return m_offset >= m_length; }
bool isSysex() { return m_data[m_offset] == 0xf0; }
bool isSystemMessage() { return m_data[m_offset] >= 0xf0; }
bool isEndOfSysex() { return m_data[m_offset] == 0xf7; }
bool isRealTimeMessage() { return m_data[m_offset] >= 0xf8; }
bool isStatusByte() { return m_data[m_offset] & 0x80; }
bool isReservedStatusByte() { return m_data[m_offset] == 0xf4 || m_data[m_offset] == 0xf5 || m_data[m_offset] == 0xf9 || m_data[m_offset] == 0xfd; }
bool acceptRealTimeMessages()
{
for (; !isEndOfData(); m_offset++) {
if (isRealTimeMessage() && !isReservedStatusByte())
continue;
return true;
}
return false;
}
bool acceptCurrentSysex()
{
ASSERT(isSysex());
for (m_offset++; !isEndOfData(); m_offset++) {
if (isReservedStatusByte())
return false;
if (isRealTimeMessage())
continue;
if (isEndOfSysex()) {
m_offset++;
return true;
}
if (isStatusByte())
return false;
}
return false;
}
bool acceptCurrentMessage()
{
ASSERT(isStatusByte());
ASSERT(!isSysex());
ASSERT(!isReservedStatusByte());
ASSERT(!isRealTimeMessage());
static const int channelMessageLength[7] = { 3, 3, 3, 3, 2, 2, 3 };
static const int systemMessageLength[7] = { 2, 3, 2, 0, 0, 1, 0 };
size_t length = isSystemMessage() ? systemMessageLength[m_data[m_offset] - 0xf1] : channelMessageLength[(m_data[m_offset] >> 4) - 8];
size_t count = 1;
for (m_offset++; !isEndOfData(); m_offset++) {
if (isReservedStatusByte())
return false;
if (isRealTimeMessage())
continue;
if (isStatusByte())
return false;
if (++count == length) {
m_offset++;
return true;
}
}
return false;
}
String getPositionString() { return "at index " + String::number(m_offset) + " (" + String::number(m_data[m_offset]) + ")."; }
const unsigned char* m_data;
const size_t m_length;
size_t m_offset;
};
}
PassRefPtrWillBeRawPtr<MIDIOutput> MIDIOutput::create(MIDIAccess* access, unsigned portIndex, const String& id, const String& manufacturer, const String& name, const String& version)
{
ASSERT(access);
return adoptRefWillBeRefCountedGarbageCollected(new MIDIOutput(access, portIndex, id, manufacturer, name, version));
}
MIDIOutput::MIDIOutput(MIDIAccess* access, unsigned portIndex, const String& id, const String& manufacturer, const String& name, const String& version)
: MIDIPort(access, id, manufacturer, name, MIDIPortTypeOutput, version)
, m_portIndex(portIndex)
{
ScriptWrappable::init(this);
}
MIDIOutput::~MIDIOutput()
{
}
void MIDIOutput::send(Uint8Array* array, double timestamp, ExceptionState& exceptionState)
{
if (timestamp == 0.0)
timestamp = now(executionContext());
if (!array)
return;
if (MessageValidator::validate(array, exceptionState, midiAccess()->sysexEnabled()))
midiAccess()->sendMIDIData(m_portIndex, array->data(), array->length(), timestamp);
}
void MIDIOutput::send(Vector<unsigned> unsignedData, double timestamp, ExceptionState& exceptionState)
{
if (timestamp == 0.0)
timestamp = now(executionContext());
RefPtr<Uint8Array> array = Uint8Array::create(unsignedData.size());
for (size_t i = 0; i < unsignedData.size(); ++i) {
if (unsignedData[i] > 0xff) {
exceptionState.throwTypeError("The value at index " + String::number(i) + " (" + String::number(unsignedData[i]) + ") is greater than 0xFF.");
return;
}
unsigned char value = unsignedData[i] & 0xff;
array->set(i, value);
}
send(array.get(), timestamp, exceptionState);
}
void MIDIOutput::send(Uint8Array* data, ExceptionState& exceptionState)
{
send(data, 0.0, exceptionState);
}
void MIDIOutput::send(Vector<unsigned> unsignedData, ExceptionState& exceptionState)
{
send(unsignedData, 0.0, exceptionState);
}
void MIDIOutput::trace(Visitor* visitor)
{
MIDIPort::trace(visitor);
}
}