#include #include #include #include "midi.h" // forward declarations bool parseDeviceStr(snd_seq_addr_t& src, const char* midi_device); // interface bool midiOpen(alsa_sequencer* seq) { if (snd_seq_open(&seq->seq_handle, "default", SND_SEQ_OPEN_INPUT, 0)) { printf("Could not open sequencer\n"); return false; } if (snd_seq_set_client_name(seq->seq_handle, "midi_to_cv")) { printf("Could not set client name\n"); return false; } seq->dest.client = snd_seq_client_id(seq->seq_handle); seq->dest.port = snd_seq_create_simple_port(seq->seq_handle, "listen:in", SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); if (seq->dest.port < 0) { printf("Could not open port\n"); return false; } printf("host client id: %d, host port: %d\n", seq->dest.client, seq->dest.port); return true; } bool midiConnect(alsa_sequencer* seq, const char* midi_device) { if (!parseDeviceStr(seq->src, midi_device)) { printf("invalid device string\n"); return false; } snd_seq_port_subscribe_malloc(&seq->subscription); snd_seq_port_subscribe_set_sender(seq->subscription, &seq->src); snd_seq_port_subscribe_set_dest(seq->subscription, &seq->dest); snd_seq_port_subscribe_set_queue(seq->subscription, 1); snd_seq_port_subscribe_set_time_update(seq->subscription, 1); snd_seq_port_subscribe_set_time_real(seq->subscription, 1); if (snd_seq_subscribe_port(seq->seq_handle, seq->subscription) == 0) return true; return false; } i32 midiProcess(const snd_seq_event_t* ev) { assert(ev != nullptr); i32 note = -1; 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: %d, 0x%2x vel(%2x)\n", ev->time.tick, type, ev->data.note.note, ev->data.note.note, ev->data.note.velocity); note = ev->data.note.note; } else if(ev->type == SND_SEQ_EVENT_CONTROLLER) { printf("[%d] Control: 0x%2x val(%2x)\n", ev->time.tick, ev->data.control.param, ev->data.control.value); } else { printf("[%d] Unknown: Unhandled Event Received\n", ev->time.tick); } return note; } i32 midiClose(alsa_sequencer* seq) { snd_seq_port_subscribe_free(seq->subscription); return snd_seq_close(seq->seq_handle); } // internal bool parseDeviceStr(snd_seq_addr_t& src, const char* midi_device) { // NOTE: midi_device should be in the form 'client:port', where each number // is an unsigned char. So max string length is 'xxx:xxx" = 7 std::string s(midi_device); if (s.size() > 0 && s.size() < 8) { u32 split = s.find(':'); if (split > 0 && split != std::string::npos) { std::string sub1 = s.substr(0, split); std::string sub2 = s.substr(split + 1, s.size()); u32 client= strtoul(sub1.c_str(), nullptr, 10); u32 port = strtoul(sub2.c_str(), nullptr, 10); if (errno == 0 && client > 0 && client < 256 && port >= 0 && port < 256) { src.client = client; src.port = port; return true; } } } return false; }