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 > Building a mixer for the DS

#45725 - gladius - Tue Jun 14, 2005 5:09 am

I need to do some generated sound on the DS, not just play a sample back. The looping sound channel effect seems like it would be a great way to do that, but the only problem is I need a notification when it reaches the end of the buffer so I can regenerate the samples for the next playback.

Anyone know if there are irq's on sound finish/loop? I took a glance through the ndslib irq list and the only thing I could see in there relating to it would be timer irq's. Anyone know/tried this already before I start fooling around with the registers?

#45726 - josath - Tue Jun 14, 2005 5:35 am

1. The NDS has a built in, 16-channel hardware mixer that automatically lets you mix 16 different sounds at once.
2. No, even with much searching, nobody has been able to find any way to tell either How much data/samples a channel has played, or When it reaches the end of the buffer. I'm still hoping there is some secret audio irq we haven't found yet.

#45735 - headspin - Tue Jun 14, 2005 11:52 am

Use if( (SCHANNEL_CR(n) & SCHANNEL_ENABLE) == 0) to find out whether the sample has finished playing or not.
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#45741 - gladius - Tue Jun 14, 2005 3:32 pm

josath: Ok, thanks, that confirms the bad news :(. I knew it had 16 sample playback, but that is not useful for me. There has to be some sort of irq or notification system set up, otherwise the commercial mixers would be fugly and inaccurate as well.

headspin: That is not useful for a looping sample, which will never turn off. I did consider starting up another short sample when the previous one finished (in effect creating a never ending buffer), but polling the register is just ugly, and has to be done constantly if you want good accuracy (or at least consistently, which is not easy).

#45747 - tepples - Tue Jun 14, 2005 3:51 pm

What happens when you set the playback rate to exactly 1065 ARM7 cycles per sample (two samples per hblank, approx. 31.5 kHz) and set up a loop 526 samples in size? Then you can derive the play and write cursors (Windows DSound terminology) from vcount.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#45862 - gladius - Wed Jun 15, 2005 10:08 pm

I got a mixer working using a fairly ugly hack. I'm sure it can be done better, but in it's current format it uses 2 channels for mono playback, and 4 for stereo, along with timer 0&1. The advantage of this is you can have any size mix buffer you want, and any mixing rate you want.

Rough pseudo-code for arm7:
Code:

void SetupSound() {
    powerON(POWER_SOUND);
    SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F);

    TIMER0_DATA = TIMER_FREQ(32000); // 32000hz is our mixing frequency
    TIMER0_CR = TIMER_DIV_1 | TIMER_ENABLE;

    // MIXBUFSIZE is the size of your mix buffer
    TIMER1_DATA = 0x10000 - MIXBUFSIZE;
    TIMER1_CR = TIMER_CASCADE | TIMER_IRQ_REQ | TIMER_ENABLE;
}

In interrupt handler:

u16 leftSoundBuffer[MIXBUFSIZE * 2];
int soundCursor = 0;

void InterruptHandler(void) {
    if (IF & IRQ_TIMER1) {
        int channel = soundCursor == 0 ? 0 : 1;
        SCHANNEL_TIMER(channel) = SOUND_FREQ(32000);
        SCHANNEL_SOURCE(channel) = (uint32)&leftSoundBuffer[MIXBUFSIZE - soundCursor];
        SCHANNEL_LENGTH(channel) = (MIXBUFSIZE * 2) >> 2;
        SCHANNEL_REPEAT_POINT(channel) = 0;
        SCHANNEL_CR(channel) = SCHANNEL_ENABLE | SOUND_ONE_SHOT | SOUND_VOL(0x7F) | SOUND_PAN(63) | SOUND_16BIT;

        // Mix data now
        for (int i = 0; i < MIXBUFSIZE; i++) leftSoundBuffer[i + soundCursor] = 0;

        // Move sound cursor
        soundCursor = MIXBUFSIZE - soundCursor;
    }
}