diff --git a/stepmania/src/RageSounds.cpp b/stepmania/src/RageSounds.cpp index 3d38395f18..7b5623e7f3 100644 --- a/stepmania/src/RageSounds.cpp +++ b/stepmania/src/RageSounds.cpp @@ -8,6 +8,7 @@ #include "TimingData.h" #include "NotesLoaderSM.h" #include "PrefsManager.h" +#include "RageDisplay.h" RageSounds *SOUND = NULL; @@ -308,6 +309,41 @@ RageSounds::~RageSounds() delete g_Mutex; } +static float GetFrameTimingAdjustment( float fDeltaTime ) +{ + /* + * We get one update per frame, and we're updated early, almost immediately after vsync, + * near the beginning of the game loop. However, it's very likely that we'll lose the + * scheduler while waiting for vsync, and some other thread will be working. Especially + * with a low-resolution scheduler (Linux 2.4, Win9x), we may not get the scheduler back + * immediately after the vsync; there may be up to a ~10ms delay. This can cause jitter + * in the rendered arrows. + * + * Compensate. If vsync is enabled, and we're maintaining the refresh rate consistently, + * we should have a very precise game loop interval. If we have that, but we're off by + * a small amount (less than the interval), adjust the time to line it up. As long as we + * adjust both the sound time and the timestamp, this won't adversely affect input timing. + * If we're off by more than that, we probably had a frame skip, in which case we have + * bigger skip problems, so don't adjust. + */ + static int iLastFPS = 0; + int iThisFPS = DISPLAY->GetFPS(); + + if( iThisFPS != DISPLAY->GetVideoModeParams().rate || iThisFPS != iLastFPS ) + { + iLastFPS = iThisFPS; + return 0; + } + + const float fExpectedDelay = 1.0f / iThisFPS; + const float fExtraDelay = fDeltaTime - fExpectedDelay; + if( fabsf(fExtraDelay) >= fExpectedDelay ) + return 0; + + /* Subtract the extra delay. (We might have delayed too little, in which case we'll return + * a positive number; this is normal.) */ + return -fExtraDelay; +} void RageSounds::Update( float fDeltaTime ) { @@ -316,6 +352,8 @@ void RageSounds::Update( float fDeltaTime ) if( !g_UpdatingTimer ) return; + const float fAdjust = GetFrameTimingAdjustment( fDeltaTime ); + if( !g_Playing->m_Music->IsPlaying() ) { /* There's no song playing. Fake it. */ @@ -367,7 +405,7 @@ void RageSounds::Update( float fDeltaTime ) } CHECKPOINT_M( ssprintf("%f", fSeconds) ); - GAMESTATE->UpdateSongPosition( fSeconds, g_Playing->m_Timing, tm ); + GAMESTATE->UpdateSongPosition( fSeconds+fAdjust, g_Playing->m_Timing, tm+fAdjust ); }