OXYGENHUB
◆ main openmsx / src / serial / MidiInReader.cc
3.4 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "MidiInReader.hh"
2
3
#include "MidiInConnector.hh"
4
5
#include "EventDistributor.hh"
6
#include "FileOperations.hh"
7
#include "PlugException.hh"
8
#include "Scheduler.hh"
9
#include "serialize.hh"
10
11
#include "checked_cast.hh"
12
13
#include <array>
14
#include <cerrno>
15
#include <cstdio>
16
#include <cstring>
17
18
namespace openmsx {
19
20
MidiInReader::MidiInReader(EventDistributor& eventDistributor_,
21
                           Scheduler& scheduler_,
22
                           CommandController& commandController)
23
	: eventDistributor(eventDistributor_), scheduler(scheduler_)
24
	, readFilenameSetting(
25
		commandController, "midi-in-readfilename",
26
		"filename of the file where the MIDI input is read from",
27
		"/dev/midi")
28
{
29
	eventDistributor.registerEventListener(EventType::MIDI_IN_READER, *this);
30
}
31
32
MidiInReader::~MidiInReader()
33
{
34
	eventDistributor.unregisterEventListener(EventType::MIDI_IN_READER, *this);
35
}
36
37
// Pluggable
38
void MidiInReader::plugHelper(Connector& connector_, EmuTime /*time*/)
39
{
40
	file = FileOperations::openFile(readFilenameSetting.getString(), "rb");
41
	if (!file) {
42
		throw PlugException("Failed to open input: ", strerror(errno));
43
	}
44
45
	auto& midiConnector = checked_cast<MidiInConnector&>(connector_);
46
	midiConnector.setDataBits(SerialDataInterface::DataBits::D8); // 8 data bits
47
	midiConnector.setStopBits(SerialDataInterface::StopBits::S1); // 1 stop bit
48
	midiConnector.setParityBit(false, SerialDataInterface::Parity::EVEN); // no parity
49
50
	setConnector(&connector_); // base class will do this in a moment,
51
	                           // but thread already needs it
52
	poller.emplace();
53
	thread = std::thread([this]() { run(); });
54
}
55
56
void MidiInReader::unplugHelper(EmuTime /*time*/)
57
{
58
	poller->abort();
59
	thread.join();
60
	poller.reset();
61
	file.reset();
62
}
63
64
zstring_view MidiInReader::getName() const
65
{
66
	return "midi-in-reader";
67
}
68
69
zstring_view MidiInReader::getDescription() const
70
{
71
	return "MIDI in file reader. Sends data from an input file to the "
72
	       "MIDI port it is connected to. The filename is set with "
73
	       "the 'midi-in-readfilename' setting.";
74
}
75
76
77
void MidiInReader::run()
78
{
79
	if (!file) return;
80
	while (true) {
81
#ifndef _WIN32
82
		if (poller->poll(fileno(file.get()))) {
83
			break;
84
		}
85
#endif
86
		std::array<uint8_t, 1> buf;
87
		size_t num = fread(buf.data(), sizeof(uint8_t), buf.size(), file.get());
88
		if (poller->aborted()) {
89
			break;
90
		}
91
		if (num != 1) {
92
			continue;
93
		}
94
		assert(isPluggedIn());
95
96
		{
97
			std::scoped_lock lock(mutex);
98
			queue.push_back(buf[0]);
99
		}
100
		eventDistributor.distributeEvent(MidiInReaderEvent());
101
	}
102
}
103
104
// MidiInDevice
105
void MidiInReader::signal(EmuTime time)
106
{
107
	auto* conn = checked_cast<MidiInConnector*>(getConnector());
108
	if (!conn->acceptsData()) {
109
		std::scoped_lock lock(mutex);
110
		queue.clear();
111
		return;
112
	}
113
	if (!conn->ready()) {
114
		return;
115
	}
116
117
	uint8_t data;
118
	{
119
		std::scoped_lock lock(mutex);
120
		if (queue.empty()) return;
121
		data = queue.pop_front();
122
	}
123
	conn->recvByte(data, time);
124
}
125
126
// EventListener
127
bool MidiInReader::signalEvent(const Event& /*event*/)
128
{
129
	if (isPluggedIn()) {
130
		signal(scheduler.getCurrentTime());
131
	} else {
132
		std::scoped_lock lock(mutex);
133
		queue.clear();
134
	}
135
	return false;
136
}
137
138
139
template<typename Archive>
140
void MidiInReader::serialize(Archive& /*ar*/, unsigned /*version*/)
141
{
142
	// don't try to resume a previous logfile (see PrinterPortLogger)
143
}
144
INSTANTIATE_SERIALIZE_METHODS(MidiInReader);
145
REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MidiInReader, "MidiInReader");
146
147
} // namespace openmsx