OXYGENHUB
◆ main openmsx / src / serial / MidiOutDevice.cc
3.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "MidiOutDevice.hh"
2
3
#include "unreachable.hh"
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
11
/** Returns the size in bytes of a message that starts with the given status.
12
  */
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.
28
			return MidiOutDevice::MAX_MESSAGE_SIZE;
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
49
zstring_view MidiOutDevice::getClass() const
50
{
51
	return "midi out";
52
}
53
54
void MidiOutDevice::clearBuffer()
55
{
56
	buffer.clear();
57
	isSysEx = false;
58
}
59
60
void MidiOutDevice::recvByte(uint8_t value, EmuTime 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
119
void MidiOutDevice::recvMessage(
120
		const std::vector<uint8_t>& /*message*/, EmuTime /*time*/)
121
{
122
	UNREACHABLE;
123
}
124
125
void MidiOutDevice::setDataBits(DataBits /*bits*/)
126
{
127
	// ignore
128
}
129
130
void MidiOutDevice::setStopBits(StopBits /*bits*/)
131
{
132
	// ignore
133
}
134
135
void MidiOutDevice::setParityBit(bool /*enable*/, Parity /*parity*/)
136
{
137
	// ignore
138
}
139
140
} // namespace openmsx