openMSX
MidiInReader.cc
Go to the documentation of this file.
1#include "MidiInReader.hh"
2#include "MidiInConnector.hh"
3#include "PlugException.hh"
4#include "EventDistributor.hh"
5#include "Scheduler.hh"
6#include "FileOperations.hh"
7#include "checked_cast.hh"
8#include "serialize.hh"
9#include <array>
10#include <cstdio>
11#include <cerrno>
12#include <cstring>
13
14namespace openmsx {
15
17 Scheduler& scheduler_,
18 CommandController& commandController)
19 : eventDistributor(eventDistributor_), scheduler(scheduler_)
20 , readFilenameSetting(
21 commandController, "midi-in-readfilename",
22 "filename of the file where the MIDI input is read from",
23 "/dev/midi")
24{
25 eventDistributor.registerEventListener(EventType::MIDI_IN_READER, *this);
26}
27
32
33// Pluggable
34void MidiInReader::plugHelper(Connector& connector_, EmuTime::param /*time*/)
35{
36 file = FileOperations::openFile(readFilenameSetting.getString(), "rb");
37 if (!file) {
38 throw PlugException("Failed to open input: ", strerror(errno));
39 }
40
41 auto& midiConnector = checked_cast<MidiInConnector&>(connector_);
42 midiConnector.setDataBits(SerialDataInterface::DataBits::D8); // 8 data bits
43 midiConnector.setStopBits(SerialDataInterface::StopBits::S1); // 1 stop bit
44 midiConnector.setParityBit(false, SerialDataInterface::Parity::EVEN); // no parity
45
46 setConnector(&connector_); // base class will do this in a moment,
47 // but thread already needs it
48 poller.reset();
49 thread = std::thread([this]() { run(); });
50}
51
52void MidiInReader::unplugHelper(EmuTime::param /*time*/)
53{
54 poller.abort();
55 thread.join();
56 file.reset();
57}
58
59std::string_view MidiInReader::getName() const
60{
61 return "midi-in-reader";
62}
63
64std::string_view MidiInReader::getDescription() const
65{
66 return "MIDI in file reader. Sends data from an input file to the "
67 "MIDI port it is connected to. The filename is set with "
68 "the 'midi-in-readfilename' setting.";
69}
70
71
72void MidiInReader::run()
73{
74 if (!file) return;
75 while (true) {
76#ifndef _WIN32
77 if (poller.poll(fileno(file.get()))) {
78 break;
79 }
80#endif
81 std::array<uint8_t, 1> buf;
82 size_t num = fread(buf.data(), sizeof(uint8_t), buf.size(), file.get());
83 if (poller.aborted()) {
84 break;
85 }
86 if (num != 1) {
87 continue;
88 }
89 assert(isPluggedIn());
90
91 {
92 std::scoped_lock lock(mutex);
93 queue.push_back(buf[0]);
94 }
95 eventDistributor.distributeEvent(MidiInReaderEvent());
96 }
97}
98
99// MidiInDevice
100void MidiInReader::signal(EmuTime::param time)
101{
102 auto* conn = checked_cast<MidiInConnector*>(getConnector());
103 if (!conn->acceptsData()) {
104 std::scoped_lock lock(mutex);
105 queue.clear();
106 return;
107 }
108 if (!conn->ready()) {
109 return;
110 }
111
112 uint8_t data;
113 {
114 std::scoped_lock lock(mutex);
115 if (queue.empty()) return;
116 data = queue.pop_front();
117 }
118 conn->recvByte(data, time);
119}
120
121// EventListener
122bool MidiInReader::signalEvent(const Event& /*event*/)
123{
124 if (isPluggedIn()) {
125 signal(scheduler.getCurrentTime());
126 } else {
127 std::scoped_lock lock(mutex);
128 queue.clear();
129 }
130 return false;
131}
132
133
134template<typename Archive>
135void MidiInReader::serialize(Archive& /*ar*/, unsigned /*version*/)
136{
137 // don't try to resume a previous logfile (see PrinterPortLogger)
138}
141
142} // namespace openmsx
bool empty() const
void push_back(U &&u)
Represents something you can plug devices into.
Definition Connector.hh:21
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
void registerEventListener(EventType type, EventListener &listener, Priority priority=Priority::OTHER)
Registers a given object to receive certain events.
zstring_view getString() const noexcept
void serialize(Archive &ar, unsigned version)
std::string_view getName() const override
Name used to identify this pluggable.
void plugHelper(Connector &connector, EmuTime::param time) override
std::string_view getDescription() const override
Description for this pluggable.
void unplugHelper(EmuTime::param time) override
MidiInReader(EventDistributor &eventDistributor, Scheduler &scheduler, CommandController &commandController)
void signal(EmuTime::param time) override
Thrown when a plug action fails.
bool isPluggedIn() const
Returns true if this pluggable is currently plugged into a connector.
Definition Pluggable.hh:49
void setConnector(Connector *conn)
Definition Pluggable.hh:58
Connector * getConnector() const
Get the connector this Pluggable is plugged into.
Definition Pluggable.hh:43
bool poll(int fd)
Waits for an event to occur on the given file descriptor.
Definition Poller.cc:42
void reset()
Reset aborted() to false.
Definition Poller.hh:43
bool aborted() const
Returns true iff abort() was called.
Definition Poller.hh:33
void abort()
Aborts a poll in progress and any future poll attempts.
Definition Poller.cc:30
EmuTime::param getCurrentTime() const
Get the current scheduler time.
Definition Scheduler.cc:82
FILE_t openFile(zstring_view filename, zstring_view mode)
Call fopen() in a platform-independent manner.
This file implemented 3 utility functions:
Definition Autofire.cc:11
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, Rs232NetEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
Definition Event.hh:445
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
#define REGISTER_POLYMORPHIC_INITIALIZER(BASE, CLASS, NAME)