gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Multiple timer interrupts and sound corruption

#141498 - ingramb - Wed Sep 26, 2007 9:40 am

Still working on sound streaming. I have several different streaming channels, with 2 possible frequencies. So I need 2 timers for each different frequency, like so (all of this is on the arm7):

Code:
TIMER_DATA(0) = TIMER_FREQ(freq1);
TIMER_CR(0) = TIMER_ENABLE;
   
TIMER_DATA(1) = 65536 - streamSize1;
TIMER_CR(1) = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;

TIMER_DATA(2) = TIMER_FREQ(freq2);
TIMER_CR(2) = TIMER_ENABLE;

TIMER_DATA(3) = 65536 - streamSize2;
TIMER_CR(3) = TIMER_ENABLE | TIMER_IRQ_REQ | TIMER_CASCADE;


My timer interrupt handlers look like this:

Code:
void Timer1Handler()
{
   NEOIPC->audioStreamCount++;
}

void Timer3Handler()
{
   NEOIPC->audioStreamCount2++;
}


Everything is fine if I just use timer0 and timer1. I don't have any audio streaming hooked up to timer2 and timer3 yet, but if I even enable them, the audio I have gets hosed. It's like the timer3Handler is causing everything to get out of sync.

But this doesn't make sense to me, because at worst, the timer1Handler will only be delayed a few instructions by the timer3Handler. And I'm only polling these counters on the arm9 once per vblank, so if the counter is slightly delayed, it shouldn't matter (as long as the timer resets itself and continues counting with no delay).

Any ideas as to what's causing this?

#141501 - simonjhall - Wed Sep 26, 2007 10:07 am

I'd avoid the interrupts - have them as infrequently as possible! It looks to me that you're going to get two interrupts (one from each stream) after every single sample that's played. I don't think you want this! As the interrupts handlers are just doing counts, stick cascaded timers after the sample count timers THEN use interrupts. That way you'll get an interrupt every time a much larger amount of music is played (eg half a buffer's worth, telling you that it's time to copy in new data).

Another thing with the interrupts - since you're starting the timers for both streams at the same time they'll both have their interrupts at pretty much the same time, assuming both streams have the same sample frequency. Given the frequency of your interrupts (they occur after every sample played) I'd expect these interrupts to collide, causing you to lose them often.

I appreciate you won't have enough timers to have three timers per stream - just get it working properly with one stream first, then when you're happy merge some of the timer definitions together so you need only one or two timers per stream.

Finally, one thing I had was that I had my timer definitions off by just one clock cycle per sample - this meant that even though I wasn't using any interrupts and the rest of the system was idle, the sound would get out of sync within a few minutes. I realised that I was missing out one clock cycle per sample played, and this was causing my timers to go very slightly faster than the sound hardware timers!
I'm at work at the moment, so I can't look up the specifics that I used - sorry!

EDIT: I think I may have mis-read the timer definitions a bit (I'm so tired...) but even if the interrupts are not as frequent as I think, I'd still not use interrupts. In the end I found the best solution is to get the main thread to spin on the timer which says whether the buffer is empty or not.
_________________
Big thanks to everyone who donated for Quake2

#141522 - ingramb - Wed Sep 26, 2007 5:35 pm

I wish I didn't need to use interrupts, but as you say, I don't have enough timers. It may turn out that I have to play all my streams at the same hardware frequency, and then manually resampe whenever the actual frequency changes. I was hoping to avoid this though (I need all the cycles I can get).

It does work properly with the interrupt and only 2 timers, although it gets out of sync after a few minutes. I'd appreciate if you could look at your actual code, because it sounds like I may be having the same problem that you were having.

Adding the second timer interrupt corrupts the sound streaming off the first timer interrupt; I'm not even reading the second counter on the arm9 yet.

(I have vblank and vcount interrupts enabled on the arm7 as well, but the handlers are empty. The timer interrupts are the only ones that acutally do anything.)

#141527 - DekuTree64 - Wed Sep 26, 2007 6:05 pm

Actually, I prefer to only use 2 timers because it's a little simpler. One timer at the sample frequency, one cascaded off it. Then in your VBlank handler, read the upper timer and compare against what its value was last VBlank to see how many samples you need to mix.

Also, using the TIMER_FREQ macro can cause out-of-sync, because CPU timers run at twice the frequency of sound timers. Try using SOUND_FREQ(freq1) * 2 to guarantee that it matches exactly.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#141584 - ingramb - Thu Sep 27, 2007 6:06 am

Quote:
Try using SOUND_FREQ(freq1) * 2 to guarantee that it matches exactly

Yes, thank you! This was indeed the source of my sync issues.

Quote:
Then in your VBlank handler, read the upper timer and compare against what its value

I'm doing this more or less now and it's working very nicely.

Thanks so much for your help.