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 > Sound Streaming

#141250 - goruka - Sun Sep 23, 2007 3:45 pm

Hi! What is the best way to do sound streaming on the DS ?
I just want a buffer to mix samples to, constantly, and some sort of interrupt that will ask me to feed more data.

Thanks!

#141263 - ThomasS - Sun Sep 23, 2007 5:15 pm

Using a ring buffer should be the best way (at least it's the way my mp3 decoding examples work).
I can't recall from which thread I got the demo I based my mp3 code on, but you can easily throw away the mp3 part of my demos and put in your mix code.
You can choose if you want to mix to the buffer on the arm9 or on the arm7.
_________________
<dsFont> <Game Collection>

#141265 - simonjhall - Sun Sep 23, 2007 5:32 pm

The lack of any interrupt when the ring buffer is (near-)empty makes writing this sort of thing a real pain.

After trying many schemes, the most effective one I found was to take the sound timer settings you use to start a playing the buffer on the ARM7 and make another timer (say TIMER0) with the exact same setup. If you then make another timer (at TIMER1) and get it to pick up the result from TIMER0 when it overflows (there's a cascade flag for this sort of thing) you'll be able to count the number of samples that have elapsed since the sound buffer started playing. To figure out when the buffer has finished playing, set TIMER1 to 65536 - number of samples in the buffer. Then make TIMER2 pick up the results from TIMER1 on overflow and every time the value in TIMER2 changes you'll know your buffer has drained.

To clarify a bit, set your timers like this:
TIMER0 - set it up with the exact same timer settings as the sound hardware is using, eg SOUND_FREQ(sampleRate)
TIMER1 - set it to 65536 - number of samples in buffer and turn cascading on
TIMER2 - set it to zero, and turn cascading on.

So, without using interrupts this'll allow you to find out when your buffer is empty and there shouldn't be any drift since both the timers and sound hardware are derived from the same timer settings and bus clock. If you do use interrupts to figure out when stuff happens, if you miss interrupts (eg you were inside another interrupt handler) then you'll get drift and the dreaded clicking!

Btw I think there's some mistakes in the timer setup I've got there, since I just wrote this from memory! At one point I had missed out one clock cycle per sample (I think it may be 65535 - number, rather than 65536 - number) and the music got out of sync after a few minutes.

Btw 2: do all this on the ARM7, as it'll be much easier.

Again, world of pain :-)
_________________
Big thanks to everyone who donated for Quake2

#141301 - goruka - Sun Sep 23, 2007 10:22 pm

That kind of sucks.. from what i see in libnds , it's pretty difficult, and even more if i want to use stereo (have to use 2 sound channels ). Strangely, there seems to be no FIFO like register i can dma-to like in the GBA.

Moonshell uses a lot of custom code for handling audio, and the examples linked above use PAlib (i'm using libnds). Any idea where i can find simple code examples for sound streaming? I'm basically almost done porting a tracker to the DS.. all the UI works like a charm, but can't find how to stream the audio in any decent way.. plus, i need to mix the audio in the ARM9 , as mixing has to be done in software.

Thanks!

#141320 - DekuTree64 - Mon Sep 24, 2007 2:25 am

goruka wrote:
That kind of sucks.. from what i see in libnds , it's pretty difficult, and even more if i want to use stereo (have to use 2 sound channels ).

It's not really any more difficult to do stereo. Just start 2 channels at the same time and you can use the same timer to track their positions. The sound hardware itself is pretty easy to use too, just check the register descriptions in gbatek.

Quote:
Strangely, there seems to be no FIFO like register i can dma-to like in the GBA.

On DS, each sound channel has its own DMA, so they don't even have a destination address to set.

Quote:
Any idea where i can find simple code examples for sound streaming?

I don't have any simple example code around, but here's some pseudo code of what I do in my half done tracker (also running a software mixer on ARM9):

ARM9:
- Set REG_IE to disable all interrupts except IPC fifo empty/not empty.
- Send a FIFO message to ARM7 telling it to start the ring buffer.
- Wait for ARM7 to set a variable in shared memory.
- Start cascaded timers, similar to Simon's explanation.
- Restore REG_IE.

ARM7:
- When buffer start message is recieved, start sound channels and set the shared variable.

For FIFO messaging, check out the system I wrote here. I'm using the unbuffered version, but either should work just as well for this.

Quote:
I'm basically almost done porting a tracker to the DS.. all the UI works like a charm, but can't find how to stream the audio in any decent way.. plus, i need to mix the audio in the ARM9 , as mixing has to be done in software.

Cool, what tracker is it? I could use a bit of inspiration to get back working on mine. I'm in that terrible place where all the fun stuff is done, and all there is to do is code up a bunch of menus, and fix bugs in the music player.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#141336 - goruka - Mon Sep 24, 2007 6:42 am

DekuTree64 wrote:

Cool, what tracker is it? I could use a bit of inspiration to get back working on mine. I'm in that terrible place where all the fun stuff is done, and all there is to do is code up a bunch of menus, and fix bugs in the music player.


I'm porting my own, ChibiTracker to Nintendo DS (http://www.chibitracker.com) It's pretty much done and the UI works very nicely on the DS. I'll be using the C software mixing routines in the ARM9, and will most likely write assembler optimized ones at some point, though for now i think they are enough.

The tracker runs pretty good on a Pentium 60, on a song using many channels, so I hope the ARM9 at 66mhz is a better CPU than that and it will handle most songs fine.

here's a peek at how it's coming along:

http://reduz.dyndns.org/chibitracker_ds.nds (dldi it)

As you can see pretty much everything works except sound, and you can fully load, edit, save .IT, .XM, .S3M, .MOD files..

I thoguht sound was going to be easier to get to work, but not having much luck, so help in that area is highly appreciated

Cheers!

#141354 - ThomasS - Mon Sep 24, 2007 1:29 pm

Quote:
the examples linked above use PAlib

No, they only use libnds. To change them to your needs, you only had to paste your own mixing routine in the function
Code:
// Must fill the ring buffer ((s16*)stream) with numsamples samples.
int SoundMixCallback(void *stream, u32 numsamples)
{
   // ... code to decode mp3s ...
}

_________________
<dsFont> <Game Collection>

#141393 - Lazy1 - Mon Sep 24, 2007 11:02 pm

How would streaming from disk work in all of this?

#141401 - goruka - Tue Sep 25, 2007 1:15 am

Well, not having much luck.. if i use the sound library from that mp3 player, touchscreen stops responding.. keys still work though... no idea why it may be :(

#141402 - Lazy1 - Tue Sep 25, 2007 1:24 am

The Arm7 code that comes with that MP3 example looks outdated, try getting the default Arm7 template and manually adding the needed changes.

#141420 - ingramb - Tue Sep 25, 2007 4:59 am

So this is set up on the arm7:
Quote:
To clarify a bit, set your timers like this:
TIMER0 - set it up with the exact same timer settings as the sound hardware is using, eg SOUND_FREQ(sampleRate)
TIMER1 - set it to 65536 - number of samples in buffer and turn cascading on
TIMER2 - set it to zero, and turn cascading on.

I'm streaming data in on the arm9 via DLDI. What's the best way to send the timer signal from the arm7 to the arm9?

I can use the IPC interrupt, but then I defeat the whole purpose of cascading the timers like this (I might miss the IPC interrupt). I can use a shared eram variable, but it seems like it would be a little messy to synchronize like this.

(I'm using shared eram now, and it works, but I get some noise. Which may be related to other problems, but I'm still curious for feedback.)

#141421 - DragonMinded - Tue Sep 25, 2007 5:45 am

Use a FIFO buffer with interrupts.
_________________
Enter the mind of the dragon.

http://dragonminded.blogspot.com

Seriously guys, how hard is it to simply TRY something yourself?

#141422 - ingramb - Tue Sep 25, 2007 6:30 am

What happens if I miss the FIFO interrupt then? I thought the whole point of the multi timer setup was to avoid relying on an interrupt.

#141441 - goruka - Tue Sep 25, 2007 4:29 pm

Well, I tried to use the template in libnds as reference, then now touchscreen and everything works fine, except no sound... i may be doing something wrong? here's how my arm7 looks like ( the app i'm porting is opensource ):

http://svn.berlios.de/viewcvs/chibitracker/trunk/program/chibitracker_ds_arm7.c?rev=184&view=markup

#141443 - ThomasS - Tue Sep 25, 2007 5:13 pm

Did you test if SoundDriverNDS::mix is correctly called? If the touchscreen code from the example is outdated, the way it handles the FIFO messages and allocates memory after the IPC struct might be also outdated and may collide with something in your code ...
_________________
<dsFont> <Game Collection>

#141445 - ingramb - Tue Sep 25, 2007 6:11 pm

Simon, can you take a quick look at your code and see if there's anything you're missing in that example?

I have your system in place, and it's working fantastic, except the sound gets out of sync after a few minutes (just like you said it might).

I tried both 65536 and 65535, and it's definately 65536 (which makes sense to me anyway).

Thanks =)