1eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// found in the LICENSE file.
4eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
5f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "content/browser/media/midi_host.h"
6eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/bind.h"
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/bind_helpers.h"
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/debug/trace_event.h"
10a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "base/process/process.h"
11eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/browser/browser_main_loop.h"
12424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "content/browser/child_process_security_policy_impl.h"
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/browser/media/media_internals.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/common/media/midi_messages.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/public/browser/content_browser_client.h"
16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/public/browser/media_observer.h"
17424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "content/public/browser/user_metrics.h"
18eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "media/midi/midi_manager.h"
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/midi/midi_message_queue.h"
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "media/midi/midi_message_util.h"
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using media::MidiManager;
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using media::MidiPortInfoList;
24eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace content {
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)namespace {
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
28a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// The total number of bytes which we're allowed to send to the OS
29a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// before knowing that they have been successfully sent.
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const size_t kMaxInFlightBytes = 10 * 1024 * 1024;  // 10 MB.
31a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
32a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// We keep track of the number of bytes successfully sent to
33a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// the hardware.  Every once in a while we report back to the renderer
34a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// the number of bytes sent since the last report. This threshold determines
35a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)// how many bytes will be sent before reporting back to the renderer.
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)const size_t kAcknowledgementThresholdBytes = 1024 * 1024;  // 1 MB.
37a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool IsDataByte(uint8 data) {
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return (data & 0x80) == 0;
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool IsSystemRealTimeMessage(uint8 data) {
43a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return 0xf8 <= data && data <= 0xff;
44a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}  // namespace
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using media::kSysExByte;
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using media::kEndOfSysExByte;
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiHost::MidiHost(int renderer_process_id, media::MidiManager* midi_manager)
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : BrowserMessageFilter(MidiMsgStart),
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      renderer_process_id_(renderer_process_id),
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      has_sys_ex_permission_(false),
55424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      midi_manager_(midi_manager),
56a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      sent_bytes_in_flight_(0),
57a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      bytes_sent_since_last_acknowledgement_(0) {
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)MidiHost::~MidiHost() {
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (midi_manager_)
6258e6fbe4ee35d65e14b626c557d37565bf8ad179Ben Murdoch    midi_manager_->EndSession(this);
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiHost::OnDestruct() const {
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  BrowserThread::DeleteOnIOThread::Destruct(this);
67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// IPC Messages handler
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bool MidiHost::OnMessageReceived(const IPC::Message& message) {
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  bool handled = true;
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  IPC_BEGIN_MESSAGE_MAP(MidiHost, message)
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    IPC_MESSAGE_HANDLER(MidiHostMsg_StartSession, OnStartSession)
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    IPC_MESSAGE_HANDLER(MidiHostMsg_SendData, OnSendData)
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    IPC_MESSAGE_UNHANDLED(handled = false)
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  IPC_END_MESSAGE_MAP()
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return handled;
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiHost::OnStartSession(int client_id) {
82a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  if (midi_manager_)
83a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    midi_manager_->StartSession(this, client_id);
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiHost::OnSendData(uint32 port,
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          const std::vector<uint8>& data,
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          double timestamp) {
89a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (!midi_manager_)
90a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return;
91a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (data.empty())
933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return;
943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Blink running in a renderer checks permission to raise a SecurityError
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // in JavaScript. The actual permission check for security purposes
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // happens here in the browser process.
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!has_sys_ex_permission_ &&
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      std::find(data.begin(), data.end(), kSysExByte) != data.end()) {
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    RecordAction(base::UserMetricsAction("BadMessageTerminate_MIDI"));
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    BadMessageReceived();
102a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return;
103424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  }
104a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!IsValidWebMIDIData(data))
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
107a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  {
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::AutoLock auto_lock(in_flight_lock_);
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Sanity check that we won't send too much data.
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // TODO(yukawa): Consider to send an error event back to the renderer
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // after some future discussion in W3C.
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (data.size() + sent_bytes_in_flight_ > kMaxInFlightBytes)
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sent_bytes_in_flight_ += data.size();
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  midi_manager_->DispatchSendMidiData(this, port, data, timestamp);
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
120a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochvoid MidiHost::CompleteStartSession(int client_id, media::MidiResult result) {
121a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  MidiPortInfoList input_ports;
122a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  MidiPortInfoList output_ports;
123a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
124010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (result == media::MIDI_OK) {
125a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    input_ports = midi_manager_->input_ports();
126a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    output_ports = midi_manager_->output_ports();
127a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    received_messages_queues_.clear();
128a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    received_messages_queues_.resize(input_ports.size());
129a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    // ChildSecurityPolicy is set just before OnStartSession by
130a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    // MidiDispatcherHost. So we can safely cache the policy.
131a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    has_sys_ex_permission_ = ChildProcessSecurityPolicyImpl::GetInstance()->
132a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch        CanSendMidiSysExMessage(renderer_process_id_);
133a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  }
134a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
135a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  Send(new MidiMsg_SessionStarted(client_id,
136010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                  result,
137a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  input_ports,
138a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch                                  output_ports));
139a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch}
140a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiHost::ReceiveMidiData(
142424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    uint32 port,
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const uint8* data,
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    size_t length,
145eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    double timestamp) {
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  TRACE_EVENT0("midi", "MidiHost::ReceiveMidiData");
147a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (received_messages_queues_.size() <= port)
149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
150a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Lazy initialization
152a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (received_messages_queues_[port] == NULL)
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    received_messages_queues_[port] = new media::MidiMessageQueue(true);
154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  received_messages_queues_[port]->Add(data, length);
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::vector<uint8> message;
157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  while (true) {
158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    received_messages_queues_[port]->Get(&message);
159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (message.empty())
160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      break;
161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
162a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // MIDI devices may send a system exclusive messages even if the renderer
163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // doesn't have a permission to receive it. Don't kill the renderer as
164a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // OnSendData() does.
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (message[0] == kSysExByte && !has_sys_ex_permission_)
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // Send to the renderer.
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Send(new MidiMsg_DataReceived(port, message, timestamp));
170a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
171eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
172eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void MidiHost::AccumulateMidiBytesSent(size_t n) {
174a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  {
175a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    base::AutoLock auto_lock(in_flight_lock_);
176a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    if (n <= sent_bytes_in_flight_)
177a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      sent_bytes_in_flight_ -= n;
178a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
179a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
180a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (bytes_sent_since_last_acknowledgement_ + n >=
181a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      bytes_sent_since_last_acknowledgement_)
182a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    bytes_sent_since_last_acknowledgement_ += n;
183a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
184a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (bytes_sent_since_last_acknowledgement_ >=
185a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      kAcknowledgementThresholdBytes) {
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    Send(new MidiMsg_AcknowledgeSentData(
187a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        bytes_sent_since_last_acknowledgement_));
188a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    bytes_sent_since_last_acknowledgement_ = 0;
189a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
190a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
191a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// static
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool MidiHost::IsValidWebMIDIData(const std::vector<uint8>& data) {
194a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  bool in_sysex = false;
195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  size_t waiting_data_length = 0;
196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for (size_t i = 0; i < data.size(); ++i) {
197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const uint8 current = data[i];
198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (IsSystemRealTimeMessage(current))
199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;  // Real time message can be placed at any point.
200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (waiting_data_length > 0) {
201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      if (!IsDataByte(current))
202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return false;  // Error: |current| should have been data byte.
203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      --waiting_data_length;
204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;  // Found data byte as expected.
205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (in_sysex) {
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (data[i] == kEndOfSysExByte)
208a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        in_sysex = false;
209a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      else if (!IsDataByte(current))
210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        return false;  // Error: |current| should have been data byte.
211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;  // Found data byte as expected.
212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
2135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (current == kSysExByte) {
214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      in_sysex = true;
215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      continue;  // Found SysEX
216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    }
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    waiting_data_length = media::GetMidiMessageLength(current);
218a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (waiting_data_length == 0)
219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return false;  // Error: |current| should have been a valid status byte.
220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    --waiting_data_length;  // Found status byte
221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return waiting_data_length == 0 && !in_sysex;
223a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}  // namespace content
226