Dowload the ROM and the Complete Source Code for the examples.
Direct
Sound
Direct Sound (not to confuse with DirectSound which is a registered trademark of Microsoft) refers to the two 8-bit digitial-to-analog converters part of the Gameboy Advance sound system (GBAS). The samples to be played, which must be 8-bit signed, are loaded in consecutive adresses starting at 0x40000A0 (REG_FIFO_A). These adresses acts as a FIFO (First-In-First-Out), meaning that lower adresses bytes are played first. Playback frequency is controlled by the overflow of either Timer 0 or Timer 1, allowing the two Direct sound channels to play at different frequencies independently. Direct sound can work in two modes: DMA mode and Interrupt mode. DMA mode is the most efficient way of playing Direct sound. Because once empty, sound FIFOs are automatically reloaded with the next samples by the DMA controller, without any program intervention. The other mode uses an interrupt handler that manually load the FIFOs. This is less efficient than DMA mode but in some cases, it is the only solution.
|
Output ratios control the output volume. Set these bits when Sound 1-4 or Direct
Sound plays too loud relative to each other. Direct Sound channels can be send
to Left, Rigth or both outputs. Bit A and E selects which timer to use as the
sampling frequncy reference. Both Direct sound channels can use the same timer,
and it is usually the case for software mixing. FIFO reset prepares the Direct
sound harware for playback and put the playing cursor back to FIFO's sample
0. It should always be performed before playback start.
The following examples demonstrate Direct Sound playback in DMA
mode and Interrupt mode.
DMA
Mode Direct Sound Example
To use DirectSound in DMA mode:
Sound should start immediately and will play past the sample if not stopped. You can use timer1 to count played samples and stop the sound. To do this, set timer 1 to cascade and enable irq for timer 1 and set its count to 0xffff-samples count. Your irq handler should stop the sound by disabling timer 0 and the dma channel(s).
Code
#include "gba.h"
extern const u32 _binary_lo1234_pcm_start[]; //the
sample. its a pcm wave file converted to an elf
file with objcopyroda.exe (devrs.com/gba)
void InterruptProcess(void)
__attribute__ ((section(".iwram"))); //the
interrupt handler from crt0.s
void InterruptProcess(void){
//sample finished!,stop Direct sound
REG_TM0CNT_H=0; //disable timer 0
REG_DMA1CNT_H=0; //stop DMA
//clear the interrupt(s)
REG_IF |= REG_IF;
}
void AgbMain (void){
//play a mono sound at 16khz
//uses timer 0 as sampling rate source
//uses timer 1 to count the samples played in order to stop the sound
REG_SOUNDCNT_H=0x0b0F; //enable DS A&B + fifo
reset + use timer0 + max volume to L and R
REG_SOUNDCNT_X=0x0080; //turn sound chip on
REG_DMA1SAD=(unsigned long)_binary_lo1234_pcm_start;
//dma1 source
REG_DMA1DAD=0x040000a0; //write to FIFO A address
REG_DMA1CNT_H=0xb600; //dma control: DMA enabled+ start
on FIFO+32bit+repeat+increment source&dest
REG_TM1CNT_L=0x7098; //0xffff-the number of samples to
play
REG_TM1CNT_H=0xC4; //enable timer1 + irq and cascade from
timer 0
REG_IE=0x10; //enable irq for timer
1
REG_IME=1; //master enable interrupts
//Formula for playback frequency is: 0xFFFF-round(cpuFreq/playbackFreq)
REG_TM0CNT_L=0xFBE8; //16khz playback freq
REG_TM0CNT_H=0x0080; //enable timer0
}
True stereo output is a simple extension of the above code:
Interrupt
Mode Direct Sound Example
DMA mode Direct Sound has reportedly being causing problems in multi-players games. This is because during DMA tranfers, if interrupts occurs, they are only processed upon completion of that DMA. That means possible transmission losses due to bytes in the serial buffer being overwitten before beign read. On possible solution to this problem would be the use of Interrupt mode Direct sound playback. In this mode you set a timer (again 0 or 1), to the sampling frequency, set it to generate interrupts and load the FIFO(s) in the interrupt handler. Note that this methodology might impose problems if interrupts are blocking (ie. not allowing multiple interrupts at once), however if several interrupts at once are allowed inside the handler, this should resolve the issue.
To use Direct sound in Interrupt mode:
Code
#include "gba.h"
extern const u32 _binary_lo1234_pcm_start[]; //the
sample. its an pcm wave file converted to an elf file with objcopyroda.exe (devrs.com/gba)
void InterruptProcess(void) __attribute__ ((section(".iwram")));//the
interrupt handler from crt0.s
int iNextSample=0;
int SampleSize=36712;
void InterruptProcess(void){
//load FIFO each 4 samples
with 4 bytes
if(!(iNextSample&3))REG_SGFIFOA=_binary_lo1234_pcm_start[iNextSample>>2];
iNextSample++;
void AgbMain(void){
//play a sample at 16Khz using interrupt mode
REG_SOUNDCNT_H=0x0B0F; //DirectSound A +
fifo reset + max volume to L and R
REG_SOUNDCNT_X=0x0080; //turn sound chip on
REG_IE=0x8; //enable timer 0 irq
REG_IME=1; //enable interrupts
/*set playback frequency. note: using anything else thank clock multipliers to serve as sample frequencies tends to generate distortion in the output. It has probably to do with timing and FIFO reloading. More testing need to be done. */
REG_TM0CNT_L=0xffff;
REG_TM0CNT_H=0x00C3; //enable timer at CPU freq/1024 +irq
=16384Khz sample rate
}
The Audio Advance by Alec Bourque is licensed under CC BY 4.0