Pololu Robotics & Electronics
Menu
My account Comments or questions? About Pololu Contact Ordering information Distributors

Orangutan plays Gershwin...or not


#1

I may have tried to push the boundaries of buzzer music a little too far here.

I was trying to think of the most un-piezo-buzzer-like song I could try to make the Orangutan play, something with a huge range of notes and an irregular, syncopated rhythm, but that could still be approximated with single notes. I like lots of music, but Rhapsody in Blue by George Gershwin is one of my all time favorites, and it seemed perfect.

I basically tried to rework Jim Remington’s buzzer code to cover six octaves. The basic changes I made were to use an integer array for the notes and durations and 16-bit timer1 to generate the frequencies. Here is my not-quite-working attempt if anyone feels like taking a look. FYI: In this version you push the left button on the Orangutan to play.

Stored in an integer array, the entire song won’t fit in the ATMega168’s flash (which seems odd to me, does the array structure really add that much overhead?). Furthermore, even with just a snipped of the beginning of the song I get odd behavior, mainly weird pauses. If I try to compile a lot of the song, like half or more, I get even weirder behavior, like the Orangutan freezing up on the first note! The actual operation of the code is fairly simple, so I’m guessing either:

A) I’m doing something very basically wrong that I’m just not seeing

B) I’m running into weird memory/variable storing/loading/capacity problems

I’m leaning towards B right now, it wouldn’t be the first time I ran up against such a vagary of microcontroller hardware. Is there a reachable variable limit and a way to tell if you’ve gone completely over it? Since I declare the array static I assumed it would be just written to flash as is, or is there a more involved way to do this?

Alternatively, now that you guys at Pololu have the X2 and 3pi prototypes playing all sorts of songs, any thoughts on different approaches to playing buzzer music and/or approaches to storing the large static tune arrays?

-Adam

P.S. Looking forward to playing Gershwin on a 3pi!


Who's good at transcribing a few measures for 3pi?
#2

Hello Adam,
Have you taken a look at the new music functions included in our Orangutan Arduino library? We have implemented something pretty close to the old GW-BASIC “play” command, allowing you to type in your notes as a big character string that is reminiscent of musical notation, e.g. “e.d8cdeee” (Mary had a little lamb). We have a version that accepts a PROGMEM string, so that all of the bytes are kept in program space. We’re going to release a C/C++ version of the library pretty soon.

OrangutanBuzzer documentation

Anyway, I think you need to give your integer array a PROGMEM declaration to prevent it from getting loaded into RAM. If I try that and use pgm_read_word to access the memory, I can think I can hear the whole tune. It sounds a bit weird toward the end, but I bet you just have some wrong notes.

-Paul


#3

Yeah, as Paul said, if you don’t explicitly declare your array to be in program memory, it will take up both flash space and RAM space, and you start running into all sorts of weird problems if you take up too much RAM since RAM is also used for the stack. The stack builds from one side and your data from the other, and when they collide and overwrite each other bad things happen.

- Ben


#4

Thanks, I thought it must be something like that. I’ll give it a shot…after the 3pm weekly lab meeting of course. Somewhere sometime I read that declaring variables const in winAVR C wrote them directly to flash, but I can’t seem to find the reference and that’s obviously not the case.

Is there any way to protect against ram collisions like this from within the program, or at least check if they have happened and indicate to the outside world what has happened. I’m a little concerned about this happening in more critical situations, like, say, on my helicopter. Fortunately it has a big net to crash into, but it’s a pain to keep untangling it!

-Adam


#5

I’m not aware of any such way of detecting these problems from within your program. In general, my approach has been to leave plenty of free space in RAM for the stack (if possible) and then trace my function calls to try to predict the maximum amount of stack space I’m going to need. AVR Studio tells you how many bytes of data it thinks you’re using when you compile.

- Ben


#6

Well, poor memory management is one of the most persistent problems affecting programs at all scales, from microcontrollers to supercomputers, so it would be good for all of us to get better at dealing with it. Here’s what I came up with to measure the amount of free RAM remaining on an AVR:

extern void __bss_end;
extern void *__brkval;

int get_free_memory()
{
  int free_memory;

  if((int)__brkval == 0)
    free_memory = ((int)&free_memory) - ((int)&__bss_end);
  else
    free_memory = ((int)&free_memory) - ((int)__brkval);

  return free_memory;
}

This came out of talking to a few people and studying the AVR malloc() documentation. The idea is that you need to subtract your current stack pointer (conveniently given by the address of a local variable) from a pointer to the top of the static variable memory (__bss_end). If malloc() is being used, the top of the heap (__brkval) needs to be used instead. In a simple test, this function seemed to do the job, showing memory gradually being used up until, with around 29 bytes free, the program started behaving erratically.

