27 snd_seq_client_info_t& cinfo, snd_seq_port_info_t& pinfo);
37 [[nodiscard]] std::string_view
getName()
const override;
42 const std::vector<uint8_t>& message, EmuTime::param time)
override;
44 template<
typename Archive>
45 void serialize(Archive& ar,
unsigned version);
53 snd_midi_event_t* event_parser;
59 bool connected =
false;
64 snd_seq_client_info_t& cinfo, snd_seq_port_info_t& pinfo)
66 , destClient(snd_seq_port_info_get_client(&pinfo))
67 , destPort(snd_seq_port_info_get_port(&pinfo))
68 , name(snd_seq_client_info_get_name(&cinfo))
69 , desc(snd_seq_port_info_get_name(&pinfo))
90void MidiOutALSA::connect()
92 sourcePort = snd_seq_create_simple_port(
93 &seq,
"MIDI out pluggable",
94 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
97 "Failed to create ALSA port: ", snd_strerror(sourcePort));
100 if (
int err = snd_seq_connect_to(&seq, sourcePort, destClient, destPort)) {
101 snd_seq_delete_simple_port(&seq, sourcePort);
103 "Failed to connect to ALSA port "
104 "(", destClient,
':', destPort,
")"
105 ": ", snd_strerror(err));
113void MidiOutALSA::disconnect()
115 snd_midi_event_free(event_parser);
116 snd_seq_disconnect_to(&seq, sourcePort, destClient, destPort);
117 snd_seq_delete_simple_port(&seq, sourcePort);
133 const std::vector<uint8_t>& message, EmuTime::param )
136 snd_seq_ev_clear(&ev);
139 snd_seq_ev_set_source(&ev, narrow_cast<uint8_t>(sourcePort));
140 snd_seq_ev_set_subs(&ev);
143 if (
auto encodeLen = snd_midi_event_encode(
144 event_parser, message.data(), narrow<long>(message.size()), &ev);
146 std::cerr <<
"Error encoding MIDI message of type "
147 << std::hex << int(message[0]) << std::dec
148 <<
": " << snd_strerror(narrow<int>(encodeLen)) <<
'\n';
151 if (ev.type == SND_SEQ_EVENT_NONE) {
152 std::cerr <<
"Incomplete MIDI message of type "
153 << std::hex << int(message[0]) << std::dec <<
'\n';
158 snd_seq_ev_set_direct(&ev);
159 if (
int err = snd_seq_event_output(&seq, &ev); err < 0) {
160 std::cerr <<
"Error sending MIDI event: "
161 << snd_strerror(err) <<
'\n';
163 snd_seq_drain_output(&seq);
166template<
typename Archive>
169 if constexpr (Archive::IS_LOADER) {
182 snd_seq_client_info_t& cinfo, snd_seq_port_info_t& pinfo);
188 [[nodiscard]] std::string_view
getName()
const override;
192 void signal(EmuTime::param time)
override;
194 template<
typename Archive>
195 void serialize(Archive& ar,
unsigned version);
203 bool signalEvent(
const Event& event)
override;
210 snd_midi_event_t* event_parser;
211 int destinationPort = -1;
216 bool connected =
false;
226 snd_seq_client_info_t& cinfo, snd_seq_port_info_t& pinfo)
227 : eventDistributor(eventDistributor_)
228 , scheduler(scheduler_)
230 , srcClient(snd_seq_port_info_get_client(&pinfo))
231 , srcPort(snd_seq_port_info_get_port(&pinfo))
232 , name(snd_seq_client_info_get_name(&cinfo))
233 , desc(snd_seq_port_info_get_name(&pinfo))
236 if (snd_seq_port_info_get_capability(&pinfo) & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) {
237 name.append(
" (input)");
251 auto& midiConnector = checked_cast<MidiInConnector&>(connector_);
268void MidiInALSA::connect()
270 destinationPort = snd_seq_create_simple_port(
271 &seq,
"MIDI in pluggable",
272 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
273 if (destinationPort < 0) {
275 "Failed to create ALSA port: ", snd_strerror(destinationPort));
278 if (
int err = snd_seq_connect_from(&seq, destinationPort, srcClient, srcPort)) {
279 snd_seq_delete_simple_port(&seq, destinationPort);
281 "Failed to connect to ALSA port "
282 "(", srcClient,
':', srcPort,
")"
283 ": ", snd_strerror(err));
287 snd_midi_event_no_status(event_parser, 1);
292 thread = std::thread([
this]() { run(); });
295void MidiInALSA::disconnect()
300 snd_midi_event_free(event_parser);
301 snd_seq_disconnect_from(&seq, destinationPort, srcClient, srcPort);
302 snd_seq_delete_simple_port(&seq, destinationPort);
307void MidiInALSA::run()
311 auto npfd = snd_seq_poll_descriptors_count(&seq, POLLIN);
312 std::vector<struct pollfd> pfd(npfd);
313 snd_seq_poll_descriptors(&seq, pfd.data(), npfd, POLLIN);
316 if (poll(pfd.data(), npfd, 1000) > 0) {
317 snd_seq_event_t *ev =
nullptr;
318 if (
auto err = snd_seq_event_input(&seq, &ev); err < 0) {
319 std::cerr <<
"Error receiving MIDI event: "
320 << snd_strerror(err) <<
'\n';
325 if (ev->type == SND_SEQ_EVENT_SYSEX) {
326 std::scoped_lock lock(mutex);
327 for (
auto i :
xrange(ev->data.ext.len)) {
328 queue.
push_back(
static_cast<uint8_t*
>(ev->data.ext.ptr)[i]);
332 std::array<uint8_t, 12> bytes = {};
333 auto size = snd_midi_event_decode(event_parser, bytes.data(), bytes.size(), ev);
335 std::cerr <<
"Error decoding MIDI event: "
336 << snd_strerror(
int(size)) <<
'\n';
337 snd_seq_free_event(ev);
341 std::scoped_lock lock(mutex);
346 snd_seq_free_event(ev);
354 auto* conn = checked_cast<MidiInConnector*>(
getConnector());
355 if (!conn->acceptsData()) {
356 std::scoped_lock lock(mutex);
360 if (!conn->ready())
return;
364 std::scoped_lock lock(mutex);
365 if (queue.
empty())
return;
368 conn->recvByte(data, time);
372bool MidiInALSA::signalEvent(
const Event& )
377 std::scoped_lock lock(mutex);
393template<
typename Archive>
396 if constexpr (Archive::IS_LOADER) {
406std::unique_ptr<MidiSessionALSA> MidiSessionALSA::instance;
415 if (
int err = snd_seq_open(&seq,
"default", SND_SEQ_OPEN_DUPLEX, 0);
418 "Could not open sequencer: ", snd_strerror(err));
421 snd_seq_set_client_name(seq,
"openMSX");
424 instance->scanClients(controller, eventDistributor, scheduler);
439void MidiSessionALSA::scanClients(
445 snd_seq_client_info_t* cInfo;
446 snd_seq_client_info_alloca(&cInfo);
447 snd_seq_client_info_set_client(cInfo, -1);
448 while (snd_seq_query_next_client(&seq, cInfo) >= 0) {
449 int client = snd_seq_client_info_get_client(cInfo);
450 if (client == SND_SEQ_CLIENT_SYSTEM) {
456 snd_seq_port_info_t* pInfo;
457 snd_seq_port_info_alloca(&pInfo);
458 snd_seq_port_info_set_client(pInfo, client);
459 snd_seq_port_info_set_port(pInfo, -1);
460 while (snd_seq_query_next_port(&seq, pInfo) >= 0) {
461 if (
unsigned type = snd_seq_port_info_get_type(pInfo);
462 !(type & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) {
465 constexpr unsigned wrCaps =
466 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
467 constexpr unsigned rdCaps =
468 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
469 if ((snd_seq_port_info_get_capability(pInfo) & wrCaps) == wrCaps) {
471 seq, *cInfo, *pInfo));
473 if ((snd_seq_port_info_get_capability(pInfo) & rdCaps) == rdCaps) {
475 eventDistributor, scheduler, seq, *cInfo, *pInfo));
This implements a queue on top of circular_buffer (not part of boost).
void printError(std::string_view message)
Represents something you can plug devices into.
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.
std::string_view getDescription() const override
Description for this pluggable.
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
MidiInALSA(EventDistributor &eventDistributor, Scheduler &scheduler, snd_seq_t &seq, snd_seq_client_info_t &cinfo, snd_seq_port_info_t &pinfo)
void unplugHelper(EmuTime::param time) override
void signal(EmuTime::param time) override
MidiOutALSA & operator=(const MidiOutALSA &)=delete
void serialize(Archive &ar, unsigned version)
MidiOutALSA(snd_seq_t &seq, snd_seq_client_info_t &cinfo, snd_seq_port_info_t &pinfo)
MidiOutALSA(const MidiOutALSA &)=delete
MidiOutALSA & operator=(MidiOutALSA &&)=delete
void plugHelper(Connector &connector, EmuTime::param time) override
std::string_view getName() const override
Name used to identify this pluggable.
void recvMessage(const std::vector< uint8_t > &message, EmuTime::param time) override
Called when a full MIDI message is ready to be sent.
MidiOutALSA(MidiOutALSA &&)=delete
void unplugHelper(EmuTime::param time) override
std::string_view getDescription() const override
Description for this pluggable.
Pluggable that connects an MSX MIDI out port to a host MIDI device.
static constexpr size_t MAX_MESSAGE_SIZE
The limit for the amount of data we'll put into one MIDI message.
Lists ALSA MIDI ports we can connect to.
MidiSessionALSA(const MidiSessionALSA &)=delete
static void registerAll(PluggingController &controller, CliComm &cliComm, EventDistributor &eventDistributor, Scheduler &scheduler)
Thrown when a plug action fails.
bool isPluggedIn() const
Returns true if this pluggable is currently plugged into a connector.
void setConnector(Connector *conn)
Connector * getConnector() const
Get the connector this Pluggable is plugged into.
Central administration of Connectors and Pluggables.
void registerPluggable(std::unique_ptr< Pluggable > pluggable)
Add a Pluggable to the registry.
EmuTime::param getCurrentTime() const
Get the current scheduler time.
This file implemented 3 utility functions:
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
size_t size(std::string_view utf8)
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
constexpr auto xrange(T e)