#1617 - Vortex - Mon Jan 20, 2003 7:44 pm
Hello,
Since I was not able to find any GBA exaples how to do mixing (i.e. more that one sound at a time) I spend some time this weekend and here is the code:
All of the sound-related functions start with sb prefix. The program uses double buffering. I am planning to rewrite some of the functions in ASM once I am happy with the overall implementation. Right now the program output is mono, but in the next version I will double the buffers and implement a stereo output. The next step will be to add some FX functions as delay, reverb and echo. Please let me know if you think there is a way to optimize the code and/or if you find any errors.
Thanks
Since I was not able to find any GBA exaples how to do mixing (i.e. more that one sound at a time) I spend some time this weekend and here is the code:
Code: |
///////////////////////////////////////////////////////////////////////////// // sound.c // ver: 0.4.3 #include "gba.h" #include "screenmode.h" #include "keypad.h" #include "sound21.c" // #include "sound25.c" /////////////////////////////////////////////////////////////////////////////// // Sound Control #define FIFO_A (0x040000A0) #define FIFO_B (0x040000A4) struct SountCntH { int Ch1to4OutputRatio : 2; int DSndAOutRatio : 1; int DSndBOutRatio : 1; int unused : 4; int EnableDSndAToRightSpeaker : 1; int EnableDSndAToLeftSpeaker : 1; int DSndASampleRateTimer : 1; int DSndAFIFOReset : 1; int EnableDSndBToRightSpeaker : 1; int EnableDSndBToLeftSpeaker : 1; int DSndBSampleRateTimer : 1; int DSndBFIFOReset : 1; }; /////////////////////////////////////////////////////////////////////////////// // Sound Buffer Definitions and Prototypes #define PLAYBACK_FREQ (16384) #define SOUND_BUFFER_SIZE (16384) s8 SoundBuffer1[SOUND_BUFFER_SIZE] __attribute__ ((section (".ewram"))) = { 0 }; s8 SoundBuffer2[SOUND_BUFFER_SIZE] __attribute__ ((section (".ewram"))) = { 0 }; s8* SoundBuffer __attribute__ ((section (".ewram"))) = SoundBuffer1; u8 activeSoundBuffer __attribute__ ((section (".ewram"))) = 0; void sbClear(void); void sbFlip(void); void sbInsertSample( const s8* sound_data, u16 sound_data_size, u16 offset ); void InterruptProcess(void) __attribute__ ((section(".iwram"))); //the interrupt handler from crt0.s /////////////////////////////////////////////////////////////////////////////// // Interrupt Handler void InterruptProcess(void) { // Init DMA1 Source REG_DMA1SAD = (unsigned long) SoundBuffer; // DMA1 source //clear the interrupt(s) REG_IF |= REG_IF; } /////////////////////////////////////////////////////////////////////////////// // Sound Buffer Functions void sbFlip(void) { if( activeSoundBuffer == 0 ) { SoundBuffer = SoundBuffer2; activeSoundBuffer = 1; } else { SoundBuffer = SoundBuffer1; activeSoundBuffer = 0; } } void sbClear(void) { u32* buffer = (u32*) SoundBuffer; u32 i; for( i = 0; i < SOUND_BUFFER_SIZE / 4; i++ ) buffer[i] = 0; } void sbClearAll(void) { u32* buffer1; u32* buffer2; u32 i; buffer1 = (u32*) SoundBuffer1; buffer2 = (u32*) SoundBuffer2; for( i = 0; i < SOUND_BUFFER_SIZE / 4; i++ ) { buffer1[i] = 0; buffer2[i] = 0; } } // sound_data_size must be divisible by 4 !!! void sbInsertSampleDMA( const s8* sound_data, u16 sound_data_size ) { // The sample is too big if( sound_data_size > SOUND_BUFFER_SIZE ) return; REG_DMA3SAD = (u32) sound_data; REG_DMA3DAD = (u32) SoundBuffer; REG_DMA3CNT_L = sound_data_size / 4; REG_DMA3CNT_H = 0x8400; // DMA Enable | 32-bit transfer } void sbInsertSample( const s8* sound_data, u16 sound_data_size, u16 offset ) { u32 i; u16 pos = offset; // The sample is too large if( sound_data_size > SOUND_BUFFER_SIZE ) return; for( i = 0; i < sound_data_size; i++ ) { SoundBuffer[pos++] = sound_data[i]; if( pos == SOUND_BUFFER_SIZE ) pos = 0; } } u32 sbGetCurrentPosition(void) { return 0; } ////////////////////////////////////////////////////////////////// // Resources Used: Timer0, Timer1, DMA1 void InitSoundSystem() { const u32 cpuFreq = 16777216; struct SountCntH snd_ctrl; activeSoundBuffer = 0; SoundBuffer = SoundBuffer1; sbClearAll(); snd_ctrl.Ch1to4OutputRatio = 0; snd_ctrl.DSndAOutRatio = 1; // 100% snd_ctrl.DSndBOutRatio = 0; snd_ctrl.unused = 0; snd_ctrl.EnableDSndAToRightSpeaker = 1; snd_ctrl.EnableDSndAToLeftSpeaker = 1; snd_ctrl.DSndASampleRateTimer = 0; //Timer 0 snd_ctrl.DSndAFIFOReset = 1; snd_ctrl.EnableDSndBToRightSpeaker = 0; snd_ctrl.EnableDSndBToLeftSpeaker = 0; snd_ctrl.DSndBSampleRateTimer = 0; // Timer 0 snd_ctrl.DSndBFIFOReset = 0; /////////////////////////////////////////////////////////////////////////////// // Play a mono sound at PLAYBACK_FREQ // 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_H = *(u16*) &snd_ctrl; REG_SOUNDCNT_X = 0x0080; // Turn sound chip on // Clear the FIFO // u32* fifo_a = (u32*) FIFO_A; // *fifo_a = 0; // Direct Sound Channel A REG_DMA1SAD = (unsigned long) SoundBuffer; // DMA1 source REG_DMA1DAD = FIFO_A; // Write to FIFO A address REG_DMA1CNT_H = 0xb600; // DMA control: DMA enabled+ start on FIFO+32bit+repeat+increment source&dest REG_DMA1CNT_L = 0; REG_TM1CNT_L = 0xFFFF - SOUND_BUFFER_SIZE; // 0xffff-the number of samples to play REG_TM1CNT_H = 0xC4; // Enable Timer1 + IRQ and cascade from Timer0 REG_IE = 0x10; // Enable IRQ for timer 1 REG_IME = 1; // Master enable interrupts // Formula for playback frequency is: 0xFFFF-round(cpuFreq/PLAYBACK_FREQ) // REG_TM0CNT_L = 0xFBE8; //16khz playback freq // REG_TM0CNT_L = 0xFD07; // 22.05 khz playback freq REG_TM0CNT_L = 0xFFFF - (cpuFreq/PLAYBACK_FREQ); REG_TM0CNT_H = 0x0080; // Enable Timer0 } void AgbMain (void) { InitSoundSystem(); SetMode( MODE_3 | BG2_ENABLE ); while(1) { if(!(*KEYS & KEY_A)) { while(!(*KEYS & KEY_A)); sbInsertSample((const s8*) SOUND21_DATA, SOUND21_LENGTH, SOUND_BUFFER_SIZE - 3000 ); // sbInsertSampleDMA((const s8*) SOUND21_DATA, SOUND21_LENGTH); } else if(!(*KEYS & KEY_B)) { while(!(*KEYS & KEY_B)); sbClear(); } } } |
All of the sound-related functions start with sb prefix. The program uses double buffering. I am planning to rewrite some of the functions in ASM once I am happy with the overall implementation. Right now the program output is mono, but in the next version I will double the buffers and implement a stereo output. The next step will be to add some FX functions as delay, reverb and echo. Please let me know if you think there is a way to optimize the code and/or if you find any errors.
Thanks