#include <stdio.h>
#include <io.h>
#include <system.h>
#include "altera_avalon_pio_regs.h"

#define NUMVOICES 5

int main() {
  unsigned char temp = 0;
  unsigned char byte = 0;
  unsigned char data[2] = {0, 0};
  
  unsigned char status = 0;
  unsigned char note = 0;
  unsigned char vel = 0;
  
  unsigned char onVoices[NUMVOICES] = {0};
  #define getFreq(note) \
 (note == 0) ? 001b \
(note == 1) ? 001d \
(note == 2) ? 001e \
(note == 3) ? 0020 \
(note == 4) ? 0022 \
(note == 5) ? 0024 \
(note == 6) ? 0026 \
(note == 7) ? 0029 \
(note == 8) ? 002b \
(note == 9) ? 002e \
(note == 10) ? 0030 \
(note == 11) ? 0033 \
(note == 12) ? 0037 \
(note == 13) ? 003a \
(note == 14) ? 003d \
(note == 15) ? 0041 \
(note == 16) ? 0045 \
(note == 17) ? 0049 \
(note == 18) ? 004d \
(note == 19) ? 0052 \
(note == 20) ? 0057 \
(note == 21) ? 005c \
(note == 22) ? 0061 \
(note == 23) ? 0067 \
(note == 24) ? 006e \
(note == 25) ? 0074 \
(note == 26) ? 007b \
(note == 27) ? 0082 \
(note == 28) ? 008a \
(note == 29) ? 0092 \
(note == 30) ? 009b \
(note == 31) ? 00a4 \
(note == 32) ? 00ae \
(note == 33) ? 00b8 \
(note == 34) ? 00c3 \
(note == 35) ? 00cf \
(note == 36) ? 00dc \
(note == 37) ? 00e9 \
(note == 38) ? 00f6 \
(note == 39) ? 0105 \
(note == 40) ? 0115 \
(note == 41) ? 0125 \
(note == 42) ? 0137 \
(note == 43) ? 0149 \
(note == 44) ? 015d \
(note == 45) ? 0171 \
(note == 46) ? 0187 \
(note == 47) ? 019f \
(note == 48) ? 01b8 \
(note == 49) ? 01d2 \
(note == 50) ? 01ed \
(note == 51) ? 020b \
(note == 52) ? 022a \
(note == 53) ? 024b \
(note == 54) ? 026e \
(note == 55) ? 0293 \
(note == 56) ? 02ba \
(note == 57) ? 02e3 \
(note == 58) ? 030f \
(note == 59) ? 033e \
(note == 60) ? 0370 \
(note == 61) ? 03a4 \
(note == 62) ? 03db \
(note == 63) ? 0417 \
(note == 64) ? 0455 \
(note == 65) ? 0527 \
(note == 66) ? 0575 \
(note == 67) ? 05c8 \
(note == 68) ? 0620 \
(note == 69) ? 067d \
(note == 70) ? 06e0 \
(note == 71) ? 0749 \
(note == 72) ? 082d \
(note == 73) ? 08aa \
(note == 74) ? 092d \
(note == 75) ? 09b9 \
(note == 76) ? 0a4d \
(note == 77) ? 0ae9 \
(note == 78) ? 0b90 \
(note == 79) ? 0c40 \
(note == 80) ? 0cfa \
(note == 81) ? 0dc0 \
(note == 82) ? 0e91 \
(note == 83) ? 0f6f \
(note == 84) ? 105a;
                            // freq[0] is MIDI note 21
  unsigned int freq[128] = {28, 29, 31, 33, 35, 37, 39, 41,
                            44, 46, 49, 52, 55, 58, 61, 65,
                            69, 73, 78, 82, 87, 92, 98, 104,
                            110, 117, 123, 131, 139, 147, 156,
                            165, 175, 185, 196, 208, 220, 233,
                            247, 262, 277, 294, 311, 330, 349,
                            370, 392, 415, 440, 466, 494, 523,
                            554, 587, 622, 659, 698, 740, 784,
                            831, 880, 932, 988, 1047, 1109, 1319,
                            1397, 1480, 1568, 1661, 1760, 1865, 2093,
                            2218, 2349, 2489, 2637, 2793, 2960, 3136,
                            3322, 3520, 3729, 3951, 4186};
  
  int numbytes = 0;
  
  // clear any stray bytes that may be lingering in the register
  IOWR_ALTERA_AVALON_PIO_DATA(STATUS_PIO_BASE, 0xf1);
  
  printf("Welcome to the Vocoder!\n");

  while (1) {
    IOWR_ALTERA_AVALON_PIO_DATA(STATUS_PIO_BASE, 0xf0); // disable clearing
    temp = IORD_ALTERA_AVALON_PIO_DATA(STATUS_PIO_BASE); // read status

    if (temp == 1) { // new byte available
        byte = IORD_ALTERA_AVALON_PIO_DATA(DATA_PIO_BASE); // read data reg
        IOWR_ALTERA_AVALON_PIO_DATA(STATUS_PIO_BASE, 0xf1); // clear status reg
        
        if (byte >> 7) { //status signal
            status = byte >> 4; // use only the top nibble (the bottom nibble holds the channel)
            data[0] = 0;
            data[1] = 0;
            numbytes = 0;
        }
        else { // note-on or note-off byte
            data[numbytes] = byte;
            numbytes++;
        }
    }
    
    if (numbytes == 2) {
        note = data[0];
        vel = data[1];
        
        if (status == 0x9) { // note-on event
            if (vel != 0) { // true note-on
                if(((note - 21) >= 0) && ((note - 21) < 85)) { // actual keyboard note
                    int x = 0;
                    while (x < NUMVOICES) {
                        if ((onVoices[x] == 0)) {
                            onVoices[x] = note;
                            // turn note on
                            IOWR_ALTERA_AVALON_PIO_DATA(NOTE_1_PIO_BASE, getFreq(note));
                            printf("Playing MIDI note: %d, Frequency: %d\n", note, freq[note - 21]);
                            break;
                        }
                        x++;
                    }
                }
            }
            else { // actually a note-off
                if(((note - 21) >= 0) && ((note - 21) < 85)) { // actual keyboard note
                    int x = 0;
                    while (x < NUMVOICES) {
                        if (onVoices[x] == note) {
                            onVoices[x] = 0;
                            // turn note off
                            printf("Stopping MIDI note: %d, Frequency: %d\n", note, freq[note - 21]);
                            break;
                        }
                        x++;
                    }
                }
            }
        }
        else if (status == 0x8) { // explicit note-off event
            if(((note - 21) >= 0) && ((note - 21) < 85)) { // actual keyboard note
                int x = 0;
                while (x < NUMVOICES) {
                    if (onVoices[x] == note) {
                        onVoices[x] = 0;
                        // turn note off
                        printf("Stopping MIDI note: %d, Frequency: %d\n", note, freq[note - 21]);
                        break;
                    }
                    x++;
                }
            }
        }
        numbytes = 0;
    }
  }
  return 0;
}
