openMSX
MidiOutDevice.cc
Go to the documentation of this file.
1 #include "MidiOutDevice.hh"
2 #include "unreachable.hh"
3 
4 
5 namespace openmsx {
6 
7 static constexpr uint8_t MIDI_MSG_SYSEX = 0xF0;
8 static constexpr uint8_t MIDI_MSG_SYSEX_END = 0xF7;
9 static constexpr uint8_t MIDI_MSG_RESET = 0xFF;
10 
13 static size_t midiMessageLength(uint8_t status)
14 {
15  if (status < 0x80) {
16  assert(false);
17  return 0;
18  } else if (status < 0xC0) {
19  return 3;
20  } else if (status < 0xE0) {
21  return 2;
22  } else if (status < 0xF0) {
23  return 3;
24  } else {
25  switch (status) {
26  case MIDI_MSG_SYSEX:
27  // Limit to force sending large SysEx messages in chunks.
29  case MIDI_MSG_SYSEX_END:
30  assert(false);
31  return 0;
32  case 0xF1:
33  case 0xF3:
34  return 2;
35  case 0xF2:
36  return 3;
37  case 0xF4:
38  case 0xF5:
39  case 0xF9:
40  case 0xFD:
41  // Data size unknown
42  return 1;
43  default:
44  return 1;
45  }
46  }
47 }
48 
50 {
51  return "midi out";
52 }
53 
55 {
56  buffer.clear();
57  isSysEx = false;
58 }
59 
60 void MidiOutDevice::recvByte(byte value, EmuTime::param time)
61 {
62  if (value & 0x80) { // status byte
63  if (value == MIDI_MSG_SYSEX_END) {
64  if (isSysEx) {
65  buffer.push_back(value);
66  recvMessage(buffer, time);
67  } else {
68  // Ignoring SysEx end without start
69  }
70  buffer.clear();
71  isSysEx = false;
72  } else if (value >= 0xF8) {
73  // Realtime message, send immediately.
74  std::vector<uint8_t> realtimeMessage = { value };
75  recvMessage(realtimeMessage, time);
76  if (value == MIDI_MSG_RESET) {
77  buffer.clear();
78  }
79  return;
80  } else {
81  // Replace any message in progress.
82  if (isSysEx) {
83  // Discarding incomplete MIDI SysEx message
84  } else if (buffer.size() >= 2) {
85  #if 0
86  std::cerr << "Discarding incomplete MIDI message with status "
87  "0x" << std::hex << int(buffer[0]) << std::dec <<
88  ", at " << buffer.size() << " of " <<
89  midiMessageLength(buffer[0]) << " bytes\n";
90  #endif
91  }
92  buffer = { value };
93  isSysEx = value == MIDI_MSG_SYSEX;
94  }
95  } else { // data byte
96  if (buffer.empty() && !isSysEx) {
97  // Ignoring MIDI data without preceding status
98  } else {
99  buffer.push_back(value);
100  }
101  }
102 
103  // Is the message complete?
104  if (!buffer.empty()) {
105  uint8_t status = isSysEx ? MIDI_MSG_SYSEX : buffer[0];
106  size_t len = midiMessageLength(status);
107  if (buffer.size() >= len) {
108  recvMessage(buffer, time);
109  if (status >= 0xF0 && status < 0xF8) {
110  buffer.clear();
111  } else {
112  // Keep last status, to support running status.
113  buffer.resize(1);
114  }
115  }
116  }
117 }
118 
120  const std::vector<uint8_t>& /*message*/, EmuTime::param /*time*/)
121 {
122  UNREACHABLE;
123 }
124 
126 {
127  // ignore
128 }
129 
131 {
132  // ignore
133 }
134 
135 void MidiOutDevice::setParityBit(bool /*enable*/, ParityBit /*parity*/)
136 {
137  // ignore
138 }
139 
140 } // namespace openmsx
void setParityBit(bool enable, ParityBit parity) override
static constexpr size_t MAX_MESSAGE_SIZE
The limit for the amount of data we&#39;ll put into one MIDI message.
void setStopBits(StopBits bits) override
uint8_t byte
8 bit unsigned integer
Definition: openmsx.hh:26
string_view getClass() const final override
A pluggable belongs to a certain class.
virtual void recvMessage(const std::vector< uint8_t > &message, EmuTime::param time)
Called when a full MIDI message is ready to be sent.
void setDataBits(DataBits bits) override
void recvByte(byte value, EmuTime::param time) override
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:16
void clearBuffer()
Discard any buffered partial MIDI message.
#define UNREACHABLE
Definition: unreachable.hh:38