midi_message_queue.cc 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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. #include "media/midi/midi_message_queue.h"
  5. #include <algorithm>
  6. #include "base/check_op.h"
  7. #include "base/notreached.h"
  8. #include "media/midi/message_util.h"
  9. namespace midi {
  10. MidiMessageQueue::MidiMessageQueue(bool allow_running_status)
  11. : allow_running_status_(allow_running_status) {}
  12. MidiMessageQueue::~MidiMessageQueue() = default;
  13. void MidiMessageQueue::Add(const std::vector<uint8_t>& data) {
  14. queue_.insert(queue_.end(), data.begin(), data.end());
  15. }
  16. void MidiMessageQueue::Add(const uint8_t* data, size_t length) {
  17. queue_.insert(queue_.end(), data, data + length);
  18. }
  19. void MidiMessageQueue::Get(std::vector<uint8_t>* message) {
  20. message->clear();
  21. while (true) {
  22. // Check if |next_message_| is already a complete MIDI message or not.
  23. if (!next_message_.empty()) {
  24. const uint8_t status_byte = next_message_.front();
  25. const size_t target_len = GetMessageLength(status_byte);
  26. if (target_len == 0) {
  27. DCHECK_EQ(kSysExByte, status_byte);
  28. if (next_message_.back() == kEndOfSysExByte) {
  29. // OK, this is a complete SysEx message.
  30. std::swap(*message, next_message_);
  31. DCHECK(next_message_.empty());
  32. return;
  33. }
  34. } else if (next_message_.size() == target_len) {
  35. // OK, this is a complete non-SysEx message.
  36. std::swap(*message, next_message_);
  37. DCHECK(next_message_.empty());
  38. if (allow_running_status_ && !IsSystemMessage(status_byte)) {
  39. // Speculatively keep the status byte in case of running status.
  40. // If this assumption is not true, |next_message_| will be cleared
  41. // anyway. Note that system common messages should reset the
  42. // running status.
  43. next_message_.push_back(status_byte);
  44. }
  45. return;
  46. } else if (next_message_.size() > target_len) {
  47. NOTREACHED();
  48. }
  49. }
  50. if (queue_.empty())
  51. return;
  52. // "System Real Time Messages" is a special MIDI message, which can appear
  53. // at an arbitrary byte position of MIDI stream. Here we reorder
  54. // "System Real Time Messages" prior to |next_message_| so that each message
  55. // can be clearly separated as a complete MIDI message.
  56. const uint8_t next = queue_.front();
  57. if (IsSystemRealTimeMessage(next)) {
  58. message->push_back(next);
  59. queue_.pop_front();
  60. return;
  61. }
  62. if (next_message_.empty()) {
  63. const size_t target_len = GetMessageLength(next);
  64. // If |target_len| is zero, it means either |next| is not a valid status
  65. // byte or |next| is a valid status byte but the message length is
  66. // unpredictable. For the latter case, only SysEx can be accepted.
  67. if (target_len > 0 || next == kSysExByte) {
  68. next_message_.push_back(next);
  69. }
  70. // Consume |next| always, since if |next| isn't status byte, which means
  71. // that |next| is just corrupted data, or a data byte followed by
  72. // reserved message, which we are unable to understand and deal with
  73. // anyway.
  74. queue_.pop_front();
  75. continue;
  76. }
  77. const uint8_t status_byte = next_message_.front();
  78. // If we receive a new non-data byte before completing the pending message,
  79. // drop the pending message and respin the loop to re-evaluate |next|.
  80. // This also clears the running status byte speculatively added above, as
  81. // well as any broken incomplete messages.
  82. if (!IsDataByte(next) &&
  83. !(status_byte == kSysExByte && next == kEndOfSysExByte)) {
  84. next_message_.clear();
  85. continue;
  86. }
  87. // OK to consume this byte.
  88. next_message_.push_back(next);
  89. queue_.pop_front();
  90. }
  91. }
  92. } // namespace midi