summaryrefslogtreecommitdiffhomepage
path: root/MIDI.cpp
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2025-01-04 11:28:20 +0100
committerRoland Reichwein <mail@reichwein.it>2025-01-04 11:28:20 +0100
commitb2c35cdf69a9084806ac7930cf4475980d596cf6 (patch)
treeb74b8f2ee2c66c59f7385407cfc34c2a0a16961f /MIDI.cpp
parentaaafcea7e26791acbf5b9612e3fb396edcdfcc8f (diff)
Separate out implementation from headers
Diffstat (limited to 'MIDI.cpp')
-rw-r--r--MIDI.cpp171
1 files changed, 171 insertions, 0 deletions
diff --git a/MIDI.cpp b/MIDI.cpp
index 1fe49fe..5e625bd 100644
--- a/MIDI.cpp
+++ b/MIDI.cpp
@@ -1,2 +1,173 @@
#include "MIDI.h"
+MIDI::MIDI()
+{
+ if (0 > snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK))
+ {
+ throw std::runtime_error("MIDI sequencer couldn't be opened");
+ }
+
+ if (0 > snd_seq_set_client_name(seq_handle, "Midi Listener"))
+ {
+ throw std::runtime_error("MIDI client name couldn't be set");
+ }
+
+ if (0 > ((in_port = snd_seq_create_simple_port(seq_handle, "24:0",
+ SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
+ SND_SEQ_PORT_TYPE_APPLICATION))))
+ {
+ throw std::runtime_error("MIDI port couldn't be opened");
+ }
+
+ debug_cout << "in_port: " << std::to_string(in_port) << std::endl;
+
+#if 1
+ snd_seq_addr_t sender, dest;
+ snd_seq_port_subscribe_t *subs;
+ sender.client = 24;
+ sender.port = 0;
+ dest.client = snd_seq_client_id(seq_handle);
+ dest.port = in_port;
+ snd_seq_port_subscribe_alloca(&subs);
+ snd_seq_port_subscribe_set_sender(subs, &sender);
+ snd_seq_port_subscribe_set_dest(subs, &dest);
+ snd_seq_port_subscribe_set_queue(subs, 1);
+ snd_seq_port_subscribe_set_time_update(subs, 1);
+ snd_seq_port_subscribe_set_time_real(subs, 1);
+ // TODO: fix timestamp (currently always 0)
+ if (0 > snd_seq_subscribe_port(seq_handle, subs))
+#endif
+ //if (0 > snd_seq_connect_from(seq_handle, in_port, 24, 0))
+ {
+ throw std::runtime_error("MIDI port couldn't be connected");
+ }
+
+ npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
+ if (npfd < 0) {
+ throw std::runtime_error("snd_seq_poll_descriptors_count() failed");
+ }
+ pfd = (struct pollfd *)malloc(npfd * sizeof(struct pollfd));
+ if (pfd == nullptr) {
+ throw std::runtime_error("alloca() error for MIDI");
+ }
+ if (0 > snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN))
+ {
+ throw std::runtime_error("snd_seq_poll_descriptors() failure");
+ }
+
+ if (npfd != 1) {
+ std::cout << "Warning: " << std::to_string(npfd) << " poll fds for MIDI" << std::endl;
+ } else if (fd() <= 2) {
+ std::cout << "Warning: Bad MIDI fd: " << std::to_string(fd()) << std::endl;
+ }
+}
+
+MIDI::~MIDI()
+{
+ free(pfd);
+}
+
+int MIDI::fd()
+{
+ return pfd->fd;
+}
+
+bool MIDI::event_ready()
+{
+ int result = snd_seq_event_input_pending(seq_handle, 1);
+ if (result < 0) {
+ throw std::runtime_error("snd_seq_event_input_pending() failed");
+ }
+
+ return result > 0;
+}
+
+snd_seq_event_t* MIDI::read(void)
+{
+ snd_seq_event_t *ev = NULL;
+ if (0 > snd_seq_event_input(seq_handle, &ev))
+ {
+ std::cerr << "snd_seq_event_input(): -EAGAIN" << std::endl;
+ }
+
+ return ev;
+}
+
+namespace {
+uint64_t timestamp_from_event(const snd_seq_event_t *ev)
+{
+ return ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick;
+}
+}
+
+// returns if click starts
+void MIDI::process(snd_seq_event_t *ev)
+{
+ uint64_t timestamp = timestamp_from_event(ev); // original timestamp
+
+ // TODO: fix timestamp to be set automatically
+ struct timespec t;
+ if (TIME_UTC != timespec_get(&t, TIME_UTC))
+ {
+ log_cout << "Error: timespec_get()" << std::endl;
+ }
+ else
+ {
+ ev->time.time.tv_sec = t.tv_sec;
+ ev->time.time.tv_nsec = t.tv_nsec;
+ }
+
+ if ((ev->type == SND_SEQ_EVENT_NOTEON) ||(ev->type == SND_SEQ_EVENT_NOTEOFF)) {
+ const char *type = (ev->type == SND_SEQ_EVENT_NOTEON) ? "on " : "off";
+ debug_cout << fmt::format("[{}] Note {}: {:2x} vel({:2x}), ch {:2x}\n", timestamp,
+ type,
+ ev->data.note.note,
+ ev->data.note.velocity,
+ ev->data.control.channel);
+ if (ev->type == SND_SEQ_EVENT_NOTEON) {
+ signal_note(ev->data.control.channel, ev->data.note.note, timestamp);
+ }
+ }
+ else if (ev->type == SND_SEQ_EVENT_CONTROLLER)
+ {
+ debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", timestamp_from_event(ev),
+ ev->data.control.param,
+ ev->data.control.value);
+ }
+ else if (ev->type == SND_SEQ_EVENT_SENSING)
+ {
+ signal_active_sensing();
+ debug_cout << fmt::format("[{}] Active Sensing\n", timestamp_from_event(ev)) << std::endl;
+ }
+ else if (ev->type == SND_SEQ_EVENT_CLOCK)
+ {
+ signal_clock();
+ debug_cout << fmt::format("[{}] Clock\n", timestamp_from_event(ev)) << std::endl;
+ }
+ else
+ {
+ log_cout << fmt::format("[{}] Unknown MIDI event: {}\n", timestamp_from_event(ev), ev->type) << std::endl;
+ }
+}
+
+void MIDI::flush()
+{
+ while (event_ready()) {
+ (void) read();
+ }
+}
+
+void MIDI::wait_for_event()
+{
+ int result = poll(pfd, npfd, 10000);
+ if (result > 0)
+ {
+ // event
+ }
+ else if (result == 0) {
+ // timeout
+ } else {
+ throw std::runtime_error("Poll unsuccessful");
+ }
+}
+