What do you think about that? Also, did you ever get your Gershwin tune fully working on your Orangutan? I’d like to get a copy of the final tune.
-Paul


#7

That’s super-useful, I’m totally going to incorporate that in my helicopter autopilot.

I only spent about 10 more minutes on Gershwin so far with the PROGMEM macro, in which time I think I got it writing to memory okay, but I’m reading back gibberish. I’m haven’t quite figured out how to reassemble multiple bytes back into coherent ints.

My parents are visiting right now, but I’ll definitely play with it more tomorrow.

-Adam


#8

Still a bit flummoxed. I’ve rewritten the code and cleaned it up a lot, I’m writing the note array to memory (Data space is 0.5% full) and I’m still getting weird silences in the playback.

I don’t think anything is wrong with the actual note values in the array, if I change the tempo or copy and paste a chunk of it I get silences at different parts the second time around, and I get the same problems if I play tunes from Jim’s original buzzertunes program, so I’m thinking this is somehow related to using timer1 to generate the frequencies…mabye?

Anyway, here’s the current state of the code if anyone cares to have a look. Music playback isn’t all that important, but I would really like to know what’s going on here so I don’t make the same mistake in a program where it wouldn’t be as obvious!

-Adam


#9

Hi, Adam:

I’ve been away for awhile, but glad to see that you are at least trying to have fun with the music routines. Rhapsody in Blue would be great! I haven’t looked carefully at the code but I’m puzzled by the unusually large note durations. I originally intended the durations to be simple to set, i.e. 8=whole note, 4=half note, etc. with the actual note duration being set by “tempo”, however you have values of over 100, which when multiplied by 5 yields over 500 ms durations. How did you arrive at these numbers? I’ll look at the whole thing more carefully because it should work…

Jim


#10

Aside from the huge range of notes in Rhapsody, there’s also a lot of tempo variation, which was faked in the midi file I decompiled with a very fast tempo and a lot of long, tied together notes. The buzzer doesn’t even begin to capture the real feeling of the song, but it’s a cute parody if you already know the tune.

I’m really confused by this random silence problem though, which I run into even when the only changes I make to your buzzer_tunes2.c is to move the tone generation from timer2 to timer1 (loading the tone into OCR1A, after setting the appropriate registers for CTC mode with output compare A as the top, enabling the compare A interrupt, setting a 64 prescale and switching buzzer toggle interrupt to TIMER1_COMPA_vect).

Thanks for looking into it, I would hate to have to stay away from the 16 bit counter!

-Adam


#11

Adam:

Part of the problem is that there are many repeated notes in Rhapsody in Blue. I only recently learned that in electronic music, it is customary for the last 1/8 duration of each note to be silent, to mimic the effect of a musician playing two successive (but identical in pitch) notes. Some tunes have few repeated notes, so neglecting this pause is not very noticeable, but it is fatal for Rhapsody in Blue. I’ve sort-of fixed that problem in the code that follows by setting the buzzer port to input for the last 1/8 duration. There don’t seem to be any actual, i.e. programmed, pauses in the tune array. Is this correct?

It sounds better now, but there are still too many weird timings. I think the gimmick of multiplying the duration by 5 (in your first example) and then dividing by 8 for the inter-note pause may be too crude. Could you dispense with the first element of the tune array and re-compile your MIDI source to give the actual note durations in milliseconds (including 1/8 duration pause)?

The version I’ve uploaded here is for the LV-168 with a 20 MHz clock and buzzer on PB2. Try it, I would like to see it work!

Cheers, Jim

