// 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 #include #include #include #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); }