//twinkle twinkle song

#include <avr/pgmspace.h>                     //librarary to access flash memro of the arduino
#include <Servo.h>                            //library for servo motor
Servo myservo;                                //servo for doll skirt movements
Servo baseServo;                              //servo for base movement
static const uint8_t tonePin = 4;             //speaker pin to arduino pin 4
const int ledPin =  LED_BUILTIN;              //led pin
int ledState = LOW;
int pirLeft = 7;                              // pir motion sensor pins to arduino pin 6 and 7
int pirRight = 6 ;
unsigned long previousMillis = 0;
const long interval = 1000;                   //checking frequnecy change in 1s interval
unsigned long previousMillispir = 0;
const long headsup = 1000;                    //checking motion sensor state in 1s
int threshold  = 1350;                        //this value is considered after plotting song and obtaing average of highest and lowest frequnecy value.
// Melody Data
// ----------------------------------------------------------------------------
// The melody is stored as an array of 16 bit unsigned integers (uint16_t)
// where each integer encodes the note, octave and duration
//
//   [duration][octave][note]  - ddddddddoooonnnn
//
//   note (nnnn) is 4 bits which are the note to play
//    C = 0, C# = 1, D = 2, D# = 3, E = 4, F = 5,
//    F# = 6, G = 7, G# = 8, A = 9, A# = 10, B = 11,
//    SILENT = 15
//
//   octave (oooo) is 4 bits which indicate the octave to play that note in,
//   maximum octave we can handle is 8, minimum is 0
//    octave 0 = 0 .. octave 8 = 8
//
//   duration (dddddddd) is the duration of the note to play in units of tempo
//    hold note for 10 tempos = 10
//

// The melodies must be named Melody0 .. Melody{NUMBER_OF_MELODIES-1}, and
// each must also have a MelodyX_Length defined.



//Notes definition : The duration is in the upper 8 bits, and the note is in the lower 8 bits.
// Octave 5 Note Codes
#define       NOTE_C_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010000)
#define      NOTE_CS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010001)
#define       NOTE_D_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010010)
#define      NOTE_DS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010011)
#define       NOTE_E_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010100)
#define       NOTE_F_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010101)
#define      NOTE_FS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010110)
#define       NOTE_G_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01010111)
#define      NOTE_GS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011000)
#define       NOTE_A_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011001)
#define      NOTE_AS_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011010)
#define       NOTE_B_5(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01011011)

// Octave 6 Note Codes
#define       NOTE_C_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100000)
#define      NOTE_CS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100001)
#define       NOTE_D_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100010)
#define      NOTE_DS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100011)
#define       NOTE_E_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100100)
#define       NOTE_F_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100101)
#define      NOTE_FS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100110)
#define       NOTE_G_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01100111)
#define      NOTE_GS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101000)
#define       NOTE_A_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101001)
#define      NOTE_AS_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101010)
#define       NOTE_B_6(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01101011)

// Octave 7 Note Codes
#define       NOTE_C_7(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01110000)
#define      NOTE_CS_7(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01110001)
#define       NOTE_D_7(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01110010)
#define      NOTE_DS_7(DURATION) ( (((uint16_t)DURATION)<<8) | 0b01110011)

#define    NOTE_SILENT(DURATION) ((((uint16_t)DURATION)<<8) | 0b00001111)

#define NUMBER_OF_MELODIES 1

void setup()
{
  myservo.attach(3);                        // rudder motion servo
  baseServo.attach(5);                        // base motor (to be replaced with stepper)
  pinMode(ledPin, OUTPUT);
  pinMode(pirLeft, INPUT);
  pinMode(pirRight, INPUT);
  Serial.begin(9600);
  uint8_t melodyToPlay = 0;
  uint8_t tempoToPlay  = 4;                 //how fast song is being played ( 1 being fastest and 127 being slowest)
  playMelody(melodyToPlay, tempoToPlay);    // Main funtion call
}
//----------------------------
void loop()
{
  // If the tune needs to be continuoslys played in loop  place main funtion in the loop.
}
//  TWINKLE TWINKLE
static const uint16_t Melody0[] PROGMEM = {
  NOTE_C_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_C_6( 119 ),  NOTE_G_6( 149 ),
  NOTE_SILENT(91 ), NOTE_A_6(  89 ),        NOTE_SILENT(  31), NOTE_A_6( 119 ),
  NOTE_G_6( 209 ),  NOTE_SILENT(  31 ),     NOTE_F_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_F_6( 119 ),  NOTE_E_6(  89 ),        NOTE_SILENT(31 ), NOTE_E_6( 119 ),
  NOTE_D_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_D_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_C_6( 209 ),  NOTE_SILENT(  31 ),     NOTE_G_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_G_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_F_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_F_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_E_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_E_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_D_6( 179 ),  NOTE_SILENT(  61 ),
  NOTE_G_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_G_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_F_6(  59 ),  NOTE_SILENT(  61 ),     NOTE_F_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_E_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_E_6(  59 ),  NOTE_SILENT(  61 ),
  NOTE_D_6( 179 ),  NOTE_SILENT(  61 ),     NOTE_C_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_C_6(  59 ),  NOTE_SILENT(  61 ),     NOTE_G_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_G_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_A_6(  89 ),  NOTE_SILENT(  31 ),
  NOTE_A_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_G_6( 179 ),  NOTE_SILENT(  61 ),
  NOTE_F_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_F_6(  59 ),  NOTE_SILENT(  61 ),
  NOTE_E_6(  59 ),  NOTE_SILENT(  61 ),     NOTE_E_6(  59 ),  NOTE_SILENT(  61 ),
  NOTE_D_6(  89 ),  NOTE_SILENT(  31 ),     NOTE_D_6(  59 ),  NOTE_SILENT(  61 ),
  NOTE_C_6( 179 ),
};
static const uint16_t Melody0_Length    = sizeof( Melody0 ) / sizeof(uint16_t);