/*
Gershwin on the Orangutan
Completely ripped off from Jim Remington's buzzer_tunes2.c
Which, in turn, was ripped off the Butterfly code from Atmel
Somewhat less of a mess, but still not entirely working
-Adam
Now closer
-Jim
*/
// LV-168 with 20 MHz clock and buzzer on PB2. 
// Timer constants not changed, so frequencies are too high by factor (20/8)
#define F_CPU 20000000 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define p   0   //pause = silence
#define	c1	2034
#define	cx1	1804
#define	d1	1703
#define	dx1	1607
#define	e1	1517
#define	f1	1435
#define	fx1	1351
#define	g1	1276
#define	gx1	1204
#define	a1	1136
#define	ax1	1073
#define	b1	1012
#define	c2	956
#define	cx2	902
#define	d2	851
#define	dx2	804
#define	e2	758
#define	f2	716
#define	fx2	676
#define	g2	638
#define	gx2	602
#define	a2	568
#define	ax2	536
#define	b2	506
#define	c3	478
#define	cx3	451
#define	d3	426
#define	dx3	402
#define	e3	379
#define	f3	358
#define	fx3	338
#define	g3	319
#define	gx3	301
#define	a3	284
#define	ax3	268
#define	b3	253
#define	c4	239
#define	cx4	225
#define	d4	213
#define	dx4	201
#define	e4	190
#define	f4	179
#define	fx4	169
#define	g4	159
#define	gx4	150
#define	a4	142
#define	ax4	134
#define	b4	127
#define	c5	119
#define	cx5	113
#define	d5	106
#define	dx5	100
#define	e5	95
#define	f5	89
#define	fx5	84
#define	g5	80
#define	gx5	75
#define	a5	71
#define	ax5	67
#define	b5	63
#define	c6	60
#define	cx6	56
#define	d6	53
#define	dx6	50
#define	e6	47
#define	f6	45
#define	fx6	42
#define	g6	40
#define	gx6	38
#define	a6	36
#define	ax6	34
#define	b6	32
#define	c7	30

