// Arduino RTTTL player with polyphonic FM sound
// * 31250 Hz sampling rate
// * 9-bit resolution
// * 4-fold polyphony (4 different tones can play simulatenously)
// * FM-synthesis with time-varying modulation amplitude
// * ADSR envelopes
// Through PWM with timer1, sound is generated on pin 9
// by Rolf Oldeman November 2019
// Licence CC BY-NC-SA 2.5 https://creativecommons.org/licenses/by-nc-sa/2.5/
#include <avr/pgmspace.h>

//song definitions
const char song00[] PROGMEM = "OhComeAl:d=4,o=5,b=160:g,2g,d,g,2a,2d,b,a,b,c6,2b,a,g,2g,f#,e,f#,g,a,b,2f#,e.,8d,2d.";
const char song01[] PROGMEM = "FelizNav:d=8,o=5,b=140:a,4d6,c#6,d6,2b.,4p,b,4e6,d6,b,2a.,4p,a,4d6,c#6,d6,4b.,g,4b,4b,a,a,b,a,4g,g,1f";
const char song02[] PROGMEM = "Jinglebe:d=4,o=6,b=90:8a5,8a5,a5,8a5,8a5,a5,8a5,8c,8f5,8g5,a5,p,8a#5,8a#5,8a#.5,16a#5,8a#5,8a5,8a.5,16a5,8a5,8g5,8g5,8a5,g5,c,8a5,8a5,a5,8a5,8a5,a5,8a5,8c,8f5,8g5,a5,p,8a#5,8a#5,8a#.5,16a#5,8a#5,8a5,8a.5,16a5,8c,8c,8a5,8g5,f.5";
const char song03[] PROGMEM = "LetItSno:d=8,o=5,b=125:c,c,c6,c6,4a#,4a,4g,4f,2c,c,c,4g.,f,4g.,f,4e,2c,4d,d6,d6,4c6,4a#,4a,2g.,e.6,16d6,4c6,c.6,16a#,4a,a#.,16a,2f.";
const char song04[] PROGMEM = "WeWishYo:d=4,o=5,b=200:d,g,8g,8a,8g,8f#,e,e,e,a,8a,8b,8a,8g,f#,d,d,b,8b,8c6,8b,8a,g,e,d,e,a,f#,2g,d,g,8g,8a,8g,8f#,e,e,e,a,8a,8b,8a,8g,f#,d,d,b,8b,8c6,8b,8a,g,e,d,e,a,f#,1g,d,g,g,g,2f#,f#,g,f#,e,2d,a,b,8a,8a,8g,8g,d6,d,d,e,a,f#,2g";
const char song05[] PROGMEM = "RudolphT:d=16,o=6,b=100:32p,g#5,8a#5,g#5,8f5,8c#,8a#5,4g#.5,g#5,a#5,g#5,a#5,8g#5,8c#,2c,f#5,8g#5,f#5,8d#5,8c,8a#5,4g#.5,g#5,a#5,g#5,a#5,8g#5,8a#5,2f5,g#5,8a#5,a#5,8f5,8c#,8a#5,4g#.5,g#5,a#5,g#5,a#5,8g#5,8c#,2c,f#5,8g#5,f#5,8d#5,8c,8a#5,4g#.5,g#5,a#5,g#5,a#5,8g#5,8d#,2c#";
const char song06[] PROGMEM = "OhChrist:d=4,o=5,b=100:c,8f.,16f,f.,8g,8a.,16a,a.,8a,8g,8a,a#,e,g,f,8p,8c6,8c6,8a,d.6,8c6,8c6,8a#,a#.,8a#,8a#,8g,c.6,8a#,8a#,8a,a,8p,8c,8f.,8f,f,8g,8a.,16a,a.,8a,8g,8a,a#,e,g,f";
const char song07[] PROGMEM = "SilentNi:d=4,o=5,b=112:g.,8a,g,2e.,g.,8a,g,2e.,2d6,d6,2b.,2c6,c6,2g.,2a,a,c6.,8b,a,g.,8a,g,2e.,2a,a,c6.,8b,a,g.,8a,g,2e.,2d6,d6,f6.,8d6,b,2c6.,2e6.,c6,g,e,g.,8f,d,2c.";
const char song08[] PROGMEM = "CarolOfT:d=8,o=5,b=180:4a,g#,a,4f#,4a,g#,a,4f#,4f#6,f#6,f#6,e6,d6,4c#6,c#6,c#6,b,a,4b,b,b,c#6,b,4f#,f#,f#,4f#,c#,d#,e,f#,g#,a,b,c#6,4b,4a,c#,d#,e,f#,g#,a,b,c#6,4b,4a,4a,g#,a,4f#,4a,g#,a,4f#";
const char song09[] PROGMEM = "SoThisIs:d=4,o=6,b=160:c5,f5,g5,a5,f5,1c5,p,2p,c5,f5,g5,a5,8g5,8f5,1d5,p,2p,d5,g5,a5,a_5,a5,2g.5,1p,c5,a5,c,a5,8a5,8g5,1f5,p,2p,f5,d,d,d,c,1a_5,p,2p,f5,a_5,c,d,1c,1p,f5,c,d,d_,d,1c,p,2p,f5,d,f,d,8a_5,8g5,2f.5";
const char song10[] PROGMEM = "SantaCla:d=4,o=5,b=160:16c,8e.,16f,g,2g,16g,8a.,16b,c6,2c6,8e.,16f,g,g,g,8a.,16g,f,2f,e,g,c,e,d,2f,b4,1c.,16c,8e.,16f,g,2g,16g,8a.,16b,c6,2c6,8e.,16f,g,g,g,8a.,16g,f,2f,e,g,c,e,d,2f,b4,1c.6";
const char song11[] PROGMEM = "FrostyTh:d=4,o=5,b=160:2g,e.,8f,g,2c6,8h,8c6,d6,c6,h,a,2g.,8h,8c6,d6,c6,h,8a,8a,g,c6,e,8g,8a,g,f,e,f,1g";
char *songs[] = {song00,song01,song02,song03,song04,song05,song06,song07,song08,song09,song10,song11};
byte nsong=sizeof(songs)/sizeof(songs[0]);

