/*** 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; }
/*** 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; } }
void Application::step(const JNIEnv *env) { // quick exit if no rom loaded to step if (!_romLoaded) { return; } processInput(); // calculate frameskipping _frameSkipCount=(_frameSkipCount+1)%(_frameSkip+1); // calc framerate /*_timeStart = _timeEnd; _timeEnd = now_ms(); _timeDelta = (_timeEnd - _timeStart); FCEUI_printf("FPS: %g", _timeDelta); */ // check for viewport change from last frame int update = bitmap.viewport.changed & 1; if (update) { int vwidth = bitmap.viewport.w + (2 * bitmap.viewport.x); int vheight = bitmap.viewport.h + (2 * bitmap.viewport.y); // interlaced mode if (config.render && interlaced) { vheight = vheight << 1; } LOGD("Bitmap Changed: %d, %d, %d, pitch: %d", bitmap.viewport.changed, vwidth, vheight, bitmap.pitch); Graphics.ReshapeEmuTexture(vwidth, vheight, SCREEN_RENDER_TEXTURE_WIDTH); _viewPortW = vwidth; _viewPortH = vheight; bitmap.viewport.changed &= ~1; bitmap.pitch = bitmap.viewport.w * bitmap.granularity; } // step emulation system_frame(_frameSkipCount); // draw the texture/fbo Graphics.DrawEMU(bitmap.data, _viewPortW, _viewPortH); // retrieve audio _ssize = audio_update() << 1; //if (!_audioInitialized) _ssize = 0; //LOGD("AudioSamples: %d", _ssize); //LOGD("DONE STEP"); }
void *audio_playback(void *param) { int rc; while(1) { audio_update(0); rc=usleep(1000); // 1ms sleep } return NULL; }
GPGX_EX void gpgx_advance(void) { if (system_hw == SYSTEM_MCD) system_frame_scd(0); else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) system_frame_gen(0); else system_frame_sms(0); if (bitmap.viewport.changed & 1) { bitmap.viewport.changed &= ~1; update_viewport(); } nsamples = audio_update(soundbuffer); }
static void PlaySound() { int size = audio_update() * 2; CellAudio->write((const int16_t* )soundbuffer, size); }