diff --git a/Makefile b/Makefile index cce8093..78abb16 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CXX=g++ CXXFLAGS= -std=c++11 -g -ggdb3 -Wall -LDFLAGS=-lasound +LDFLAGS=-lasound -lgpiod OBJDIR = build SRCDIR = src diff --git a/src/main.cpp b/src/main.cpp index 0f287a5..730a257 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,4 @@ -// 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 @@ -13,10 +12,13 @@ #include #include -#include "spi.h" +#include "spi.h" // FIXME: should rename to gpio.h #include "midi.h" +#define GPIO_CHIP_NAME "gpiochip1" +#define GPIO_LINE_NUM 82 // GPIO Pin #38 on 'le-potato' + #define SPI_SPEED 1000000 #define SPI_BUFFER_LENGTH 2 #define SPI_CHANNEL 0 @@ -57,18 +59,49 @@ convertNoteToDACInput(u8 midi_note) } void -loopMIDI(alsa_sequencer* seq, spi_connection* spi) +loopMIDI(alsa_sequencer* seq, spi_connection* spi, gpio_info* gpio) { + // TODO: there's probably a more elegant way to handle this, but this was + // the first idea that came up, and seems to work + // NOTE: MIDI Note '0' is a vaild note, but 255 is not. So, we'll use 255 + // as a placeholder to represent an unused PLAYING_NOTE + static const u32 MAX_NOTES = 6; + static u8 PLAYING_NOTES[MAX_NOTES] = {255,255,255,255,255,255}; + 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); + // FIXME: we should only be processing notes from the first channel + alsa_note_event nev = midiProcess(ev); + bool note_tracked = false; + + for (u32 i = 0; i < MAX_NOTES; i++) { + if (PLAYING_NOTES[i] == nev.note) { + note_tracked = true; + + if (nev.note_on) { // do nothing + break; + } else { // remove note from tracked list + assert(gpioSetValue(gpio, 0)); // send set gate off + PLAYING_NOTES[i] = 255; + break; + } + } + } + // send note voltage to DAC, and add note to tracked list + if (!note_tracked && nev.note_on ) { + for (u32 i = 0; i < MAX_NOTES; i++) { + if (PLAYING_NOTES[i] == 255) { + PLAYING_NOTES[i] = nev.note; + assert(gpioSetValue(gpio, 1)); // send set gate on + } + } + + i32 dac_in = convertNoteToDACInput(nev.note); // TODO: could check return value here for SPI error spiSendVoltage(spi, dac_in); } @@ -101,6 +134,11 @@ main(i32 argc, char* argv[]) 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); + gpio_info gpio = {0}; + + if (!gpioInit(&gpio, GPIO_CHIP_NAME, GPIO_LINE_NUM)) + return 1; + if (spi.fd < 0) { printf("error opening SPI device\n"); return 1; @@ -112,7 +150,7 @@ main(i32 argc, char* argv[]) while ((opt = getopt(argc, argv, "tlk:h")) != -1) { switch (opt) { case 'l': - loopMIDI(&seq, &spi); + loopMIDI(&seq, &spi, &gpio); break; case 'k': if (!midiConnect(&seq, optarg)) { @@ -120,7 +158,7 @@ main(i32 argc, char* argv[]) exit(1); } - loopMIDI(&seq, &spi); + loopMIDI(&seq, &spi, &gpio); break; case 't': loopTestVoltages(&spi); diff --git a/src/midi.cpp b/src/midi.cpp index d670cc8..4b773bb 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -63,22 +63,27 @@ midiConnect(alsa_sequencer* seq, const char* midi_device) return false; } -i32 +alsa_note_event midiProcess(const snd_seq_event_t* ev) { assert(ev != nullptr); - i32 note = -1; + 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)\n", - ev->time.tick, type, + 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); - 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) { @@ -92,7 +97,7 @@ midiProcess(const snd_seq_event_t* ev) printf("[%d] Unknown: Unhandled Event Received\n", ev->time.tick); } - return note; + return nev; } i32 diff --git a/src/midi.h b/src/midi.h index 39678fd..16fe738 100644 --- a/src/midi.h +++ b/src/midi.h @@ -14,11 +14,18 @@ struct alsa_sequencer snd_seq_addr_t dest; }; +struct alsa_note_event +{ + u8 note; + u8 channel; + bool note_on; +}; + bool midiOpen(alsa_sequencer* seq); bool midiConnect(alsa_sequencer* seq, const char* midi_device); -i32 midiProcess(const snd_seq_event_t* ev); +alsa_note_event midiProcess(const snd_seq_event_t* ev); i32 midiClose(alsa_sequencer* seq); diff --git a/src/spi.cpp b/src/spi.cpp index 80d37a7..0a2520b 100644 --- a/src/spi.cpp +++ b/src/spi.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -9,6 +10,57 @@ #include "spi.h" +// GPIO + +bool +gpioInit(gpio_info* gpio, const char* chip_name, u32 line_num) +{ + strncpy(gpio->chip_name, chip_name, sizeof(gpio->chip_name)); + gpio->line_num = line_num; + gpio->chip = gpiod_chip_open_by_name(gpio->chip_name); + + if (!gpio->chip) { + printf("Error opening chip, %s\n", gpio->chip_name); + return false; + } + + gpio->line = gpiod_chip_get_line(gpio->chip, line_num); + + if (!gpio->line) { + printf("Error opening line, chip: %s, line: %d\n", gpio->chip_name, line_num); + gpiod_chip_close(gpio->chip); + return false; + } + + i32 ret = gpiod_line_request_output(gpio->line, "Consumer", 0); + + if (ret < 0) { + printf("Error setting line output, chip: %s, line: %d\n", gpio->chip_name, line_num); + gpiod_line_release(gpio->line); + gpiod_chip_close(gpio->chip); + return false; + } + + return true; +} + +bool +gpioSetValue(gpio_info* gpio, u32 value) +{ + assert(gpio); + i32 ret = gpiod_line_set_value(gpio->line, value); + + if (ret < 0) { + printf("Error setting line value, chip: %s, line: %d\n", gpio->chip_name, gpio->line_num); + return false; + } + + return true; +} + + +// SPI + i32 open_device(u8 channel, u32 speed, u8 mode_flags); diff --git a/src/spi.h b/src/spi.h index 5678243..268e5d7 100644 --- a/src/spi.h +++ b/src/spi.h @@ -3,10 +3,28 @@ #include #include +#include #include "types.h" +// GPIO + +struct gpio_info +{ + char chip_name[16]; + u32 line_num; + gpiod_chip* chip; + gpiod_line* line; +}; + +bool gpioInit(gpio_info* gpio, const char* chip_name, u32 line_num); + +bool gpioSetValue(gpio_info* gpio, u32 value); + + +// SPI + struct spi_connection { i32 fd; @@ -17,7 +35,6 @@ struct spi_connection u8* buffer; }; - spi_connection spiInit(u32 speed, u32 max_dac_input, u8* buf, u32 buf_len, u8 flags, u8 channel); bool spiWrite(i32 fd, u32 speed, u8* buf, u32 buf_len);