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; }
// 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; }
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 ***/ 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 audio_resume(void) { playing = true; if(AUDIO_GetDMAEnableFlag()==0) { switch_buffers(); AUDIO_StartDMA(); } }
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); }
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 int play(void *data, int remaining, int flags) { int processed = 0; int samples = BUFFER_SIZE / (sizeof(s16) * HW_CHANNELS); s16 *source = (s16 *)data; while (remaining >= request_size && get_space() >= request_size) { copy_channels((s16 *)buffers[buffer_fill], source, samples, processed, remaining); DCStoreRangeNoSync(buffers[buffer_fill], BUFFER_SIZE); buffer_fill = (buffer_fill + 1) % BUFFER_COUNT; processed += request_size; source += request_size / sizeof(s16); buffered += BUFFER_SIZE; remaining -= request_size; } if ((flags & AOPLAY_FINAL_CHUNK) && remaining > 0) { samples = remaining / (sizeof(s16) * HW_CHANNELS); memset(buffers[buffer_fill], 0, BUFFER_SIZE); copy_channels((s16 *)buffers[buffer_fill], source, samples, processed, 0); DCStoreRangeNoSync(buffers[buffer_fill], BUFFER_SIZE); buffer_fill = (buffer_fill + 1) % BUFFER_COUNT; processed += remaining; buffered += BUFFER_SIZE; } if (!playing)// && buffered > request_size) { playing = true; switch_buffers(); AUDIO_StartDMA(); } return processed; }
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 int wii_audio_start(audio_mode_t *am, audio_fifo_t *af) { audio_buf_t *ab; int tbuf = 0, i; uint32_t level; int64_t pts; media_pipe_t *mp; prop_sub_t *s_vol; s_vol = prop_subscribe(PROP_SUB_DIRECT_UPDATE, PROP_TAG_CALLBACK_FLOAT, set_mastervol, NULL, PROP_TAG_ROOT, prop_mastervol, NULL); LWP_InitQueue(&audio_queue); AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ); AUDIO_RegisterDMACallback(switch_buffers); AUDIO_StartDMA(); while(1) { level = IRQ_Disable(); while(tbuf == cur_buffer) LWP_ThreadSleep(audio_queue); tbuf = cur_buffer; IRQ_Restore(level); ab = af_deq(af, 0); if(am != audio_mode_current) { /* We're not the selected audio output anymore, return. */ if(ab != NULL) ab_free(ab); break; } if(ab != NULL) { const int16_t *src = (const int16_t *)ab->ab_data; int16_t *dst = (int16_t *)buffer[!tbuf]; for(i = 0; i < ADMA_BUFFER_SIZE / 2; i++) *dst++ = (*src++ * audio_vol) >> 16; /* PTS is the time of the first frame of this audio packet */ if((pts = ab->ab_pts) != AV_NOPTS_VALUE && ab->ab_mp != NULL) { #if 0 /* Convert the frame delay into micro seconds */ d = (fr * 1000 / aam->aam_sample_rate) * 1000; /* Subtract it from our timestamp, this will yield the PTS for the sample currently played */ pts -= d; /* Offset with user configure delay */ #endif pts += am->am_audio_delay * 1000; mp = ab->ab_mp; hts_mutex_lock(&mp->mp_clock_mutex); mp->mp_audio_clock = pts; mp->mp_audio_clock_realtime = showtime_get_ts(); mp->mp_audio_clock_epoch = ab->ab_epoch; hts_mutex_unlock(&mp->mp_clock_mutex); } ab_free(ab); } else { memset(buffer[!tbuf], 0, ADMA_BUFFER_SIZE); } DCFlushRange(buffer[!tbuf], ADMA_BUFFER_SIZE); }
static bool wii_audio_start(void *data) { (void)data; AUDIO_StartDMA(); return true; }