parse midi messages with ALSA sequencer API, and output SPI message to MCP4921 DAC for controlling an analog synthesizer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

141 lines
3.2 KiB

#include <cassert>
#include <cmath>
#include <string>
#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;
}
alsa_note_event
midiProcess(const snd_seq_event_t* ev)
{
assert(ev != nullptr);
alsa_note_event nev = {0};
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), channel: %d\n",
ev->time.tick,
type,
ev->data.note.note,
ev->data.note.note,
ev->data.note.velocity,
ev->data.note.channel);
nev.note = ev->data.note.note;
nev.channel = ev->data.note.channel;
nev.note_on = (ev->type == SND_SEQ_EVENT_NOTEON) ? true : false;
}
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 nev;
}
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;
}