//instrument definitions          
#define ninstr 10           //  piano  guitar bell2 flute trump barit harp xylph bell1 bell3
unsigned int ADSR_a[ninstr]  = { 4096, 8192, 8192,  512,  256,  256, 8192, 8192, 4096, 8192}; // attack parameter  
unsigned int ADSR_d[ninstr]  = {   16,   32,   32,  512,  256,  256,   32,   32,   16,   32}; // decay parameter   
unsigned int ADSR_s[ninstr]  = {    0,    0,    0,  192,  192,  192,    0,    0,    0,    0}; // sustain parameter 
unsigned int ADSR_r[ninstr]  = {  128,   32,   32,  512,  256,  256,   32,   32,   16,   32}; // release parameter 
unsigned int FM_inc[ninstr]  = {  256,  192,  340,  512,  128,   64,  256,  128,  170,  680}; // FM frequency wrt pitch
unsigned int FM_a1[ninstr]  =  {   64,  512,  512,  128,  128,  128,  256,  512,  256,  512}; // FM amplitude start
unsigned int FM_a2[ninstr]  =  {   32,   32,   32,  128,  128,  128,   32,   32,   32,   32}; // FM amplitude end
unsigned int FM_dec[ninstr]  = {   64,  256,  128,    0,    0,    0,  256,  128,   64,  128}; // FM decay


void setup() {

  //setup the array with sine values
  setsine();

  //setup the array with sine values
  setexp8();

  //setup array with tone frequency phase increments
  settones();

  //setup PORTB (pins D8-D13) to high impedance, and D9 to output
  DDRB=0B00000010; PORTB=0B00000000;

  //Set a fast PWM signal on pin D9, TIMER1A, 9-bit resolution, 31250Hz
  TCCR1A = 0B10000010; //9-bit fast PWM
  TCCR1B = 0B00001001;

  //randomSeed(analogRead(0));
  randomSeed(1);
}

void loop() {

  //select random instrument and song
  byte ins=random(ninstr);
  byte isong=random(nsong);
  playsong (ins,songs[isong]);
  delay(2000);
  
  //for (byte isong=0; isong<nsong; isong++){
  //  for (byte ins=0; ins<ninstr; ins++){
  //    playsong (ins,songs[isong]);
  //    delay(2000);
  //  }
  //}

}

//set up array with sine values in signed 8-bit numbers
const float pi = 3.14159265;
char sine[256];
void setsine() {
  for (int i = 0; i < 256; ++i) {
    sine[i] = (sin(2 * 3.14159265 * (i + 0.5) / 256)) * 128;
  }
}

