openMSX
MidiOutDevice.cc
Go to the documentation of this file.
1#include "MidiOutDevice.hh"
2#include "unreachable.hh"
3
4
5namespace openmsx {
6
7static constexpr uint8_t MIDI_MSG_SYSEX = 0xF0;
8static constexpr uint8_t MIDI_MSG_SYSEX_END = 0xF7;
9static constexpr uint8_t MIDI_MSG_RESET = 0xFF;
10
13[[nodiscard]] static constexpr 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
49std::string_view MidiOutDevice::getClass() const
50{
51 return "midi out";
52}
53
55{
56 buffer.clear();
57 isSysEx = false;
58}
59
60void 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{
123}
124
126{
127 // ignore
128}
129
131{
132 // ignore
133}
134
135void MidiOutDevice::setParityBit(bool /*enable*/, ParityBit /*parity*/)
136{
137 // ignore
138}
139
140} // namespace openmsx
void clearBuffer()
Discard any buffered partial MIDI message.
virtual void recvMessage(const std::vector< uint8_t > &message, EmuTime::param time)
Called when a full MIDI message is ready to be sent.
static constexpr size_t MAX_MESSAGE_SIZE
The limit for the amount of data we'll put into one MIDI message.
void setStopBits(StopBits bits) override
void setParityBit(bool enable, ParityBit parity) override
std::string_view getClass() const final
A pluggable belongs to a certain class.
void setDataBits(DataBits bits) override
void recvByte(byte value, EmuTime::param time) override
This file implemented 3 utility functions:
Definition Autofire.cc:11
#define UNREACHABLE