diff options
author | Roland Reichwein <mail@reichwein.it> | 2025-01-03 10:31:38 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2025-01-03 10:31:38 +0100 |
commit | 4af400141af0c97c4e4bcd47acf78107a17eafbe (patch) | |
tree | cd919376466679f4177d68cf6c14bdf9a0f80595 /MIDI.h | |
parent | 7b9e5cb03b530458b7f9df1fee678e2ba3a746da (diff) |
Separated class files
Diffstat (limited to 'MIDI.h')
-rw-r--r-- | MIDI.h | 169 |
1 files changed, 169 insertions, 0 deletions
@@ -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; +}; + |