/** Play a melody.

   Provide the MelodyX and MelodyX_Length as well as a tempo for the melody to play it on
   tonePin (defined above).

*/

void playMelody_Data(const uint16_t MelodyData[], const uint16_t MelodyLength, const uint8_t tempo)
{
  uint16_t  logs[MelodyLength];
  int i = 0; int left = 0; int right = 0;

  static const uint16_t Freq8[] PROGMEM = { 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902 };                   // from frequency of c8 to B8 lower vlues like c6 calculated
  for (uint16_t  x = 0; x < MelodyLength; x++)
  {
    unsigned long currentMillis = millis();                                     //start timer
    int count = 0;
    int left = digitalRead(pirLeft);                                            //reading left pir sesnor
    int right = digitalRead(pirRight);                                          //reading right pir sesnor
    uint16_t data = pgm_read_word((uint16_t *)& MelodyData[x]);                 //Get the melody data from PROGMEM array defined above along with its address.
    if ((data & 0xF) == 0xF)                                                    //check if data is zero which means silence
    {
      noTone(tonePin);                                                          //Notone during silence
    }
    else
    {
      uint16_t Freq = pgm_read_word(&Freq8[data & 0xF]) / ( 1 << (8 - (data >> 4 & 0xF)) );   //calculate exact frequency value of the note defined in progmem uisng equal tempered scale formula.                                          // frequencies of played notes will be obtained

      /*The basic formula for the frequencies of the notes of the equal tempered scale is given by
        fn = f0 * (a)n
        where
        f0 = the frequency of one fixed note which must be defined. A common choice is setting the A above middle C (A4) at f0 = 440 Hz.
        n = the number of half steps away from the fixed note you are. If you are at a higher note, n is positive. If you are on a lower note, n is negative.
        fn = the frequency of the note n half steps away.
        a = (2)1/12 = the twelth root of 2 = the number which when multiplied by itself 12 times equals 2 = 1.059463094359...
      */

      uint16_t  logs[MelodyLength];
      if (Freq > threshold)                                              //lowest value is 1046hz and highest 1760hz for twinkle twinkle song hence close to 1400hz can be considered as threshold.
      {
        logs[i] = 1 ;                                                //set value high
        Serial.println(1);
        myservo.writeMicroseconds(1000);                            //rotate servo in clockwise
      }
      else
      {
        logs[i] = 0 ;                                               //set value low
        Serial.println(0);
        if (currentMillis - previousMillis >= interval)
        {
          previousMillis = currentMillis;
          if (count == 1)
          {
            myservo.writeMicroseconds(1500);
            count = 0;
            digitalWrite(LED_BUILTIN, HIGH);
          }
          else
          {
            myservo.writeMicroseconds(2000);              //rotate servo in clockwise direction
            count++;
            digitalWrite(LED_BUILTIN, LOW);
          }
        }
      }
      if (currentMillis - previousMillispir >= headsup)
      {
        previousMillispir = currentMillis;
        if (left == 1)                                  //check if left motion sensor detects the motion
        {
          left = 0;
          baseServo.writeMicroseconds(2000);            //rotate servo associated with basse to left
        }
        else if (right == 1)                            //check if right motion sensor detects the motion
        {
          baseServo.writeMicroseconds(1000);            //rotate servo associated with base to right
          right = 0;
        }
        else                                           //servo reset
        {
          baseServo.writeMicroseconds(1500);
        }
      }

      i++;                              //increment array index of log to store next value
      tone(tonePin, Freq);              //play the note
      if (x == MelodyLength - 1)        //to check if playing song ended.
      {
        Serial.println(" End of Playback");
      }
    }
    int16_t Duration = data >> 8;       //play the song for duration spefied in melody array 
   while (Duration--) delay(tempo);     
  }
  noTone(4);                            //once song is played maintain silence
  myservo.writeMicroseconds(1500);      //reset skirt pin servo
  //-----
}
//-----------------------------------
inline static void playMelody(uint8_t melodyNumber, uint8_t tempo)
{
  switch (melodyNumber)
  {
#if NUMBER_OF_MELODIES > 0
    case 0: {
        playMelody_Data(Melody0, Melody0_Length, tempo);      
        return;
      }
#endif
  }
}