//set up array with exponential values mapping 0-255 -> 2^8 - 2^16
unsigned int exp8[256];
void setexp8() {
  for (int i = 0; i < 256; ++i) {
    exp8[i] = 256 * exp(log(2.0) * (i + 0.5) / 32.0);
  }
}

//setup frequencies/phase increments, starting at C3=0 to B7=59. (A4 is defined as 440Hz)
#define ntone 60
unsigned int tone_inc[ntone];
void settones() {
  for (byte itone = 0; itone < ntone; itone++) {
    tone_inc[itone] = 440.0 * pow(2.0, ( (itone - 21) / 12.0)) * 65536.0 / (16000000.0 / 512) + 0.5;
  }
}

//main parameters used in the pulse length setting
#define nch 4 //number of channels that can produce sound simultaneously
unsigned int phase[nch]  = {0, 0, 0, 0};
int          inc[nch]    = {0, 0, 0, 0};
byte         amp[nch]    = {0, 0, 0, 0};
unsigned int FMphase[nch]= {0, 0, 0, 0};
unsigned int FMinc[nch]  = {0, 0, 0, 0};
unsigned int FMamp[nch]  = {0, 0, 0, 0};

// main function to update the pulse length
inline void setPWM() {

  //wait for the timer to complete loop
  while ((TIFR1 & 0B00000001) == 0);

  //Clear(!) the overflow bit by writing a 1 to it
  TIFR1 |= 0B00000001;

  //increment the phases of the FM
  FMphase[0] += FMinc[0];
  FMphase[1] += FMinc[1];
  FMphase[2] += FMinc[2];
  FMphase[3] += FMinc[3];

  //increment the phases of the note
  phase[0] += inc[0];
  phase[1] += inc[1];
  phase[2] += inc[2];
  phase[3] += inc[3];

  //calculate the output value and set pulse width for timer2
  int val = sine[(phase[0] + sine[FMphase[0] >> 8] * FMamp[0]) >> 8] * amp[0];
  val += sine[(phase[1] + sine[FMphase[1] >> 8] * FMamp[1]) >> 8] * amp[1];
  val += sine[(phase[2] + sine[FMphase[2] >> 8] * FMamp[2]) >> 8] * amp[2];
  val += sine[(phase[3] + sine[FMphase[3] >> 8] * FMamp[3]) >> 8] * amp[3];

  //set the pulse length
  OCR1A = val / 128 + 256;
}

//properties of each note played
//static
byte         amp_base[nch]  = {0, 0, 0, 0}; //amplitude
unsigned int inc_base[nch]  = {0, 0, 0, 0}; //pitch
unsigned int lch[nch]       = {0, 0, 0, 0}; //length of the note
unsigned int ADSRa[nch]     = {0, 0, 0, 0}; //ADSR attack
unsigned int ADSRd[nch]     = {0, 0, 0, 0}; //ADSR decay
unsigned int ADSRs[nch]     = {0, 0, 0, 0}; //ADSR sustain
unsigned int ADSRr[nch]     = {0, 0, 0, 0}; //ADSR release
unsigned int FMa0[nch]      = {0, 0, 0, 0}; //initial FM amplitude 
int          FMda[nch]      = {0, 0, 0, 0}; //FM amplitude variation
unsigned int FMinc_base[nch]= {0, 0, 0, 0}; //FM modulation frequency
unsigned int FMdec[nch]     = {0, 0, 0, 0}; //FM amplitude decay time
//dynamic
unsigned int tch[nch]       = {0, 0, 0, 0}; //time into the note
byte         iADSR[nch]     = {0, 0, 0, 0}; //ADSR phase
unsigned int envADSR[nch]   = {0, 0, 0, 0}; //ADSR envelope
unsigned int FMexp[nch]     = {0, 0, 0, 0}; //FM amplitude variation
unsigned int FMval[nch]     = {0, 0, 0, 0}; //FM amplitude value


