diff options
author | Roland Reichwein <mail@reichwein.it> | 2025-01-04 11:28:20 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2025-01-04 11:28:20 +0100 |
commit | b2c35cdf69a9084806ac7930cf4475980d596cf6 (patch) | |
tree | b74b8f2ee2c66c59f7385407cfc34c2a0a16961f /MIDI.cpp | |
parent | aaafcea7e26791acbf5b9612e3fb396edcdfcc8f (diff) |
Separate out implementation from headers
Diffstat (limited to 'MIDI.cpp')
-rw-r--r-- | MIDI.cpp | 171 |
1 files changed, 171 insertions, 0 deletions
@@ -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"); + } +} + |