Browse Source

add cli option to connect to midi device

main
cinnaboot 3 years ago
parent
commit
be3315e5e3
  1. 80
      src/main.cpp
  2. 80
      src/midi.cpp
  3. 10
      src/midi.h

80
src/main.cpp

@ -1,4 +1,8 @@
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <linux/spi/spi.h>
#include "spi.h"
@ -15,6 +19,21 @@
#define MAX_DAC_INPUT 4095 // 2^12 - 1
void
showUsage(const char* prog_name)
{
printf("Usage: %s [options]\n", prog_name);
printf(" options:\n");
printf(" -t: cycle test voltages on DAC\n");
printf(" -k sequencer-port: attempt to connect to port \n");
printf(" -f midi-file: play the first track, first channel of a MIDI file\n");
printf(" -h: show this help \n");
printf(" example:\n");
printf(" %s -k 20:0\n", prog_name);
printf(" attempt to connect to MIDI input device on channel:port 20:0\n");
}
u32
convertNoteToDACInput(u8 midi_note)
{
@ -28,11 +47,34 @@ convertNoteToDACInput(u8 midi_note)
return dac;
}
// TODO: add an argument option that slowly cycles between 0-1V for tuning an
// oscillator
void
loopMIDI(alsa_sequencer* seq, spi_connection* spi)
{
while(1)
{
printf("\n--------------------\n");
snd_seq_event_t *ev = nullptr;
snd_seq_event_input(seq->seq_handle, &ev);
i32 note = midiProcess(ev);
if (note >= 0) {
i32 dac_in = convertNoteToDACInput(note);
// TODO: could check return value here for SPI error
spiSendVoltage(spi, dac_in);
}
}
}
i32
main()
main(i32 argc, char* argv[])
{
if (argc == 1) {
showUsage(argv[0]);
exit(0);
}
i32 opt = 0;
alsa_sequencer seq = {0};
u8 spi_buf[SPI_BUFFER_LENGTH] = {0};
spi_connection spi = spiInit(SPI_SPEED, MAX_DAC_INPUT, spi_buf, SPI_BUFFER_LENGTH, SPI_FLAGS, SPI_CHANNEL);
@ -45,20 +87,30 @@ main()
if (!midiOpen(&seq))
return 1;
while(1)
{
printf("\n--------------------\n");
snd_seq_event_t *ev = nullptr;
snd_seq_event_input(seq.seq_handle, &ev);
i32 note = midiProcess(ev);
if (note > 0) {
i32 dac_in = convertNoteToDACInput(note);
while ((opt = getopt(argc, argv, "tk:f:h")) != -1) {
switch (opt) {
case 'f':
printf("TODO: parse and play sequence from midi file\n");
break;
case 'k':
if (!midiConnect(&seq, optarg)) {
printf("Error connecting to MIDI device, %s\n", optarg);
exit(1);
}
// TODO: could check return value here for SPI error
spiSendVoltage(&spi, dac_in);
loopMIDI(&seq, &spi);
break;
case 't':
printf("TODO: cycle test voltages to SPI DAC\n");
break;
case 'h':
default:
showUsage(argv[0]);
exit(1);
}
}
midiClose(&seq);
return spiClose(spi.fd);
}

80
src/midi.cpp

@ -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;
}

10
src/midi.h

@ -9,10 +9,16 @@
struct alsa_sequencer
{
snd_seq_t* seq_handle;
i32 in_port;
snd_seq_port_subscribe_t* subscription;
snd_seq_addr_t src;
snd_seq_addr_t dest;
};
bool midiOpen(alsa_sequencer* handles);
bool midiOpen(alsa_sequencer* seq);
bool midiConnect(alsa_sequencer* seq, const char* midi_device);
i32 midiProcess(const snd_seq_event_t* ev);
i32 midiClose(alsa_sequencer* seq);

Loading…
Cancel
Save