void playsong(byte ins, char* song){

  //RTTTL defaults
  byte         dur_def=4;  //tone duration (1 2 4 8 16 32) 
  byte         oct_def=6;  //octave (5 6 7 8)
  unsigned int bpm=63; //beats per minute (25-900)
  unsigned int nloop= 468750/bpm; //loops per full note(4 beats)
  unsigned int nwait=0;

  //parse first part of RTTTL string (title)
  unsigned int ind=0; char ch=pgm_read_byte_near(song+ind);
  while(ch!=':'){
    ind++; ch=pgm_read_byte_near(song+ind);
  }
  //parse second part of RTTTL strig (default duration and octave, bpm)
  ind++; ch=pgm_read_byte_near(song+ind);
  while(ch!=':'){
 
    if(ch=='d'){   
      ind+=2; ch=pgm_read_byte_near(song+ind);
      dur_def=0;
      while(isdigit(ch)){
        dur_def=10*dur_def+ch-'0';
        ind++; ch=pgm_read_byte_near(song+ind);
      } 
    }
    if(ch==','){ind++; ch=pgm_read_byte_near(song+ind);}
    if(ch=='o'){
      ind+=2; ch=pgm_read_byte_near(song+ind);
      oct_def=0;
      while(isdigit(ch)){
        oct_def=10*oct_def+ch-'0';
        ind++; ch=pgm_read_byte_near(song+ind);
      }
    }
    if(ch==','){ind++; ch=pgm_read_byte_near(song+ind);}      
    if(ch=='b'){
      ind+=2; ch=pgm_read_byte_near(song+ind);
      bpm=0;
      while(isdigit(ch)){
        bpm=10*bpm+ch-'0';
        ind++; ch=pgm_read_byte_near(song+ind);
      }
      nloop= 468750/bpm;
    }      
  }
 
  
  // main loop. Duration of loop is determined by number of setPWM calls
  // Each setPWMcall takes 512 clockcycles=32mus
  // Each loop takes 32mus * #setPWM. #setPWM=16 gives Tloop=0.512ms

  //disable all inerrupts to avoid glitches
  noInterrupts();

  bool lastnote=false;
  bool readnote=true;
  while(1){

    byte key=0;
    byte dur=0;
    byte oct=0;
    byte vol=64;
    
    unsigned int len=nloop/4;
    bool newnote=false;

    //read a new note
    if(readnote){

      //skip one character (either : or , )
      ind++; ch=pgm_read_byte_near(song+ind);
      //read duration
      while(isdigit(ch)){
        dur=10*dur+ch-'0';
        ind++; ch=pgm_read_byte_near(song+ind);
      }
      if(dur==0)dur=dur_def;
      len=nloop/dur;

      //read pitch 
      if(not isdigit(ch)){
        if(ch=='c')key= 0; 
        if(ch=='d')key= 2;
        if(ch=='e')key= 4;
        if(ch=='f')key= 5;
        if(ch=='g')key= 7;
        if(ch=='a')key= 9;
        if(ch=='b' or ch=='h')key=11;
        if(ch=='p')key=100;
        ind++; ch=pgm_read_byte_near(song+ind);
        if(ch=='#' or ch=='_'){
          key+=1;
          ind++; ch=pgm_read_byte_near(song+ind);
        }
      }
      
      //check for dot
      if(ch=='.'){
        len+=len/2;
        ind++; ch=pgm_read_byte_near(song+ind);
      }               
      //read octave
      if(isdigit(ch)){
        oct=ch-'0';
        ind++; ch=pgm_read_byte_near(song+ind);
      }
      if(oct==0) oct=oct_def;
      key+=12*(oct-3);
      //check for dot again
      if(ch=='.'){
        len+=len/2;
        ind++; ch=pgm_read_byte_near(song+ind);
      }        
      //check if the end has been reached
      if(ch==0){
        lastnote=true;
      }
      nwait=len;
      if(key<100)newnote=true;
      readnote=false;
    }
    if(nwait==0 and not lastnote)readnote=true;

    setPWM(); //#1
 
    //find the best channel to start a new note
    byte nextch = 255;
    //check for an empty channel
    if (iADSR[0] == 0)nextch = 0;
    if (iADSR[1] == 0)nextch = 1;
    if (iADSR[2] == 0)nextch = 2;
    if (iADSR[3] == 0)nextch = 3;
    //otherwise use the channel with the longest playing note
    if (nextch == 255) {
      nextch = 0;
      if (tch[0] > tch[nextch])nextch = 0;
      if (tch[1] > tch[nextch])nextch = 1;
      if (tch[2] > tch[nextch])nextch = 2;
      if (tch[3] > tch[nextch])nextch = 3;
    }

    setPWM(); //#2

    //initiate new note if needed
    if (newnote) {
      phase[nextch]=0;
      amp_base[nextch] = vol;
      inc_base[nextch] = tone_inc[key];
      ADSRa[nextch]=ADSR_a[ins];
      ADSRd[nextch]=ADSR_d[ins];
      ADSRs[nextch]=ADSR_s[ins]<<8;
      ADSRr[nextch]=ADSR_r[ins];
      iADSR[nextch] = 1;
      FMphase[nextch]=0;
      FMinc_base[nextch] = ((long)inc_base[nextch]*FM_inc[ins])/256;
      FMa0[nextch] = FM_a2[ins];
      FMda[nextch] = FM_a1[ins]-FM_a2[ins];
      FMexp[nextch]=0xFFFF;
      FMdec[nextch]=FM_dec[ins];
      tch[nextch] = 0;
      lch[nextch]=(len-len/4);
    }

    setPWM(); //#3

    //end a note if it's over time
    if (tch[0] > lch[0])iADSR[0] = 4;
    if (tch[1] > lch[1])iADSR[1] = 4;
    if (tch[2] > lch[2])iADSR[2] = 4;
    if (tch[3] > lch[3])iADSR[3] = 4;

    setPWM(); //#4

    //update FM decay exponential 
    FMexp[0]-=(long)FMexp[0]*FMdec[0]>>16;
    FMexp[1]-=(long)FMexp[1]*FMdec[1]>>16;
    FMexp[2]-=(long)FMexp[2]*FMdec[2]>>16;
    FMexp[3]-=(long)FMexp[3]*FMdec[3]>>16;

    setPWM(); //#5

    //adjust the ADSR envelopes
    for (byte ich = 0; ich < nch; ich++) {
      if (iADSR[ich] == 4) {
        if (envADSR[ich] <= ADSRr[ich]) {
          envADSR[ich] = 0;
          iADSR[ich] = 0;
        }
        else envADSR[ich] -= ADSRr[ich];
      }
      if (iADSR[ich] == 2) {
        if (envADSR[ich] <= (ADSRs[ich] + ADSRd[ich])) {
        //if ((envADSR[ich]-ADSRd[ich]) <= ADSRs[ich]) {
          envADSR[ich] = ADSRs[ich];
          iADSR[ich] = 3;
        }
        else envADSR[ich] -= ADSRd[ich];
      }
      if (iADSR[ich] == 1) {
        if ((0xFFFF - envADSR[ich]) <= ADSRa[ich]) {
          envADSR[ich] = 0xFFFF;
          iADSR[ich] = 2;
        }
        else envADSR[ich] += ADSRa[ich];
      }
      setPWM(); //#6-9
    }

    //update the tone for channel 0
    amp[0] = (amp_base[0] * (envADSR[0] >> 8)) >> 8;
    inc[0] = inc_base[0];
    FMamp[0] = FMa0[0] + ((long)FMda[0] * FMexp[0]>>16);
    FMinc[0] = FMinc_base[0];
    setPWM(); //#10

    //update the tone for channel 1
    amp[1] = (amp_base[1] * (envADSR[1] >> 8)) >> 8;
    inc[1] = inc_base[1];
    FMamp[1] = FMa0[1] + ((long)FMda[1] * FMexp[1]>>16);
    FMinc[1] = FMinc_base[1];
    setPWM(); //#11

    //update the tone for channel 2
    amp[2] = (amp_base[2] * (envADSR[2] >> 8)) >> 8;
    inc[2] = inc_base[2];
    FMamp[2] = FMa0[2] + ((long)FMda[2] * FMexp[2]>>16);
    FMinc[2] = FMinc_base[2];
    setPWM(); //#12

    //update the tone for channel 3
    amp[3] = (amp_base[3] * (envADSR[3] >> 8)) >> 8;
    inc[3] = inc_base[3];
    FMamp[3] = FMa0[3] + ((long)FMda[3] * FMexp[3]>>16);
    FMinc[3] = FMinc_base[3];
    setPWM(); //#13

    //update counters
    tch[0]++;
    tch[1]++;
    tch[2]++;
    tch[3]++;
    if(nwait>0)nwait--;

    setPWM(); //#14

    if (lastnote and nwait==0 and iADSR[0]==0 and iADSR[1]==0 and iADSR[2]==0 and iADSR[3]==0 ) break;

    setPWM(); //#15
    setPWM(); //#16
  }
  
  //interrupts are allowed again
  interrupts();
   
}
