//Jacob Lehrman
//ME 608
//Dr. Wilson
//University of Kansas
//Last Updated: 5/4/2025
//Desc: Code to test multi-channel audio output. Data, scripts, and information sourced from http://wiki.openmusiclabs.com/wiki/PWMDAC, https://www.norwegiancreations.com/2022/03/digital-audio-synthesis-part-1-the-oscillator/, and https://grybouilli.github.io/posts/multivoice_arduino/
//Music by Redstarfire

uint8_t counter = 0;
int16_t phaseAccumulator[] = {2047, 2047, 2047, 2047};//This will track the progress of each Voice through their respective waveform.
const uint8_t phases = 4;//The number of voices in the phase accumulator
uint8_t phaseIndex[] = {0, 0, 0, 0};//Tracks where in the waveform the music is
uint16_t control[] = {0, 0, 0, 0};//Holds the control words for the current notes
const uint16_t controlWords[] = {274, 290, 307, 326, 345, 366, 387, 411, 435, 461, 488, 517};//These make the phase accumulator update appropriately. Correspond to the notes C4 through B4, including sharps.
const uint8_t sinusoid[] = {15, 21, 26, 29, 31, 29, 26, 21, 15, 9, 4, 1, 0, 1, 4, 9};//Samples of a sinewave to use for generating notes.
const uint8_t triangle[] = {0, 3, 7, 11, 15, 19, 23, 27, 31, 27, 23, 19, 15, 11, 7, 3};//Samples of Triangle Wave
const uint8_t square50[] = {0, 0, 1, 1, 2, 2, 3, 3, 20, 20, 19, 19, 18, 18, 17, 17};//Samples of Square Wave. This is actually a Moog Modified square wave for slightly better sound (and because Redstarfire specified it). Had to be turned down otherwise it would drown out other sounds.
const uint8_t sawtooth[] = {0, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 41};//Samples of Sawtooth. Had to be apmlified a bit otherwise other sounds would drown it out.{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31} Unmodified Original
const uint8_t* voices[] = {triangle, square50, sawtooth, sinusoid};//Bundling Samples together for easy addressing in code

const uint8_t Bass[4][32] = {
  //Each array is a measure of the Bass, in 16th note increments
  //format: Note control word index, bitshifts to get the octave right.
  {4,1,4,1,11,1,11,1,4,1,4,1,11,1,11,1,4,1,4,1,11,1,11,1,4,1,4,1,11,1,11,1},
  {7,1,7,1,2,0,2,0,7,1,7,1,2,0,2,0,7,1,7,1,2,0,2,0,7,1,7,1,2,0,2,0},
  {2,1,2,1,9,1,9,1,2,1,2,1,9,1,9,1,2,1,2,1,9,1,9,1,2,1,2,1,9,1,9,1},
  {0,1,0,1,7,1,7,1,0,1,0,1,7,1,7,1,0,1,0,1,7,1,7,1,0,1,0,1,7,1,7,1}
};

const uint8_t Arpeggio[4][32] = {
  {4,1,11,0,7,0,4,0,4,1,11,0,7,0,4,0,4,1,11,0,7,0,4,0,4,1,11,0,7,0,4,0},
  {2,1,11,0,7,0,4,0,2,1,11,0,7,0,4,0,2,1,11,0,7,0,4,0,2,1,11,0,7,0,4,0},
  {6,1,2,1,11,0,7,0,6,1,2,1,11,0,7,0,6,1,2,1,11,0,7,0,6,1,2,1,11,0,7,0},
  {7,1,4,1,0,1,7,0,7,1,4,1,0,1,7,0,7,1,4,1,0,1,7,0,7,1,4,1,0,1,7,0}
};

