#22584 - Saint - Thu Jun 24, 2004 5:31 pm
Not really sure whether I'm in the right place here, but anyways.
I wrote a Direct Sound player with interrupts, extending the version described at audioadvance. Problem is, that program generates interrupts 22050 times per second, but only copies a dword of data every forth frame.
Sooo, I figured I could save some processing power by setting the timer to take 4 times as long, and then just update the FIFO with every interrupt, but this strangely enough produced a very low-pitched version of the original sound, though with same length as the original audio.
I'm really no audio wizard, and I have very little previous experience with writing low-level sound code, so is there anyone here who knows what is happening? or better yet, can provide a sollution?
#22587 - poslundc - Thu Jun 24, 2004 6:25 pm
Make sure you are using two separate timers: one for your interrupt and one for the direct sound chip. If you are only using a single timer to clock both the sound chip and the interrupt, you will have to switch to two timers if you want to call your interrupt at a reduced frequency.
When you start mixing, consider using DMA with double-buffers instead, and setting your interrupt to a much lower frequency so it just has to keep the buffers filled. In practice a mixer running off a double-buffer will be more efficient than a purely interrupt-driven mixer on the GBA, and will be more fault-tolerant of delays if you are using multiple interrupts (such as if you have a critical HBlank ISR or serial communications going on).
Dan.
#22588 - Saint - Thu Jun 24, 2004 6:59 pm
Well, that would certainly never have occured to me.
Thanks a lot!
As for the DMA and double- buffering, I can see how this would be faster, but how would you go about when the buffer needs re-filling? would you calculate the sound in real-time, or stay "one buffer ahead", calculating a little each frame?
Also, what about sound effects? are you going to (in the interrupt handler) keep track of where in the double buffer you're currently at, so that you can play an effect instantly by just copying it into that position, or would it be wiser to use a separate handler for those? or is it perhaps better to use a DMA small enough so that all sound effects are simply added the next time the buffer is re-filled, and the buffer is small enough to keep the time difference neglible?
Anything else I should think of?
#22589 - poslundc - Thu Jun 24, 2004 7:14 pm
Saint wrote: |
As for the DMA and double- buffering, I can see how this would be faster, but how would you go about when the buffer needs re-filling? would you calculate the sound in real-time, or stay "one buffer ahead", calculating a little each frame? |
The idea behind double-buffering is that while one buffer is being read from, the other buffer is being filled. This prevents erroneous data if your filler gets interrupted, because the data currently being used is coming from a different source.
You time it so that your interrupt triggers right when the DMA is at the end of one buffer. It can then immediately switch the DMA over to the other buffer, and then mix into the buffer that the DMA just left.
Quote: |
Also, what about sound effects? are you going to (in the interrupt handler) keep track of where in the double buffer you're currently at, so that you can play an effect instantly by just copying it into that position, or would it be wiser to use a separate handler for those? or is it perhaps better to use a DMA small enough so that all sound effects are simply added the next time the buffer is re-filled, and the buffer is small enough to keep the time difference neglible? |
You've got the right idea. Just turn the sound effect "on", and the next time your mixer runs, you start playing it.
IIRC, I think my MOD player uses a buffer of 512 bytes, double-buffered to 1K, and then stereo-channeled for a total of 2K. The sound chip is running at 16KHz, so the mixer runs (refilling one of the buffers) at a frequency of 32 Hz, or every 0.031 seconds. I don't think there are many gamers out there who will notice if your sound effect starts 0.031 seconds late, do you?
Dan.
#22590 - Saint - Thu Jun 24, 2004 7:23 pm
poslundc wrote: |
I don't think there are many gamers out there who will notice if your sound effect starts 0.031 seconds late, do you? |
...Point taken. I'm assuming (since I as I said don't know anything about hardcore sound programming) that when the interrupt tells the DMA to swap buffers, it also calls a function to refill the backbuffer, and that you have some kind of handler for this.
You've been a great help, I really mean it. Thanks.
Btw, assuming I wanted to make a player for some format of my own, do you know of any resource sites except for www.wotsit.org? Or just for audio programming in general? Everybody has tutorials for graphics programming on every system under the sun, but only a select few seems to do their own sound systems.
#22592 - poslundc - Thu Jun 24, 2004 7:46 pm
Saint wrote: |
I'm assuming (since I as I said don't know anything about hardcore sound programming) that when the interrupt tells the DMA to swap buffers, it also calls a function to refill the backbuffer, and that you have some kind of handler for this. |
If you're doing it as an interrupt it's best to do it all in a single function, since you want it to be running lean and mean. In fact, if you were ever thinking about learning assembly, an interrupt-driven mixer is a good candidate for assembly optimization.
The very first thing the interrupt should do is switch the DMA over to the other buffer, so no matter how long the mixing takes, the DMA is ready to go as soon as it is needed. (Note that if you implement your double-buffer as a single, continous block of memory you'll only have to stop and restart the DMA every other buffer-swap, when you reach the end of the second buffer.)
The alternative to interrupt-based mixing is to mix on VBlank, which many other people on the forum choose to do. I prefer to mix on interrupts because I get rounder numbers for my frequencies and buffer sizes, and I have to agonize less over the (admittedly simple enough) math, but there are a few advantages to mixing to VBlank, namely:
- Don't have to worry about what happens when you conflict with an HBlank ISR
- Frees up a timer, for what it's worth
- Don't have to wait for an ISR to launch (something like 50-70 cycles in the BIOS, apparently).
Neither of the first two things are very important to me (I've configured my HBlank ISR to preempt my mixer, and four timers is plenty for me). As for the third, I shrug my shoulders at it, frankly, as my player is still very lean and mean. Plus I'm leaving more VBlank time to do VBlank-related tasks, which is important to me.
Quote: |
Btw, assuming I wanted to make a player for some format of my own, do you know of any resource sites except for www.wotsit.org? Or just for audio programming in general? Everybody has tutorials for graphics programming on every system under the sun, but only a select few seems to do their own sound systems. |
Wotsit is where I got all of my specs, I think. Other than that, you can google, search the forums, or post your questions here and we'll try our best to field them. Maybe someone else will be able to suggest additional sites.
Dan.
#22593 - tepples - Thu Jun 24, 2004 8:08 pm
poslundc wrote: |
I don't think there are many gamers out there who will notice if your sound effect starts 0.031 seconds late, do you? |
It appears you've never played Beatmania, Pop'n Music, or any other Konami rhythm game. I absolutely cannot play the GBC versions of those in an emulator because of all the added audio latency.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#22594 - poslundc - Thu Jun 24, 2004 8:20 pm
Way to rain on my parade, tepples. ;)
Of course, the emulator you were using probably has a much larger sound buffer. I don't think 0.03 seconds is so bad, but you can always make your buffer smaller if it is becoming a problem. I am using a 512 byte buffer, but I started with a 256 byte buffer, so running at 16 KHz that would mean a maximum latency of 0.0155 seconds. Keep in mind that it's the maximum latency as well: you can probably assume a uniform distrubtion such that it is only 0.005 or so on average.
Dan (what more can they want?!).
#22598 - Saint - Thu Jun 24, 2004 9:29 pm
I actually do know some assembly, wrote a few games back in the 90s. I planned to do stuff like that in ASM, but I'm obviously a little rusty, and I haven't gotten to reading up on ARM assembly... yet.
As for the rest, I don't know much, but I'd agree it's better to use another timer. For one thing, you keep your graphics and sound system a little more separated, which is cleaner, if nothing else.
Thanks again. Oh, and tepples, thanks for pointing it out, but I don't think I'll be doing a GBA DDR... Should I happen to change my mind, I'll ask you about it =)
#22600 - DekuTree64 - Fri Jun 25, 2004 12:06 am
Actually, you can get near-perfect timing for music when using double buffers. If you make your mixer where you can send the number of samples for it to mix (usually restricted to multiples of at least 4, depending on how your mixer works), then you can calculate up the number of samples per song tick, and mix that many. Then update the song, mix that many more samples, update the song, until the samples/tick is more than what's left of your buffer, and then just mix however many is left, and keep track of how many more it will be until the next song tick for next frame.
As for sound effects, since most games run at 60 or 30Hz, they don't NEED to be more accurate than one frame's buffer length, because you would expect all the sounds played on a single frame to start at the same time. Makes life easy for us coders^_^
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#22601 - poslundc - Fri Jun 25, 2004 12:44 am
DekuTree64 wrote: |
As for sound effects, since most games run at 60 or 30Hz, they don't NEED to be more accurate than one frame's buffer length, because you would expect all the sounds played on a single frame to start at the same time. |
There's the logic I was lookin' for earlier, folks.
Dan.