1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/midi/midi_message_queue.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "media/midi/midi_message_util.h"
11
12namespace media {
13namespace {
14
15const uint8 kSysEx = 0xf0;
16const uint8 kEndOfSysEx = 0xf7;
17
18bool IsDataByte(uint8 data) {
19  return (data & 0x80) == 0;
20}
21
22bool IsFirstStatusByte(uint8 data) {
23  return !IsDataByte(data) && data != kEndOfSysEx;
24}
25
26bool IsSystemRealTimeMessage(uint8 data) {
27  return 0xf8 <= data && data <= 0xff;
28}
29
30}  // namespace
31
32MidiMessageQueue::MidiMessageQueue(bool allow_running_status)
33    : allow_running_status_(allow_running_status) {}
34
35MidiMessageQueue::~MidiMessageQueue() {}
36
37void MidiMessageQueue::Add(const std::vector<uint8>& data) {
38  queue_.insert(queue_.end(), data.begin(), data.end());
39}
40
41void MidiMessageQueue::Add(const uint8* data, size_t length) {
42  queue_.insert(queue_.end(), data, data + length);
43}
44
45void MidiMessageQueue::Get(std::vector<uint8>* message) {
46  message->clear();
47
48  while (true) {
49    if (queue_.empty())
50      return;
51
52    const uint8 next = queue_.front();
53    queue_.pop_front();
54
55    // "System Real Time Messages" is a special kind of MIDI messages, which can
56    // appear at arbitrary byte position of MIDI stream. Here we reorder
57    // "System Real Time Messages" prior to |next_message_| so that each message
58    // can be clearly separated as a complete MIDI message.
59    if (IsSystemRealTimeMessage(next)) {
60      message->push_back(next);
61      return;
62    }
63
64    // Here |next_message_[0]| may contain the previous status byte when
65    // |allow_running_status_| is true. Following condition fixes up
66    // |next_message_| if running status condition is not fulfilled.
67    if (!next_message_.empty() &&
68        ((next_message_[0] == kSysEx && IsFirstStatusByte(next)) ||
69         (next_message_[0] != kSysEx && !IsDataByte(next)))) {
70      // An invalid data sequence is found or running status condition is not
71      // fulfilled.
72      next_message_.clear();
73    }
74
75    if (next_message_.empty()) {
76      if (IsFirstStatusByte(next)) {
77        next_message_.push_back(next);
78      } else {
79        // MIDI protocol doesn't provide any error correction mechanism in
80        // physical layers, and incoming messages can be corrupted, and should
81        // be corrected here.
82      }
83      continue;
84    }
85
86    // Here we can assume |next_message_| starts with a valid status byte.
87    const uint8 status_byte = next_message_[0];
88    next_message_.push_back(next);
89
90    if (status_byte == kSysEx) {
91      if (next == kEndOfSysEx) {
92        std::swap(*message, next_message_);
93        next_message_.clear();
94        return;
95      }
96      continue;
97    }
98
99    DCHECK(IsDataByte(next));
100    DCHECK_NE(kSysEx, status_byte);
101    const size_t target_len = GetMidiMessageLength(status_byte);
102    if (next_message_.size() < target_len)
103      continue;
104    if (next_message_.size() == target_len) {
105      std::swap(*message, next_message_);
106      next_message_.clear();
107      if (allow_running_status_) {
108        // Speculatively keep the status byte in case of running status. If this
109        // assumption is not true, |next_message_| will be cleared anyway.
110        next_message_.push_back(status_byte);
111      }
112      return;
113    }
114
115    NOTREACHED();
116  }
117}
118
119}  // namespace media
120