const uint8_t Melody1_2[5][32] = {
  //Value of 12 defines silence
  {4,0,4,0,12,0,12,0,4,0,4,0,4,0,4,0,7,0,7,0,4,0,4,0,2,0,2,0,11,0,11,0},
  {12,0,12,0,11,0,11,0,7,0,7,0,4,0,4,0,11,0,11,0,7,0,7,0,4,0,4,0,11,0,11,0},
  {6,0,6,0,12,0,12,0,6,0,6,0,9,0,9,0,6,0,6,0,4,0,4,0,2,0,2,0,4,0,4,0},
  {12,0,12,0,7,0,7,0,4,0,4,0,2,0,2,0,4,0,4,0,7,0,7,0,4,0,4,0,2,0,2,0},
  {12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,12,0,}
};

const uint8_t Melody3[5][32] = {
  {4,0,12,0,4,0,12,0,7,0,7,0,4,0,12,0,7,0,12,0,7,0,9,0,9,0,9,0,7,0,7,0},//Play 2x, Play 1x
  {4,0,12,0,4,0,12,0,7,0,7,0,4,0,4,0,7,0,12,0,7,0,9,0,9,0,9,0,7,0,7,0},//Play 1x, Play 1x after 4th one 
  {9,0,9,0,7,0,7,0,4,0,4,0,7,0,7,0,9,0,9,0,7,0,7,0,4,0,4,0,7,0,7,0},//Play 1x, 
  {4,0,12,0,4,0,12,0,7,0,7,0,4,0,12,0,7,0,12,0,7,0,9,0,9,0,9,0,11,0,11,0},//Play 0x, Play 1x
  {9,0,9,0,7,0,7,0,4,0,4,0,11,0,11,0,0,1,0,1,11,0,11,0,7,0,7,0,9,0,12,0}//Play 0x, Play 1x
};
/*
Audio code removed from final. More optimal than waht is implemented here, but unused as this works.
//Music data/variables
const uint8_t sinusoid[] = {15, 21, 26, 29, 31, 29, 26, 21, 15, 9, 4, 1, 0, 1, 4, 9};//Samples of a sinewave to use for generating notes.
const uint8_t triangle[] = {0, 3, 7, 11, 15, 19, 23, 27, 31, 27, 23, 19, 15, 11, 7, 3};//Samples of Triangle Wave
const uint8_t square50[] = {0, 0, 1, 1, 2, 2, 3, 3, 20, 20, 19, 19, 18, 18, 17, 17};//Samples of Square Wave. This is actually a Moog Modified square wave for slightly better sound (and because Redstarfire specified it). Had to be turned down otherwise it would drown out other sounds.
const uint8_t sawtooth[] = {0, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 41};//Samples of Sawtooth. Had to be apmlified a bit otherwise other sounds would drown it out.{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31} Unmodified Original
const uint8_t* voices[] = {triangle, square50, sawtooth, sinusoid};//Bundling Samples together for easy addressing in code
const uint16_t controlWords[] = {548, 581, 615, 652, 691, 732, 775, 822, 870, 922, 977, 1035};//These make the phase accumulator update appropriately. Correspond to the notes C4 through B4, including sharps. Included here for making the actual array in ram.
//All the notes that are actually used in the song. Bit shifts to the right represent lowering the octave, bit shifts left represent raising it.
const uint16_t usedWords[] = {controlWords[0]>>1,controlWords[2]>>1,controlWords[4]>>1,controlWords[7]>>1,controlWords[9]>>1,controlWords[11]>>1,controlWords[2],controlWords[4],controlWords[6],controlWords[7],controlWords[9],controlWords[11],controlWords[0]<<1,controlWords[2]<<1,controlWords[4]<<1,controlWords[6]<<1,controlWords[7]<<1,};
uint8_t melodyTracker = 0;//8 bits for tracking the melody's progress
uint8_t repetitionTracker = 0;//8 bits to track whcih repetition and measure are current (only use lower 7)
uint8_t playing = 0x00;//8 boolean values, used to track the 3 voices that can be playing. initialized with none playing during calibration step.
uint8_t counter = 0;//Counts number of interrupts to make music sound good
int16_t phaseAccumulator[] = {2047, 2047, 2047};//This will track the progress of each Voice through their respective waveform.
const uint8_t phases = 3;//The number of voices in the phase accumulator
uint8_t phaseIndex[] = {0, 0, 0};//Tracks where in the waveform the music is
uint16_t control[] = {0, 0, 0};//Holds the control words for the current notes
const uint8_t samples = 16;//Number of samples in waveworms
const uint16_t indexer = 2047;//Value to add to the phaseAccumulator to make sure the waveform is produced appropriately.


uint8_t Bass[4][2] = {
  //Each array is the control word indices for the two note sequence repeated 4 times to make a measure in the order they appear
  {2,5},
  {3,6},
  {1,4},
  {0,3}
};
uint8_t Arpeggio[4][4] = {
  //Each array is the control word indices for the four note sequence repeated 4 times to make a measure in the order they appear
  {14,11,9,7},
  {13,11,9,7},
  {15,13,11,9},
  {16,14,12,9}
};

uint8_t Melody[3][8][16] = {

  {{7,7,17,17,7,7,7,7,9,9,7,7,6,6,11,11},
  {17,17,11,11,9,9,7,7,11,11,9,9,7,7,11,11},
  {8,8,17,17,8,8,10,10,8,8,7,7,6,6,7,7},
  {17,17,9,9,7,7,6,6,7,7,9,9,7,7,6,6},
  {7,7,17,17,7,7,7,7,9,9,7,7,6,6,11,11},
  {17,17,11,11,9,9,7,7,11,11,9,9,7,7,11,11},
  {8,8,17,17,8,8,10,10,8,8,7,7,6,6,7,7},
  {17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17}},

  {{7,7,17,17,7,7,7,7,9,9,7,7,6,6,11,11},
  {17,17,11,11,9,9,7,7,11,11,9,9,7,7,11,11},
  {8,8,17,17,8,8,10,10,8,8,7,7,6,6,7,7},
  {17,17,9,9,7,7,6,6,7,7,9,9,7,7,6,6},
  {7,7,17,17,7,7,7,7,9,9,7,7,6,6,11,11},
  {17,17,11,11,9,9,7,7,11,11,9,9,7,7,11,11},
  {8,8,17,17,8,8,10,10,8,8,7,7,6,6,7,7},
  {17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17}},

  {{7,17,7,17,9,9,7,17,9,17,9,10,10,10,9,9},
  {7,17,7,17,9,9,7,17,9,17,9,10,10,10,9,9},
  {7,17,7,17,9,9,7,7,9,17,9,10,10,10,9,9},
  {10,10,9,9,7,7,9,9,10,10,9,9,7,7,9,9},
  {7,17,7,17,9,9,7,17,9,17,9,10,10,10,9,9},
  {7,17,7,17,9,9,7,17,9,17,9,10,10,10,11,11},
  {7,17,7,17,9,9,7,7,9,17,9,10,10,10,9,9},
  {10,10,9,9,7,7,11,11,12,12,11,11,9,9,10,17}}
};
*/

