Pages

Wednesday, November 30, 2011

RTTTL Player

The code runs at a clock frequency of 8 MHz. The controller is programmed using STK500 in ISP programming mode. RTTTL is a text encoding containing the information about the characteristics of a song. Here is a sample tone in RTTTL format given below and in that mentioned the default duration (d), octave(o), and beats per minute (b) information is specified.

Happy Birthday Song:d=4,o=5,b=125:8g.,
16g,a,g,c6,2b,8g.,16g,a,g,d6,2c6,
8g.,16g,g6,e6,c6,b,a,8f6.,16f6,e6,
c6,d6,2c6,8g.,16g,a,g,c6,2b,8g.,16g,
a,g,d6,2c6,8g.,16g,g6,e6,c6,b,a,
8f6.,16f6,e6,c6,d6,2c6

       Songs have been stored in the Flash memory of the microcontroller using the macro PROGMEM.

char song1[] PROGMEM = "Happy Birthday
Song:d=4,o=5,b=125:8g.,16g,a,g,c6,
2b,8g.,16g,a,g,d6,2c6,8g.,16g,g6,e6,
c6,b,a,8f6.,16f6,e6,c6,d6,2c6,8g.,
16g,a,g,c6,2b,8g.,16g,a,g,d6,2c6,
8g.,16g,g6,e6,c6,b,a,8f6.,16f6,e6,
c6,d6,2c6";

      Here is the code for decoding the format according to the RTTTL specifications:
// format: d=N,o=N,b=NNN:
// find the start (skip name, etc)
while(pgm_read_byte(p) != ':')
p++;                                            // skip ':'
p++;                                            // Moving to 'd'
// get default duration
if(pgm_read_byte(p) == 'd')
{
p++;                // skip "d"
p++;          // skip "="
num = 0;
while(isdigit(pgm_read_byte(p)))
{
num = (num * 10) + (pgm_read_byte(p++) - '0');
}
if(num > 0)
default_dur = num;
p++;                                            // skip comma
}
// get default octave
if(pgm_read_byte(p) == 'o')
{
p++;                                      // skip "o"
p++;                                      // skip "="
num = pgm_read_byte(p++) - '0';
if(num >= 4 && num <=8)
default_oct = num;
p++;                 // skip comma
}
// get BPM
if(pgm_read_byte(p) == 'b')
{
p++;                                      // skip "b="
p++;                                      // skip "b="
num = 0;
while(isdigit(pgm_read_byte(p)))
{
num = (num * 10) + (pgm_read_byte(p++) - '0');
}
bpm = num;
p++;                                      // skip colon  
}
// BPM usually expresses the number of quarter notes per minute
wholenote = (((60.0 * 1000.0) / (float)bpm) * 4.0);
// this is the time for whole note (in milliseconds)
// now begin note loop
while(pgm_read_byte(p))
{
// first, get note duration, if available
num = 0;
while(isdigit(pgm_read_byte(p)))
{
num = (num * 10) + (pgm_read_byte(p++) - '0');
}
if(num)
duration = wholenote / (float)num;      //milliseconds of the time to play the note
else
duration = wholenote / (float)default_dur;
// we will need to check if we are a dotted note after
// now get the note
note = 0;
switch(pgm_read_byte(p))
{
case 'c':
note = 1;
break;
case 'd':
note = 3;
break;
case 'e':
note = 5;
break;
case 'f':
note = 6;
break;
case 'g':
note = 8;
break;
case 'a':
note = 10;
break;
case 'b':
note = 12;
break;
case 'p':
note = 0;
}
p++;
// now, get optional '#' sharp
if(pgm_read_byte(p) == '#')
{
note++;
p++;
}
octave = top[note];
// now, get optional '.' dotted note
if(pgm_read_byte(p) == '.')
{
duration += duration/2;
p++;
}
// now, get scale
if(isdigit(pgm_read_byte(p)))
{
scale = pgm_read_byte(p) - '0';
p++;
}
else
{
scale = default_oct;
}
/* Process octave */
switch (scale)
{
case 4 : /* Do noting */                // x>>y = x/2*y
break;
case 5 : /* %2 */
octave = octave >> 1;
break;
case 6 : /* %4 */
octave = octave >> 2;
break;
case 7 : /* %8 */
octave = octave >> 4;
break;
case 8 : /* %16 */
octave = octave >> 8;
break;
}
if(pgm_read_byte(p) == ',')
p++;                 // skip comma for next note

         After we get the scale and duration of a note, we play the note for the specified duration. This is achieved by two timers, Timer0 for duration and Timer1 in PWM mode, to produce a square wave of a particular frequency by setting the TOP value of the OCR1C register.

DDRB |= (1<<PB3);                           //Setting the PWM channel output pin
TCCR0A &= ~(1<<WGM00);                       //Normal mode
TCCR0B |= ((1<<CS02) | (1<<CS00));            //Prescalar 1024
if(note)                                    //If a note occurs
{
TCCR1A |=  ((1<<COM1B1) | (1<<PWM1B));        //Non inverting mode, Fast PWM
TCCR1B |=  ((1<<CS13) | (1<<CS10));     //Prescalar 256
TCCR1C |= (1<<COM1B1);                 //Clear on compare match
TCCR1D &=~((1<<WGM11) | (1<<WGM10));
OCR1C = octave;                        //setting up Top value
OCR1B = (OCR1C>>1);                    //50% duty cycle
TCNT0L = 0;
for(;;)
{
if(TCNT0L >= 78)                 //Duration checking
{
duration = duration - 10.0;
TCNT0L = 0;
}
if(duration <= 0.00)
break;
}
TCCR0B = 0x00;
}
else                                        //If a pause occurs
{
TCNT0L = 0;
for(;;)
{
if(TCNT0L >= 78)                 //Duration checking
{
duration = duration - 10.0;
TCNT0L = 0;
}
if(duration <= 0.00)
break;
}
TCCR0B = 0x00;
}

Enjoy...

0 comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...