static void switch_buffers() { if (playing && buffered > 0) { AUDIO_InitDMA((u32)buffers[buffer_play], BUFFER_SIZE); buffer_play = (buffer_play + 1) % BUFFER_COUNT; buffered -= BUFFER_SIZE; } else { AUDIO_InitDMA((u32)silence, BUFFER_SIZE); } }
static void *gx_audio_init(const char *device, unsigned rate, unsigned latency) { gx_audio_t *wa = (gx_audio_t*)memalign(32, sizeof(*wa)); if (!wa) return NULL; gx_audio_data = wa; memset(wa, 0, sizeof(*wa)); AUDIO_Init(NULL); AUDIO_RegisterDMACallback(dma_callback); if (rate < 33000) { AUDIO_SetDSPSampleRate(AI_SAMPLERATE_32KHZ); g_settings.audio.out_rate = 32000; } else { AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); g_settings.audio.out_rate = 48000; } LWP_InitQueue(&wa->cond); wa->dma_write = BLOCKS - 1; DCFlushRange(wa->data, sizeof(wa->data)); AUDIO_InitDMA((uint32_t)wa->data[wa->dma_next], CHUNK_SIZE); AUDIO_StartDMA(); return wa; }
int sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr ) { switch(*freqptr) { case 32000: samplerate = AI_SAMPLERATE_32KHZ; break; case 48000: samplerate = AI_SAMPLERATE_48KHZ; break; default: printf("Sample rate %d not supported on Wii\n", *freqptr); return 1; } sfifo_init( &sound_fifo, BUFSIZE ); *stereoptr = 1; AUDIO_Init( NULL ); AUDIO_SetDSPSampleRate( samplerate ); #ifndef DISPLAY_AUDIO AUDIO_RegisterDMACallback( sound_dmacallback ); memset( dmabuf, 0, BUFSIZE ); AUDIO_InitDMA( (u32)dmabuf, BUFSIZE ); DCFlushRange( dmabuf, dmalen ); AUDIO_StartDMA(); #endif return 0; }
/*** gx_audio_Update This function retrieves samples for the frame then set the next DMA parameters Parameters will be taken in account only when current DMA operation is over To keep audio & video synchronized, DMA from external memory to audio interface is started once by video update function when first frame is ready to be displayed, then anytime video mode is changed and emulation resynchronized to video hardware. Once started, audio DMA restarts automatically when all samples have been played. At that time: - if DMA settings have not been updated, previous sound buffer will be played again - if DMA settings are updated too fast, one sound buffer frame might be skipped Therefore, in order to maintain perfect audio playback without any sound skipping or lagging, we need to make sure frame emulation is completed and this function is called before previous DMA transfer is finished and after it has been started. This is done by synchronizing frame emulation with audio DMA interrupt (which happens anytime audio DMA restarts). When video sync is enabled, to keep emulation in sync with both video AND audio, an appropriate number of samples is rendered per frame by adjusting emulator output samplerate. ***/ int gx_audio_Update(int status) { /* Current available soundbuffer */ s16 *sb = (s16 *)(soundbuffer[bufferIndex]); /* Make sure current audio frame has not already been updated */ if (status & AUDIO_UPDATE) { /* Retrieve audio samples (size must be multiple of 32 bytes) */ bufferSize = audio_update(sb) * 4; DCFlushRange((void *)sb, bufferSize); /* Mark current audio frame as being updated */ status &= ~AUDIO_UPDATE; } /* Wait until previous audio frame is started before pushing current audio frame into DMA */ if ((status & AUDIO_WAIT) && !audioWait) { /* Update audio DMA settings for current frame */ AUDIO_InitDMA((u32)sb, bufferSize); /* Next soundbuffer */ bufferIndex = (bufferIndex + 1) % SOUND_BUFFER_NUM; /* Set audio wait flag */ audioWait = audioSync; /* Current audio frame is ready for upcoming DMA */ status &= ~AUDIO_WAIT; } return status; }
// Called whenever more audio data is required. static void play_more_audio() { // Copy from mix buffer to DMA buffer. sample* const src_begin = &mix_buffer[mix_buffer_pointer]; sample* const dst_begin = &dma_buffers[current_dma_buffer][0]; const sample* const dst_end = dst_begin + samples_per_dma_buffer; sample* src = src_begin; sample* dst = dst_begin; while (dst != dst_end) { // We have to swap the channels, because Quake stores the left // channel first, whereas the GameCube expects right first. const u32 mix_sample = *src; *src++ = 0; *dst++ = (mix_sample >> 16) | ((mix_sample & 0x0000ffff) << 16); } // Set up the DMA. const u32 dma_src_address = (u32)(dst_begin); const size_t bytes = samples_per_dma_buffer * sizeof(sample); AUDIO_InitDMA(dma_src_address, bytes); // Flush the data cache. DCFlushRange(dst_begin, bytes); // Start the DMA. AUDIO_StartDMA(); // Move the mix buffer pointer. mix_buffer_pointer = (mix_buffer_pointer + samples_per_dma_buffer) % samples_per_mix_buffer; // Use the other DMA buffer next time. current_dma_buffer = 1 - current_dma_buffer; }
/*** gx_audio_Update This function retrieves samples for the frame then set the next DMA parameters Parameters will be taken in account only when current DMA operation is over ***/ void gx_audio_Update(void) { /* retrieve audio samples */ int size = audio_update() * 4; /* set next DMA soundbuffer */ s16 *sb = (s16 *)(soundbuffer[mixbuffer]); DCFlushRange((void *)sb, size); AUDIO_InitDMA((u32) sb, size); mixbuffer ^= 1; /* Start Audio DMA */ /* this is called once to kick-off DMA from external memory to audio interface */ /* DMA operation is automatically restarted when all samples have been sent. */ /* If DMA settings are not updated at that time, previous sound buffer will be used. */ /* Therefore we need to make sure frame emulation is completed before current DMA is */ /* completed, either by synchronizing frame emulation with DMA start or by syncing it */ /* with Vertical Interrupt and outputing a suitable number of samples per frame. */ /* */ /* In both cases, audio DMA need to be synchronized with VSYNC and therefore need to */ /* be resynchronized (restarted) every time video settings are changed (hopefully, */ /* this generally happens while no music is played. */ if (!audioStarted) { /* restart audio DMA */ AUDIO_StopDMA(); AUDIO_StartDMA(); audioStarted = 1; /* resynchronize emulation */ frameticker = 1; } }
static void dma_callback(void) { g_audio->dma_busy = g_audio->dma_next; g_audio->dma_next = (g_audio->dma_next + 1) & (BLOCKS - 1); DCFlushRange(g_audio->data[g_audio->dma_next], CHUNK_SIZE); AUDIO_InitDMA((u32)g_audio->data[g_audio->dma_next], CHUNK_SIZE); LWP_ThreadSignal(g_audio->cond); }
static void sound_dmacallback( void ) { if( sfifo_used( &sound_fifo ) < 128) return; dmalen = MIN( BUFSIZE, sfifo_used( &sound_fifo ) ); sfifo_read( &sound_fifo, dmabuf, dmalen ); DCFlushRange( dmabuf, dmalen ); AUDIO_InitDMA( (u32)dmabuf, dmalen ); AUDIO_StartDMA(); }
static void StartDMA(void) { AUDIO_StopDMA(); soundpos++; if (soundpos >= NUMSOUNDBLOCKS) soundpos = 0; AUDIO_InitDMA((u32)stereodata16[soundpos], soundtruelen * 4); DCFlushRange((void *)stereodata16[soundpos], soundtruelen * 4); AUDIO_StartDMA(); }
static void switch_buffers(void) { AUDIO_StopDMA(); cur_buffer ^= 1; AUDIO_InitDMA((u32)buffer[cur_buffer], buffer_size[cur_buffer]); AUDIO_StartDMA(); LWP_ThreadSignal(audio_queue); }
static void dma_callback(void) { gx_audio_t *wa = (gx_audio_t*)gx_audio_data; // erase last chunk to avoid repeating audio memset(wa->data[wa->dma_busy], 0, CHUNK_SIZE); wa->dma_busy = wa->dma_next; wa->dma_next = (wa->dma_next + 1) & (BLOCKS - 1); DCFlushRange(wa->data[wa->dma_next], CHUNK_SIZE); AUDIO_InitDMA((uint32_t)wa->data[wa->dma_next], CHUNK_SIZE); LWP_ThreadSignal(wa->cond); }
int SNDWiiInit() { AUDIO_Init(NULL); AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); soundpos = 0; soundlen = 44100 / 60; // 60 for NTSC or 50 for PAL. Initially assume it's going to be NTSC. soundtruelen = 48000 / 60; truesoundoffset = stereodata16[0]; memset(stereodata16, 0, SOUNDBUFSIZE); issoundmuted = 0; AUDIO_RegisterDMACallback(StartDMA); AUDIO_InitDMA((u32)stereodata16[soundpos], soundlen * 4); DCFlushRange((void *)stereodata16[soundpos], soundlen * 4); AUDIO_StartDMA(); return 0; }
void OSystem_Wii::initSfx() { _mixer = new Audio::MixerImpl(this, 48000); sfx_thread_running = false; sfx_thread_quit = false; sfx_stack = (u8 *) memalign(32, SFX_THREAD_STACKSIZE); if (sfx_stack) { memset(sfx_stack, 0, SFX_THREAD_STACKSIZE); LWP_InitQueue(&sfx_queue); s32 res = LWP_CreateThread(&sfx_thread, sfx_thread_func, _mixer, sfx_stack, SFX_THREAD_STACKSIZE, SFX_THREAD_PRIO); if (res) { printf("ERROR creating sfx thread: %d\n", res); LWP_CloseQueue(sfx_queue); return; } sfx_thread_running = true; } for (u32 i = 0; i < SFX_BUFFERS; ++i) { sound_buffer[i] = (u8 *) memalign(32, SFX_THREAD_FRAG_SIZE); memset(sound_buffer[i], 0, SFX_THREAD_FRAG_SIZE); DCFlushRange(sound_buffer[i], SFX_THREAD_FRAG_SIZE); } _mixer->setReady(true); sb_hw = 0; AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); AUDIO_RegisterDMACallback(audio_switch_buffers); AUDIO_InitDMA((u32) sound_buffer[sb_hw], SFX_THREAD_FRAG_SIZE); AUDIO_StartDMA(); }
static void *gx_audio_init(const char *device, unsigned rate, unsigned latency) { if (g_audio) return g_audio; AUDIO_Init(NULL); AUDIO_RegisterDMACallback(dma_callback); if (rate < 33000) { AUDIO_SetDSPSampleRate(AI_SAMPLERATE_32KHZ); g_settings.audio.out_rate = 32000; } else { AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); g_settings.audio.out_rate = 48000; } if (!g_audio) { g_audio = memalign(32, sizeof(*g_audio)); memset(g_audio, 0, sizeof(*g_audio)); LWP_InitQueue(&g_audio->cond); } else { memset(g_audio->data, 0, sizeof(g_audio->data)); g_audio->dma_busy = g_audio->dma_next = 0; g_audio->write_ptr = 0; g_audio->nonblock = false; } g_audio->dma_write = BLOCKS - 1; DCFlushRange(g_audio->data, sizeof(g_audio->data)); AUDIO_InitDMA((u32)g_audio->data[g_audio->dma_next], CHUNK_SIZE); AUDIO_StartDMA(); return g_audio; }
static void inline play_buffer(void){ #ifndef THREADED_AUDIO // We should wait for the other buffer to finish its DMA transfer first while( AUDIO_GetDMABytesLeft() ); AUDIO_StopDMA(); #else // THREADED_AUDIO // Wait for a sample to actually be played to work around a deadlock LWP_SemWait(first_audio); // This thread will keep giving buffers to the audio as they come while(thread_running){ // Wait for a buffer to be processed LWP_SemWait(buffer_full); #endif // Make sure the buffer is in RAM, not the cache DCFlushRange(buffer[thread_buffer], buffer_size); // Actually send the buffer out to be played next AUDIO_InitDMA((unsigned int)&buffer[thread_buffer], buffer_size); #ifdef THREADED_AUDIO // Wait for the audio interface to be free before playing LWP_SemWait(audio_free); #endif // Start playing the buffer AUDIO_StartDMA(); #ifdef THREADED_AUDIO // Move the index to the next buffer NEXT(thread_buffer); } #endif }
static void audio_switch_buffers() { sb_hw = (sb_hw + 1) % SFX_BUFFERS; AUDIO_InitDMA((u32) sound_buffer[sb_hw], SFX_THREAD_FRAG_SIZE); LWP_ThreadSignal(sfx_queue); }