const uint8_t samples = 16;//Number of samples in waveworms
const uint16_t indexer = 2047;//Value to add to the phaseAccumulator to make sure the waveform is produced appropriately.
bool playing[] = {true, true, true, false};
bool mel2 = false;

void setup() {
  //Enabling the pins
  pinMode(10, OUTPUT);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  Serial.begin(9600);
  //Setup Timer 1 - This will be the Music Track
  //Initialization Code for Timer 1 by grybouilli

  cli();//Disable interrupts during setup
  // Mode 14 : Fast PWM, no prescaler, TOP value defined by ICR
  TCCR1A = 0x00;//Clear both registers to make sure the right bits are written.
  TCCR1B = 0x00;
  TCCR1A = 0x22;//0b00100010 - Enables OCR1B (Pin 10) in Clear on Compare, Set at Bottom mode and sets WGM bits 1:0 to 10
  TCCR1B = 0x19;//0b00011001 - Sets WGM Bits 3:2 to 11 and CS 2:0 to 001
  // Max value is 127
  ICR1H = 0;
  ICR1L = 0x7F;
  // Enable overflow interrupt
  TIMSK1 = 0x01;
  // Unused upper byte for compare match
  //OCR1AH = 0;
  OCR1BH = 0;
  sei();//Enables Interrupts
}