const unsigned int tune[] PROGMEM={//RhapsodyInBlue
	20,
	30,f2,7,g2,9,a2,9,ax2,9,c3,6,d3,9,dx3,7,f3,11,g3,
	12,a3,12,ax3,15,c4,16,d4,22,dx4,16,f4,20,g4,29,a4,99,ax4,12,gx4,
	10,ax4,20,gx4,40,fx4,40,gx4,40,fx4,40,gx4,40,fx4,60,f4,50,dx4,90,d4,
	90,cx4,60,dx4,84,e4,103,f4,73,d4,50,ax3,8,gx3,12,ax3,118,gx3,62,fx3,
	51,f3,61,f3,113,f3,37,g3,9,gx3,19,g3,3,gx3,7,g3,45,f3,37,g3,
	10,gx3,15,g3,6,gx3,7,g3,45,f3,41,g3,7,gx3,10,g3,10,gx3,10,g3,
	57,f3,1,ax3,104,ax3,40,c4,8,cx4,12,c4,7,cx4,11,c4,42,ax3,38,c4,
	10,cx4,10,c4,10,cx4,7,c4,45,ax3,38,c4,10,cx4,10,c4,10,cx4,10,c4,
	72,ax3,106,dx4,118,dx4,13,f4,13,fx4,13,f4,39,dx4,87,dx5,39,c5,37,f4,
	40,fx4,40,g4,40,ax4,40,dx4,50,dx3,60,dx3,60,dx3,90,dx3,45,cx3,45,cx3,
	60,cx3,90,cx3,30,dx3,60,dx3,60,dx3,90,dx3,30,fx3,60,fx3,60,fx3,93,fx3,
	42,g3,65,dx3,55,c3,75,cx3,60,c3,60,ax2,61,fx2,58,g2,61,ax2,60,g2,
	61,dx2,1,dx3,21,f3,19,dx3,19,f3,21,dx3,20,f3,20,dx3,20,f3,10,dx3,
	11,f3,10,g3,10,gx3,10,ax3,10,c4,9,cx4,11,dx4,10,f4,10,g4,97,gx4,
	8,fx4,12,gx4,20,fx4,40,e4,40,fx4,40,e4,40,fx4,40,e4,60,dx4,60,cx4,
	60,c4,100,b3,70,cx4,70,d4,20,dx4,38,c4,62,gx3,118,fx3,62,e3,60,dx3,
	20,dx3,100,e2,5,fx2,5,gx2,5,a2,5,b2,5,cx3,5,d3,5,e3,5,fx3,
	5,gx3,5,a3,5,b3,5,cx4,5,d4,5,e4,5,fx4,5,gx4,1,a4,99,g4,
	21,a4,28,g4,41,f4,40,g4,40,f4,40,g4,40,f4,70,e4,60,d4,60,cx4,
	80,c4,100,d4,60,dx4,80,e4,100,cx4,60,a3,75,g3,104,f3,41,e3,60,e3,
	20,a4,95,g4,25,a4,30,g4,40,f4,40,g4,40,f4,40,g4,38,f4,72,e4,
	60,d4,60,cx4,80,c4,100,d4,60,dx4,80,e4,100,cx4,60,a3,90,g3,90,f3,
	40,e3,80,e3,29,e3,29,g3,32,a3,28,g3,42,ax3,60,ax3,49,dx3,29,a3,
	32,b3,30,a3,30,cx4,29,dx4,31,b3,30,a3,30,g3,28,ax3,32,c4,28,ax3,
	42,cx4,65,cx4,45,fx3,28,c4,32,d4,30,c4,30,e4,28,fx4,32,d4,30,c4,
	29,ax3,29,cx4,32,dx4,28,cx4,32,e4,60,e4,60,a3,28,dx4,32,f4,28,dx4,
	32,g4,28,a4,32,f4,30,dx4,30,cx4,28,e4,32,fx4,30,e4,29,g4,61,g4,
	60,c4,28,fx4,32,gx4,28,fx4,32,ax4,28,c5,32,gx4,30,fx4,30,e4,28,g4,
	32,a4,30,g4,28,ax4,62,ax4,60,dx4,28,a4,32,b4,28,a4,32,cx5,30,dx5,
	29,b4,31,a4,78,ax4,62,ax4,60,ax4,70,ax4,28,a4,20,gx4,62,gx4,60,gx4,
	68,gx4,30,a4,30,ax4,52,ax4,60,ax4,70,ax4,28,c5,22,cx5,60,cx5,60,cx5,
	41,cx5,77,g4,62,g4,60,g4,70,g4,28,fx4,20,f4,62,f4,58,f4,72,f4,
	28,fx4,30,g4,52,g4,60,g4,70,g4,28,a4,20,ax4,60,ax4,62,ax4,99,ax4,
	61,b4,18,g4,62,e4,40,g4,60,f4,60,e4,60,d4,60,ax3,58,b3,62,d4,
	58,b3,92,g3,88,ax4,60,b4,60,g4,62,ax4,60,b4,60,d4,60,e4,40,f4,
	38,e4,32,d4,8,fx4,30,g4,32,fx4,30,g4,30,fx4,60,g4,60,dx4,60,c4,
	61,g3,119,ax3,58,b3,60,g3,60,ax3,62,b3,60,d3,61,e3,37,f3,40,e3,
	30,d3,12,fx3,28,g3,32,fx3,30,g3,30,fx3,60,g3,58,e3,63,cx3,60,a2,
	117,f3,62,fx3,58,d3,62,f3,60,fx3,60,a2,61,b2,37,c3,42,b2,40,a2,
	38,d3,62,d3,20,d3,70,fx4,10,g4,60,g4,60,g4,50,a4,20,g4,30,fx4,
	60,f4,20,f4,60,f4,68,f4,32,fx4,60,g4,20,g4,60,g4,70,g4,20,gx4,
	10,a4,60,ax4,20,ax4,60,ax4,100,ax4,60,b4,60,g4,48,e4,32,g4,40,f4,
	60,e4,60,d4,60,ax3,58,b3,62,d4,60,b3,50,g3,80,fx4,10,g4,60,g4,
	60,g4,60,a4,8,g4,32,fx4,60,f4,20,f4,60,f4,70,f4,30,fx4,60,g4,
	20,g4,60,g4,70,g4,20,gx4,10,a4,60,ax4,20,ax4,60,ax4,98,ax4,62,b4,
	60,g4,48,e4,32,g4,40,f4,60,e4,60,d4,88,ax3,30,b3,62,d4,58,b3,
	92,g3,48,a4,62,a4,60,a4,40,b4,30,a4,30,gx4,60,g4,20,g4,60,g4,
	68,g4,32,gx4,58,a4,22,a4,58,a4,62,a4,20,ax4,20,b4,60,c5,20,c5,
	60,c5,100,c5,60,cx5,60,a4,50,fx4,10,a4,60,g4,60,fx4,60,e4,61,c4,
	59,cx4,60,e4,60,cx4,120,a3,120,gx4,20,a4,60,a4,60,a4,50,b4,20,a4,
	30,gx4,60,g4,20,g4,60,g4,70,g4,30,gx4,60,a4,20,a4,60,a4,60,a4,
	20,ax4,20,b4,60,c5,20,c5,60,c5,100,c5,60,cx5,60,a4,50,fx4,10,a4,
	60,g4,60,fx4,60,e4,61,c4,59,cx4,58,e4,62,cx4,60,a3,59,d2,61,e2,
	45,g2,13,g2,60,d2,62,e2,50,d2,20,e2,110,f2,38,e2,22,d2,20,a1,
	78,b1,80,a1,2,c2,58,d2,62,e2,45,g2,16,g2,58,d2,61,e2,48,d2,
	20,e2,92,f2,20,e2,10,f2,49,e2,61,d2,79,a1,81,b1,80,a1,119,c2,
	60,d2,61,e2,45,g2,16,g2,58,d2,62,e2,48,d2,19,e2,92,f2,20,e2,
	10,f2,49,e2,21,d2,18,c2,22,d2,80,c2,80,b1,80,c2,119,a1,81,b1,
	60,c2,60,d2,60,e2,60,fx2,60,g2,60,a2,80,ax2,30,fx2,111,g2,120,ax2,
	58,g2,26,ax2,35,fx2,1,g2,120,ax2,58,g2,61,f2,120,f2,119,f2,1,d2,
	1,f2,58,fx2,41,g2,40,gx2,40,a2,40,ax2,20,fx2,120,g2,1,ax2,45,g2,
	45,c3,30,g2,120,ax2,58,g2,61,f2,118,f2,1,f2,61,d2,
	0,0};

