As first project with Arduino I thought to build a sound player with “music notes” indicator. There are several tutorials ready to use but I would try to start from scratch. In order to play music for “Music Notation” I chose to use “English Notation” and implement a command like that : PLAY “CDEFG” (do you remember C=128?)
In “English Notation” each note correspond to an alphabetic letter for example note DO -is-> C letter. “The notes of the 12-tone scale can be written by their letter names A-G, possibly with a trailing sharp or flat symbol, such as A♯ or B♭. This is the most common way of specifying a note in English speech or written text.”
Each note correspond to a frequency:
https://entertainment.howstuffworks.com/guitar2.htm
264 Hz - C, do
297 Hz - D, re
330 Hz - E, mi
352 Hz - F, fa
396 Hz - G, sol
440 Hz - A, la
495 Hz - B, si
528 Hz - C, do
As said the simple idea is to have a command that plays music … in order to get this I defined following function:
char *musicNotes="-:10000 C:2000";
play_music(musicNotes);
where format of musicNotes is <note>:<duration_ms> and – symbol means silence/pause.
So a composition is for example like this :
-:4000 c:800 e:400 e:400 e:400 d:200 c:400 g:800 -:400 g:500 f:400 e:400 e:400 e:400 d:200 c:500 g:800 -:400 g:400 f:400 e:300 f:400: g:400 f:400 e:400 d:800 -:5000 (by Stefania)
How to make sound
I used a piezoelectric speaker (do you remember beep of Sinclair Spectrum, old PC IBM,…?). This component is also named beeper, buzzer or cicalino (in italian). Piezoelectric crystal has the property to expand or reduce its volume based on the difference of potential which is applied to it. This “alternate” expansion and reduction of spacial volume generates a pressure wave in air. This changing of pressure wave (with a frequency) is for our ears a sound. Frequency (which correspond to a specific music note) is the speed of expansion and reduction.
In order to drive the piezoelectric beeper we attach it to TTL Arduino output.

(above image doesn’t report motor presence)

If we need to reproduce DO note then we need to expand and contract volume 264 times each second (DO correspond to 264Hz). So we need to change with this frequency the difference of potential that we apply to beeper.
So for note DO the frequency is: 264Hz so output pin should be HIGH and LOW (1 cycle/period) 264 times/second.
T = 1/264 = 0.003787 sec
T/2 = 0.00189393939 sec
So pin output has to be HIGH (5V) for T/2 second and LOW (0V) for T/2
Pseudo code :
1. turn HIGH (5V) Arduino output pin 5 2. delay di 1893 microsec 3. turn LOW (0V) Arduino output pin 5 4. delay di 1893 microsec 5. if duration on note isn't reached then goto 1
Source Code :
#include <string.h>
#include <Servo.h>
#define AUDIOPIN 5
#define MOTORPIN 9
int did=0;
Servo mservo;
void setup(){
Serial.begin(9600);
pinMode(AUDIOPIN,OUTPUT);
mservo.attach(MOTORPIN);
Serial.println("===Start===");
}
void loop(){
char *musicNotes="E:500 E:500 F:500 G:500 G:500 F:500 E:500 C:500 D:500 E:500 E:500 F:500 F:500 E:500 E:500 F:500 G:500 G:500 F:500 E:500 D:500 C:500 C:500 D:500 E:500 D:500 C:500 C:800";
//char *musicNotes="a:500 e:250 c:500 c:750 c:250 e:250 a:500 e:250 a:500 e:250 c:750 d:250 f:400";
//char *musicNotes="A:400 a:400 g:500 -:300 f:200 a:200 g:400 f:400 D:400 g:400 f:400 D:400 d:400 -:100 f:400";
//char *musicNotes="-:10000 C:2000 D:2000 E:2000 F:2000 G:2000 A:2000 B:2000";
if (!did){
Serial.println("play music ...");
play_music(musicNotes);
Serial.println(musicNotes);
}
did=1;
}
void play_music(char *musicString){
char note;
unsigned long duration_ms;
char *s;
char *n;
char *musicString_tmp;
musicString_tmp = (char *)malloc(strlen(musicString)+1);
if (musicString_tmp == NULL)
return;
strncpy(musicString_tmp,musicString,strlen(musicString));
s = strtok_r(musicString_tmp, " ",&n);
do {
note = s[0];
duration_ms = atoi(&s[2]);
Serial.print(note);
Serial.println(duration_ms);
play_tone(note,duration_ms);
} while( ( s = strtok_r(NULL, " ",&n) ) !=NULL );
free(musicString_tmp);
}
void play_tone(unsigned char letter,unsigned long duration_ms){
switch (letter){
case 'C':;
case 'c':
mservo.write(0);
play(264.0,duration_ms);
break;
case 'D':;
case 'd':
mservo.write(20);
play(297.0,duration_ms);
break;
case 'E':;
case 'e':
mservo.write(40);
play(330.0,duration_ms);
break;
case 'F':;
case 'f':
mservo.write(60);
play(352.0,duration_ms);
break;
case 'G':;
case 'g':
mservo.write(80);
play(396.0,duration_ms);
break;
case 'A':;
case 'a':
mservo.write(100);
play(440.0,duration_ms);
break;
break;
case 'B':;
case 'b':
mservo.write(120);
play(495.0,duration_ms);
break;
case '#':
play(528.0,duration_ms);
break;
case '-':
play(0,duration_ms);
break;
}
}
void play(float freqHz, unsigned long duration_ms){
float delayTime = 1000000.0f/(2.0f*freqHz);
unsigned long time = millis();
while( time + duration_ms > millis() ){
if (freqHz > 0.0f){
digitalWrite(AUDIOPIN,HIGH);
delayMicroseconds(delayTime);
digitalWrite(AUDIOPIN,LOW);
delayMicroseconds(delayTime);
} else {
delayMicroseconds(delayTime*2.0f);
}
}
}
TODO
- complete music notes support
- support for this notation :
2:4|A–a–g—-fa–g–f–D–g–f–D-d—f——–d-c—c–d–D–c–d-D—f–f–
2:3|————————————————A————————–
BONUS
Some links :
http://www.sengpielaudio.com/calculator-notenames.htm
http://tabnabber.com/documents/howtoreadtabs.asp
(some sheets music)
http://tabnabber.com/MidiToTabConverter.aspx
(midi converter to English Notation)
http://www.gametabs.net/games
(sheets music of games)
http://home.swipnet.se/~w-22134/nmm/mitten.html
(midi game)
I like the helpful information you provide in your
articles. I will bookmark your weblog and check again here regularly.
I am quite sure I’ll learn plenty of new stuff right here! Good luck for the next!