//Melody - Will play a melody
// Sort-of-MIDI
// Alter the variable string 'melody' in setup() to change the melody played.
// notes are defined with 4 characters
// Xa01
// X is the note letter, [A-F], capitals only.
// a is the accidental, or modifier. s for sharp [+1 semitone], n for natural[+0 semitone] b for flat [-1 semitone]. lowercase only.
// 0 is the octave modifier, [0-9]. C4 is middle C. Octave -1 was not implemented because fuck you.
// 1 is the length number of the note, [0-9]. Common range [3-7]
// 9: longa
// 8: double whole note/breve
// 7: whole note/semibreve
// 6: half note/minim
// 5: quarter note/crotchet
// 4: eighth note/quaver
// 3: sixteenth note/semiquaver
// 2: thirty-second note/demisemiquaver
// 1: sixty-fourth note/hemidemisemiquaver
// 0: hundred twenty-eighth note/Quasihemidemisemiquaver/Semihemidemisemiquaver
//
// The melody string can be however long a char array is allowed to be, I couldn't find it in the reference but I didn't look very hard.
// It must consist of only a series of note definitions, Xa01Xa01Xa01
// E.G. "Cn44Gn43En43An34Bn45As45An35Gn46En44Gn45An34Fn43Gn46En43Cn45Dn45Bn44"
//Constants
#define speakerPin 12 //Pin the speaker is connected to
#define quavLength 600 //How long a quaver lasts. 600 should give 100BPM, I think.
#define midA 440 //Frequency of middle A in Hz all not frequencies are derived from here.
#define errorLED 13 //Pin that the error LED is connected to. Built in LED on Mega is 13.
float* globF; // Global for the frequencies
int* globL; // Global for the lengths
int numNotes; //how many notes there are in the melody. Also needs to be passed from setup() to loop()
//Functions:
void error(String message) { // What to do in case of error
digitalWrite(errorLED, HIGH); // Turn the error light on
Serial.println(message); // Print an error message
delay(10000); // Wait 10 seconds
}
float switchNote(char X, char a, int o) { // Convert note to frequency
//Based on the modifier, convert note to a step number using maths with the ASCII values
int noteStep;
if (a=='s') {
noteStep = X - 63;
}
else if (a=='n') {
noteStep = X - 64;
}
else if (a=='b') {
noteStep = X - 65;
}
else {
error("Invalid Modifier");
}
int steps = o-4;
steps = steps * 12;
if(steps>=0) {
steps = steps + noteStep;
}
else if(steps<0) {
steps = steps - noteStep;
}
else {
error("This error should be impossible");
}
float exponent = steps/12;
float tempow = pow(2, exponent);
float frequency = tempow * midA; // Calculated using the midi equation from http://en.wikipedia.org/wiki/Note
return frequency;
}
float findLength(int y) { // Turn the length value into a number of milliseconds
float temp = 0.625; // How many quavers in a Semihemidemisemiquaver
for(int i = 0;i<y;i++) { // Convert the note length int into a factor to multiply the quaver length by
temp = temp + temp;
}
return temp;
}
//The standard functions
void setup() {
pinMode(speakerPin, OUTPUT);
Serial.begin(9600);
//The melody to play goes here
char melody[] = "Cn64Gn63En63An64Bn65As65An65Gn66En64Gn65An64Fn63Gn66En63Cn65Dn65Bn64\00";
// Must be nothing but a series of 4 note strings.
//edit: Luke. added null at the end to be safe.
//How many notes long is the melody?
numNotes = (strlen(*melody) -1) / 4;
//edit: strip the null first.
//I don't know why the leading * is needed, but apparently it is.
//edit: Luke Because without the * you are dividing the len of a pointer by 4 insted of whats addressed by it
//Arrays to hold the frequencies and lengths of the notes.
float* freqs = (float*) malloc(numNotes*sizeof(float)); //because float freqs[numNotes]; would be too simple
int* lengths = (int*) malloc(numNotes*sizeof(int));
for(int i=0; i<numNotes; i++) { //separate the melody out into notes etc
char currNote[5];
currNote[0] = melody[4*i];
currNote[1] = melody[(4*i)+1];
currNote[2] = melody[(4*i)+2];
currNote[3] = melody[(4*i)+3];
currNote[4] = char(0); //Make the last character a NULL/terminator
int hurr = int(currNote[2]) - 48; //fucking char - int conversions
int durr = int(currNote[3]) - 48;
freqs[i] = switchNote(currNote[0], currNote[1], hurr); //convert the note to a frequency and store it in freqs
lengths[i] = findLength(durr); //Convert the timing
}
globF = freqs; //Put the array of frequencies into the global so loop() can read it
globL = lengths; //Do the same for the lengths
Serial.print(freqs);
Serial.println("<--freqs (setup)");
Serial.print(String(lengths));
Serial.println("<--lengths (setup)");
}
void loop() {
Serial.print(String(globF));
Serial.println("<--freqs (loop)");
Serial.print(String(globL));
Serial.println("<--lengths (loop)");
int gapLength = 150; //How long the speaker is silent for before it plays the next note
for(int i=0; i<numNotes; i++) { // Not sure if this will use the right value for numNotes, should be tested with Serial.print.
int playLength = globL[i] - gapLength;
tone(speakerPin, globF[i], playLength);
delay(globL[i]); // Not entirely sure if this should be gapLength, globL[i], or something else
}
delay(5000); //Pause for 5 seconds before repeating the melody
}