void playNotes(const uint8_t notes1[32], const uint8_t notes2[32], const uint8_t notes3[32]){
  //Function to play a measure
  for(int i=0;i<32;i+=2){
    if(notes1[i]<12){
      //If the current note isn't silence, play it
      playing[0] = (digitalRead(2)==HIGH);//If the Bass is enabled
      control[0] = controlWords[notes1[i]] >> notes1[i+1];//Bassline only shifts to the right
    } else {
      //otherwise, don't play this voice
      playing[0] = false;
    }
    if(notes2[i]<12){
      playing[1] = (digitalRead(2)==HIGH);//If the Arpeggio is enabled;
      control[1] = controlWords[notes2[i]] << notes2[i+1];//Arpeggio and bassline both shift left, never right.
    } else {
      playing[1] = false;
    }
    if(notes3[i]<12){
      playing[2] = (digitalRead(3)==HIGH);//If the Melody is enabled;
      control[2] = controlWords[notes3[i]] << notes3[i+1];
      if(mel2){control[2] = control[2] << 1;}//This makes it so that the repetition of the melody is one octave higher than the first.
    } else {
      playing[2] = false;
    }
    delay(100);
  }
}

ISR(TIMER1_OVF_vect){
  //Code translated from grybouilli
  if(counter++ & 3){return;}//This should only return false every 4 overflows, which is what we need.
  uint8_t output = 0;//Enables Silence to be played
  for(uint8_t v=0;v<phases;v++){
    if(playing[v] == true){
      phaseAccumulator[v] -= control[v];//Decrement the PA by the control Word
      if(phaseAccumulator[v] < 0){//If the PA becomes negative, go on to the next sample
        phaseAccumulator[v] += indexer;//Reset the PA for the next sample
        phaseIndex[v]++;
        phaseIndex[v] = phaseIndex[v] % samples;//Keep the Index in range of the sinusoid array
      }
      output += voices[v][phaseIndex[v]];//Can't just go directly to OCR1AL as need to allow default value of 0 in case of silence.
    }
  }
  OCR1BL = output;//Output Compare Register 1 A Lower = output
}

void loop() {
    for(uint8_t m=0;m<4;m++){
      playNotes(Bass[m],Arpeggio[m],Melody1_2[m]);
    }
    for(uint8_t m=0;m<3;m++){
      playNotes(Bass[m],Arpeggio[m],Melody1_2[m]);
    }
    mel2 = !mel2;//Switch Melody 1 to Melody 2
    playNotes(Bass[3],Arpeggio[3],Melody1_2[4]);
    for(uint8_t m=0;m<4;m++){
      playNotes(Bass[m],Arpeggio[m],Melody1_2[m]);
    }
    for(uint8_t m=0;m<3;m++){
      playNotes(Bass[m],Arpeggio[m],Melody1_2[m]);
    }
    mel2 = !mel2;//Switch Melody 2 to Melody 1
    playNotes(Bass[3],Arpeggio[3],Melody1_2[4]);
    //This section plays the 3rd Melody
    playNotes(Bass[0],Arpeggio[0],Melody3[0]);
    for(uint8_t m=0;m<3;m++){
      playNotes(Bass[m+1],Arpeggio[m+1],Melody3[m]);
    }
    playNotes(Bass[0],Arpeggio[0],Melody3[0]);
    playNotes(Bass[1],Arpeggio[1],Melody3[3]);
    playNotes(Bass[2],Arpeggio[2],Melody3[4]);
    playNotes(Bass[3],Arpeggio[3],Melody3[1]);
}