unsigned char tempo;
unsigned int tune_index,duration=0,silence=0;

ISR(TIMER0_COMPA_vect){
	if(duration>0){
		duration--;//keep playing note
		if (duration == silence) DDRB=~(1<<PB2);	//turn off buzzer output for last 1/8 duration
	}else{
		tune_index+=2;//increment memory address
		duration=tempo*pgm_read_word(tune_index);//read note length
		silence = duration>>3;			//time for inter-note pause
		tune_index+=2;//increment memory address
		OCR1A=pgm_read_word(tune_index);//read note freqency
		DDRB=(1<<PB2);		//set buzzer port to output
		if(OCR1A==0){//tune over
			TIMSK0&=~(1<<OCIE0A);//disable counter compare interrupts
			TIMSK1&=~(1<<OCIE1A);
		}
	}
}

ISR(TIMER1_COMPA_vect){
	PORTB^=(1<<PB2);//Toggle Buzzer
}

int main(void){
//	PORTB|=(1<<PB3);
//	while(!(PINB&(1<<PB3)));//Wait for button press

	DDRB|=(1<<PB2);//Set Buzzer Pin to Output
	
	//Initialize Timer 0
	TCCR0A|=(1<<WGM01);//Set CTC mode, OCR0A Top
	OCR0A=125;//set OCR0A
	TIMSK0|=(1<<OCIE0A);//Enable Compare A interrupt
	TCCR0B|=(1<<CS01)|(1<<CS00);//Set 64 prescaler

	//Initialize Timer 1
	TCCR1B|=(1<<WGM12)|(1<<CS11)|(1<<CS10);//Set CTC Mode, OCR1A Top, 64 prescaler
	TIMSK1|=(1<<OCIE1A);//Enable Compare A interrupt
	OCR1A|=0xFFFF;//Make OCR1A something other than 0 to start so cycles can occur

	tempo=pgm_read_word(tune);//set tempo
	tune_index=(int)tune;//point to current tune
	sei();
	
	while(1);

	return 0;
}

#12

I’m starting to suspect that the root of the problem is a weird collision of the timer interrupts.

To compare the difference between using timers 1 and 2 to generate tones I set up a program that initialized both timers 1 and 2, set both to CTC mode with a 64 prescaling, but which waited for a button press to enable either OCIE1A or OCIE2A. I was totally confused by the results of playing tones on timer1 at first, since sometimes sorcerer’s apprentice would play just fine, while other times it would have the odd pauses, but it would never play incorrectly the same way twice.

Looking over the code I realized that, while OCR0A was initially set to 125 (to generate the 1ms interrupts) I didn’t initialize OCR1A to some non-zero value, so until I pressed the button to load the first tone into it counter 1 was essentially not counting, and I was randomly varying the initial offset between the note counting timer and the frequency generating timer. I’m still not sure why this would only be a problem with using timers 0 and 1, and not a problem when using counters 0 and 2, but at least it’s progress.

-Adam

P.S. I wonder if this has something to do with the fact that Timers 0 and 1 share prescaler hardware, time to try to translate section 16 of the datasheet into English!


#13

Yahtzee, sort of.

So Timers 0 and 1 share a prescaler, which just means that they tick in lock-step if they use the same prescale value, and they’re operating at similar frequencies. I think what’s happening is that playing different notes for different durations, timer 1 is hitting multiples/factors of the timer0 frequency. Timer1 interrupts will take precedence if they occur on exactly the same clock cycle, but I could see timer0 delaying timer1’s interrupt routine at higher frequencies, changing the sound produced in weird ways. Does this sound remotely plausible? Darn, now I sort of wish I had invested in one of those fancy on-chip debugging programmers. Oh well.

Anyway, I’ll be sure to be careful of interrupt generating clocks running close to each other in the future, but it occurs to me that you really don’t need to use a separate counter just to decrement the note duration variable. Instead you can scale the duration inversely to the note frequency and use the frequency generating counter to decrement it. Here’s a working version that only uses one timer. It happens to be timer1 for the octave range I need, but there’s no reason this approach wouldn’t work just as well with one 8 bit timer. Jim, I also took your advice (sort of) and added a 5ms delay between notes (also interrupt driven), which makes things sound even nicer:

