Arduino plays music

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.

Arduino beeper schema
Arduino beeper schema

(above image doesn’t report motor presence)

square wave to drive the beeper
square wave to drive the beeper

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

  1. complete music notes support
  2. 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)

Tags

1 Comment

Click here to post a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • 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!