|
|
|
|
@ -1,9 +1,18 @@
|
|
|
|
|
|
|
|
|
|
#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) |
|
|
|
|
{ |
|
|
|
|
@ -12,27 +21,48 @@ midiOpen(alsa_sequencer* seq)
|
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (snd_seq_set_client_name(seq->seq_handle, "Midi Listener")) { |
|
|
|
|
if (snd_seq_set_client_name(seq->seq_handle, "midi_to_cv")) { |
|
|
|
|
printf("Could not set client name\n"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
seq->in_port = snd_seq_create_simple_port(seq->seq_handle, |
|
|
|
|
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->in_port < 0) { |
|
|
|
|
if (seq->dest.port < 0) { |
|
|
|
|
printf("Could not open port\n"); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
i32 client_id = snd_seq_client_id(seq->seq_handle); |
|
|
|
|
printf("client id: %d, MIDI port: %d\n", client_id, seq->in_port); |
|
|
|
|
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) |
|
|
|
|
{ |
|
|
|
|
@ -64,3 +94,43 @@ midiProcess(const snd_seq_event_t* ev)
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|