#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)) { throw std::runtime_error("MIDI port couldn't be connected"); } } else { 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", 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: {}", timestamp_from_event(ev), ev->type) << std::endl; } signal_count_events(snd_seq_event_length(ev)); //log_cout << fmt::format("MIDI Bytes: {}", snd_seq_event_length(ev)) << 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"); } }