summaryrefslogtreecommitdiffhomepage
path: root/MIDI.h
diff options
context:
space:
mode:
Diffstat (limited to 'MIDI.h')
-rw-r--r--MIDI.h169
1 files changed, 169 insertions, 0 deletions
diff --git a/MIDI.h b/MIDI.h
new file mode 100644
index 0000000..f0175bc
--- /dev/null
+++ b/MIDI.h
@@ -0,0 +1,169 @@
+#pragma once
+
+#include "config.h"
+
+#include <boost/signals2.hpp>
+
+#include <iostream>
+
+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;
+ }
+ }
+
+ ~MIDI()
+ {
+ free(pfd);
+ }
+
+ boost::signals2::signal<void()> 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";
+ printf("[%d] Note %s: %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)
+ {
+ printf("[%d] 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)
+ {
+ printf("[%d] Clock\n", ((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_REAL) ? ev->time.time.tv_sec : ev->time.tick);
+ }
+ else
+ {
+ printf("[%d] Unknown: Unhandled Event Received\n", ev->time.tick);
+ }
+ }
+
+ 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;
+};
+