#pragma once #include "config.h" #include "debug.h" #include #include #include class MIDI { public: 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"); } std::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() { free(pfd); } boost::signals2::signal signal_click; int fd() { return pfd->fd; } bool 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 *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; } // TODO: fix timestamp to be set automatically struct timespec t; if (TIME_UTC != timespec_get(&t, TIME_UTC)) { std::cerr << "Error: timespec_get()" << std::endl; } else { ev->time.time.tv_sec = t.tv_sec; ev->time.time.tv_nsec = t.tv_nsec; } return ev; } // returns if click starts void process(const snd_seq_event_t *ev) { 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", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick, type, ev->data.note.note, ev->data.note.velocity, ev->data.control.channel); if (ev->type == SND_SEQ_EVENT_NOTEON) { if (true || (ev->data.note.note == CLICK_NOTE && ev->data.control.channel == CLICK_CHANNEL)) { signal_click(); } } } else if(ev->type == SND_SEQ_EVENT_CONTROLLER) { debug_cout << fmt::format("[{}] Control: {:2x} val({:2x})\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick, ev->data.control.param, ev->data.control.value); } else if(ev->type == SND_SEQ_EVENT_CLOCK) { debug_cout << fmt::format("[{}] Clock\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick) << std::endl;; } else { debug_cout << fmt::format("[{}] Unknown: Unhandled Event Received\n", ev->time.tick) << std::endl;; } } void 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"); } } private: snd_seq_t *seq_handle; int in_port; int npfd; struct pollfd* pfd; };