/*
Gershwin on the Orangutan, using only ONE TIMER
Mostly ripped off from Jim Remington's buzzer_tines2.c
Only slightly a mess now
-Adam
*/

#define F_CPU 8000000 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define p   0   //pause = silence
#define	c1	2034
#define	cx1	1804
#define	d1	1703
#define	dx1	1607
#define	e1	1517
#define	f1	1435
#define	fx1	1351
#define	g1	1276
#define	gx1	1204
#define	a1	1136
#define	ax1	1073
#define	b1	1012
#define	c2	956
#define	cx2	902
#define	d2	851
#define	dx2	804
#define	e2	758
#define	f2	716
#define	fx2	676
#define	g2	638
#define	gx2	602
#define	a2	568
#define	ax2	536
#define	b2	506
#define	c3	478
#define	cx3	451
#define	d3	426
#define	dx3	402
#define	e3	379
#define	f3	358
#define	fx3	338
#define	g3	319
#define	gx3	301
#define	a3	284
#define	ax3	268
#define	b3	253
#define	c4	239
#define	cx4	225
#define	d4	213
#define	dx4	201
#define	e4	190
#define	f4	179
#define	fx4	169
#define	g4	159
#define	gx4	150
#define	a4	142
#define	ax4	134
#define	b4	127
#define	c5	119
#define	cx5	113
#define	d5	106
#define	dx5	100
#define	e5	95
#define	f5	89
#define	fx5	84
#define	g5	80
#define	gx5	75
#define	a5	71
#define	ax5	67
#define	b5	63
#define	c6	60
#define	cx6	56
#define	d6	53
#define	dx6	50
#define	e6	47
#define	f6	45
#define	fx6	42
#define	g6	40
#define	gx6	38
#define	a6	36
#define	ax6	34
#define	b6	32
#define	c7	30

