Orangutan plays Gershwin...or not

Support for the A-Star and the Orangutan family of robot controllers.

Orangutan plays Gershwin...or not

Postby nexisnet » Wed Jun 11, 2008 6:57 am

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.

Image

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!
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...or not

Postby paul » Wed Jun 11, 2008 9:10 am

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
User avatar
paul
Pololu Employee
 
Posts: 1491
Joined: Sat Nov 29, 2003 11:06 pm
Location: Las Vegas, NV

Re: Orangutan plays Gershwin...or not

Postby Ben » Wed Jun 11, 2008 9:31 am

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
User avatar
Ben
Pololu Employee
 
Posts: 3455
Joined: Mon Aug 28, 2006 12:05 pm
Location: Las Vegas, NV

Re: Orangutan plays Gershwin...or not

Postby nexisnet » Wed Jun 11, 2008 10:21 am

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
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...or not

Postby Ben » Wed Jun 11, 2008 10:32 am

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
User avatar
Ben
Pololu Employee
 
Posts: 3455
Joined: Mon Aug 28, 2006 12:05 pm
Location: Las Vegas, NV

Re: Orangutan plays Gershwin...or not

Postby paul » Sat Jun 14, 2008 9:13 pm

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:

Code: Select all Expand


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
User avatar
paul
Pololu Employee
 
Posts: 1491
Joined: Sat Nov 29, 2003 11:06 pm
Location: Las Vegas, NV

Re: Orangutan plays Gershwin...or not

Postby nexisnet » Sun Jun 15, 2008 5:50 am

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
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...or not

Postby nexisnet » Mon Jun 16, 2008 8:16 am

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
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...or not

Postby Jim Remington » Mon Jun 16, 2008 5:57 pm

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
Jim Remington
 
Posts: 623
Joined: Mon Aug 21, 2006 10:32 pm
Location: Eugene,OR

Re: Orangutan plays Gershwin...or not

Postby nexisnet » Mon Jun 16, 2008 6:39 pm

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
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...or not

Postby Jim Remington » Mon Jun 16, 2008 8:02 pm

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

Code: Select all Expand
Jim Remington
 
Posts: 623
Joined: Mon Aug 21, 2006 10:32 pm
Location: Eugene,OR

Re: Orangutan plays Gershwin...or not

Postby nexisnet » Tue Jun 17, 2008 7:16 am

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!
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...for real this time!

Postby nexisnet » Tue Jun 17, 2008 9:31 am

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:

Code: Select all Expand


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
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Re: Orangutan plays Gershwin...or not

Postby Ben » Tue Jun 17, 2008 10:14 am

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
User avatar
Ben
Pololu Employee
 
Posts: 3455
Joined: Mon Aug 28, 2006 12:05 pm
Location: Las Vegas, NV

Re: Orangutan plays Gershwin...or not

Postby nexisnet » Tue Jun 17, 2008 10:17 am

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

-Adam
User avatar
nexisnet
 
Posts: 1216
Joined: Mon Mar 21, 2005 8:29 pm
Location: Boston, Massachusetts

Next

Return to A-Star and Orangutan Robot Controllers

Who is online

Users browsing this forum: Yahoo [Bot] and 1 guest

cron