Home | History | Annotate | Download | only in midi
      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 
     12 namespace media {
     13 namespace {
     14 
     15 const uint8 kSysEx = 0xf0;
     16 const uint8 kEndOfSysEx = 0xf7;
     17 
     18 bool IsDataByte(uint8 data) {
     19   return (data & 0x80) == 0;
     20 }
     21 
     22 bool IsFirstStatusByte(uint8 data) {
     23   return !IsDataByte(data) && data != kEndOfSysEx;
     24 }
     25 
     26 bool IsSystemRealTimeMessage(uint8 data) {
     27   return 0xf8 <= data && data <= 0xff;
     28 }
     29 
     30 }  // namespace
     31 
     32 MIDIMessageQueue::MIDIMessageQueue(bool allow_running_status)
     33     : allow_running_status_(allow_running_status) {}
     34 
     35 MIDIMessageQueue::~MIDIMessageQueue() {}
     36 
     37 void MIDIMessageQueue::Add(const std::vector<uint8>& data) {
     38   queue_.insert(queue_.end(), data.begin(), data.end());
     39 }
     40 
     41 void MIDIMessageQueue::Add(const uint8* data, size_t length) {
     42   queue_.insert(queue_.end(), data, data + length);
     43 }
     44 
     45 void 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