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.
124 lines
2.7 KiB
124 lines
2.7 KiB
|
|
// TODO: gate on/off controls, can just use hardware GPIO in digital mode |
|
// TODO: update README with wiring instructions for SPI to DAC |
|
// TODO: DAC voltage stepping |
|
// if we use DAC input 4080 = 5V, then we can do steps of 1/12V on exactly |
|
// the correct DAC value. Would then need to set Vref to: |
|
// DAC: 5V = 4080 / 4095 * Vref, Vref ~= 5.0184 |
|
|
|
|
|
#include <cstdio> |
|
#include <cstdlib> |
|
|
|
#include <unistd.h> |
|
#include <linux/spi/spi.h> |
|
|
|
#include "spi.h" |
|
#include "midi.h" |
|
|
|
|
|
#define SPI_SPEED 1000000 |
|
#define SPI_BUFFER_LENGTH 2 |
|
#define SPI_CHANNEL 0 |
|
#define SPI_FLAGS SPI_MODE_3|SPI_CS_HIGH |
|
|
|
#define MIDI_NOTE_OFFSET 24 |
|
#define MIDI_NOTE_LENGTH 61 |
|
|
|
#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) |
|
{ |
|
// NOTE: assuming |
|
// DAC: 5V = 4080 / 4095 * Vref, Vref ~= 5.0184 |
|
// 1 half step/note = 1/12V = dac 68 |
|
// 1 octave = 1V = dac 816 |
|
u32 dac = midi_note * 68; |
|
(dac > MAX_DAC_INPUT) ? dac = MAX_DAC_INPUT : dac; |
|
|
|
return dac; |
|
} |
|
|
|
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(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); |
|
|
|
if (spi.fd < 0) { |
|
printf("error opening SPI device\n"); |
|
return 1; |
|
} |
|
|
|
if (!midiOpen(&seq)) |
|
return 1; |
|
|
|
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); |
|
} |
|
|
|
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); |
|
} |
|
|
|
|