const unsigned int tune[] PROGMEM={//RhapsodyInBlue
	7,
	30,f2,7,g2,9,a2,9,ax2,9,c3,6,d3,9,dx3,7,f3,11,g3,
	12,a3,12,ax3,15,c4,16,d4,22,dx4,16,f4,20,g4,29,a4,99,ax4,12,gx4,
	10,ax4,20,gx4,40,fx4,40,gx4,40,fx4,40,gx4,40,fx4,60,f4,50,dx4,90,d4,
	90,cx4,60,dx4,84,e4,103,f4,73,d4,50,ax3,8,gx3,12,ax3,118,gx3,62,fx3,
	51,f3,61,f3,113,f3,37,g3,9,gx3,19,g3,3,gx3,7,g3,45,f3,37,g3,
	10,gx3,15,g3,6,gx3,7,g3,45,f3,41,g3,7,gx3,10,g3,10,gx3,10,g3,
	57,f3,1,ax3,104,ax3,40,c4,8,cx4,12,c4,7,cx4,11,c4,42,ax3,38,c4,
	10,cx4,10,c4,10,cx4,7,c4,45,ax3,38,c4,10,cx4,10,c4,10,cx4,10,c4,
	72,ax3,106,dx4,118,dx4,13,f4,13,fx4,13,f4,39,dx4,87,dx5,39,c5,37,f4,
	40,fx4,40,g4,40,ax4,40,dx4,50,dx3,60,dx3,60,dx3,90,dx3,45,cx3,45,cx3,
	60,cx3,90,cx3,30,dx3,60,dx3,60,dx3,90,dx3,30,fx3,60,fx3,60,fx3,93,fx3,
	42,g3,65,dx3,55,c3,75,cx3,60,c3,60,ax2,61,fx2,58,g2,61,ax2,60,g2,
	61,dx2,1,dx3,21,f3,19,dx3,19,f3,21,dx3,20,f3,20,dx3,20,f3,10,dx3,
	11,f3,10,g3,10,gx3,10,ax3,10,c4,9,cx4,11,dx4,10,f4,10,g4,97,gx4,
	8,fx4,12,gx4,20,fx4,40,e4,40,fx4,40,e4,40,fx4,40,e4,60,dx4,60,cx4,
	60,c4,100,b3,70,cx4,70,d4,20,dx4,38,c4,62,gx3,118,fx3,62,e3,60,dx3,
	20,dx3,100,e2,5,fx2,5,gx2,5,a2,5,b2,5,cx3,5,d3,5,e3,5,fx3,
	5,gx3,5,a3,5,b3,5,cx4,5,d4,5,e4,5,fx4,5,gx4,1,a4,99,g4,
	21,a4,28,g4,41,f4,40,g4,40,f4,40,g4,40,f4,70,e4,60,d4,60,cx4,
	80,c4,100,d4,60,dx4,80,e4,100,cx4,60,a3,75,g3,104,f3,41,e3,60,e3,
	20,a4,95,g4,25,a4,30,g4,40,f4,40,g4,40,f4,40,g4,38,f4,72,e4,
	60,d4,60,cx4,80,c4,100,d4,60,dx4,80,e4,100,cx4,60,a3,90,g3,90,f3,
	40,e3,80,e3,29,e3,29,g3,32,a3,28,g3,42,ax3,60,ax3,49,dx3,29,a3,
	32,b3,30,a3,30,cx4,29,dx4,31,b3,30,a3,30,g3,28,ax3,32,c4,28,ax3,
	42,cx4,65,cx4,45,fx3,28,c4,32,d4,30,c4,30,e4,28,fx4,32,d4,30,c4,
	29,ax3,29,cx4,32,dx4,28,cx4,32,e4,60,e4,60,a3,28,dx4,32,f4,28,dx4,
	32,g4,28,a4,32,f4,30,dx4,30,cx4,28,e4,32,fx4,30,e4,29,g4,61,g4,
	60,c4,28,fx4,32,gx4,28,fx4,32,ax4,28,c5,32,gx4,30,fx4,30,e4,28,g4,
	32,a4,30,g4,28,ax4,62,ax4,60,dx4,28,a4,32,b4,28,a4,32,cx5,30,dx5,
	29,b4,31,a4,78,ax4,62,ax4,60,ax4,70,ax4,28,a4,20,gx4,62,gx4,60,gx4,
	68,gx4,30,a4,30,ax4,52,ax4,60,ax4,70,ax4,28,c5,22,cx5,60,cx5,60,cx5,
	41,cx5,77,g4,62,g4,60,g4,70,g4,28,fx4,20,f4,62,f4,58,f4,72,f4,
	28,fx4,30,g4,52,g4,60,g4,70,g4,28,a4,20,ax4,60,ax4,62,ax4,99,ax4,
	61,b4,18,g4,62,e4,40,g4,60,f4,60,e4,60,d4,60,ax3,58,b3,62,d4,
	58,b3,92,g3,88,ax4,60,b4,60,g4,62,ax4,60,b4,60,d4,60,e4,40,f4,
	38,e4,32,d4,8,fx4,30,g4,32,fx4,30,g4,30,fx4,60,g4,60,dx4,60,c4,
	61,g3,119,ax3,58,b3,60,g3,60,ax3,62,b3,60,d3,61,e3,37,f3,40,e3,
	30,d3,12,fx3,28,g3,32,fx3,30,g3,30,fx3,60,g3,58,e3,63,cx3,60,a2,
	117,f3,62,fx3,58,d3,62,f3,60,fx3,60,a2,61,b2,37,c3,42,b2,40,a2,
	38,d3,62,d3,20,d3,70,fx4,10,g4,60,g4,60,g4,50,a4,20,g4,30,fx4,
	60,f4,20,f4,60,f4,68,f4,32,fx4,60,g4,20,g4,60,g4,70,g4,20,gx4,
	10,a4,60,ax4,20,ax4,60,ax4,100,ax4,60,b4,60,g4,48,e4,32,g4,40,f4,
	60,e4,60,d4,60,ax3,58,b3,62,d4,60,b3,50,g3,80,fx4,10,g4,60,g4,
	60,g4,60,a4,8,g4,32,fx4,60,f4,20,f4,60,f4,70,f4,30,fx4,60,g4,
	20,g4,60,g4,70,g4,20,gx4,10,a4,60,ax4,20,ax4,60,ax4,98,ax4,62,b4,
	60,g4,48,e4,32,g4,40,f4,60,e4,60,d4,88,ax3,30,b3,62,d4,58,b3,
	92,g3,48,a4,62,a4,60,a4,40,b4,30,a4,30,gx4,60,g4,20,g4,60,g4,
	68,g4,32,gx4,58,a4,22,a4,58,a4,62,a4,20,ax4,20,b4,60,c5,20,c5,
	60,c5,100,c5,60,cx5,60,a4,50,fx4,10,a4,60,g4,60,fx4,60,e4,61,c4,
	59,cx4,60,e4,60,cx4,120,a3,120,gx4,20,a4,60,a4,60,a4,50,b4,20,a4,
	30,gx4,60,g4,20,g4,60,g4,70,g4,30,gx4,60,a4,20,a4,60,a4,60,a4,
	20,ax4,20,b4,60,c5,20,c5,60,c5,100,c5,60,cx5,60,a4,50,fx4,10,a4,
	60,g4,60,fx4,60,e4,61,c4,59,cx4,58,e4,62,cx4,60,a3,59,d2,61,e2,
	45,g2,13,g2,60,d2,62,e2,50,d2,20,e2,110,f2,38,e2,22,d2,20,a1,
	78,b1,80,a1,2,c2,58,d2,62,e2,45,g2,16,g2,58,d2,61,e2,48,d2,
	20,e2,92,f2,20,e2,10,f2,49,e2,61,d2,79,a1,81,b1,80,a1,119,c2,
	60,d2,61,e2,45,g2,16,g2,58,d2,62,e2,48,d2,19,e2,92,f2,20,e2,
	10,f2,49,e2,21,d2,18,c2,22,d2,80,c2,80,b1,80,c2,119,a1,81,b1,
	60,c2,60,d2,60,e2,60,fx2,60,g2,60,a2,80,ax2,30,fx2,111,g2,120,ax2,
	58,g2,26,ax2,35,fx2,1,g2,120,ax2,58,g2,61,f2,120,f2,119,f2,1,d2,
	1,f2,58,fx2,41,g2,40,gx2,40,a2,40,ax2,20,fx2,120,g2,1,ax2,45,g2,
	45,c3,30,g2,120,ax2,58,g2,61,f2,118,f2,1,f2,61,d2,
	0,0};

unsigned char tempo;
unsigned int tune_index;
long int duration=1;

ISR(TIMER1_COMPA_vect){
	duration--;

	if(duration>0){
		PORTB^=(1<<PB0);//Toggle Buzzer
	}else if(duration==0){
		OCR1A=125;//1ms count
	}else if(duration<-5){//after 5ms silence
		tune_index+=2;//increment memory address
		duration=tempo*pgm_read_word(tune_index);//read note length
		tune_index+=2;//increment memory address
		OCR1A=pgm_read_word(tune_index);//read note freqency
		if(OCR1A==0){//tune over
			TIMSK0&=~(1<<OCIE0A);//disable counter compare interrupts
			TIMSK1&=~(1<<OCIE1A);
		}
		duration=duration*125/OCR1A;
	}
}

int main(void){
	while(!(PINB&(1<<PB3)));//Wait for button press

	DDRB|=(1<<PB0);//Set Buzzer Pin to Output
	
	//Initialize Timer 1
	TCCR1B|=(1<<WGM12)|(1<<CS11)|(1<<CS10);//Set CTC Mode, OCR1A Top, 64 prescaler
	OCR1A|=0xFFFF;//Make OCR1A something other than 0 to start so cycles can occur
	TIMSK1|=(1<<OCIE1A);//Enable Compare A interrupt

	tempo=pgm_read_word(tune);//set tempo
	tune_index=(int)tune;//point to current tune
	sei();
	
	while(1);

	return 0;
}

At some point I’ll add some more comments too, I stripped out all of Jim’s helpful ones to make more code fit on one screen. The buzzer really chokes on the low notes at the end, but all in all I’m pleased. Here’s the real deal for comparison.

-Adam


#14

This is how we do it on the X2, since we didn’t have additional timers to spare for duration timing. The OrangutanBuzzer documentation Paul linked to earlier in this thread uses the same technique (i.e. counts timer1 overflows in order to achieve the desired duration).

- Ben


#15

D’oh, I completely missed that. I was just looking at how the note arrays were written to and read back from memory.

-Adam


#16

Nice work! I noticed that the Pololu music routines for the X2 also use only one timer, based on the same idea.

Now, if there were an easy way to generate a music array from an arbitrary tune,
life would be even better. Maybe just scan in a score? Finally, we need polyphonic output for our robots!

Jim


#17

I totally agree. That midi-decompiling program you found is great, and the source file I used to generate the note array only had three tracks. I’m really tempted to wire up two more buzzers…

-Adam


#18

Adam:
Try this (it is more suitable for the range of buzzer pitch)

      OCR1A=(pgm_read_word(tune_index)>>2);//read note frequency and transpose up two octaves

Still seem to be a few flaky notes, but well worth the effort!

Finally, you do a divide by zero (OCR1A) at the end of the tune. I wonder what happens then?

Jim


#19

Just stumbled on this, it really makes you appreciate the power of modern computers and microcontrollers to do all sorts of things, from factoring primes to playing Baa Baa Black Sheep:
‘Oldest’ computer music unveiled

-Adam


#20

Hi, I just wanted to mention in this thread that our new play() function and the handy get_free_ram() function are included in the C/C++ libraries that we released this week, in case you want to try them out.