precise, framerate-independent assist ticks

This commit is contained in:
Glenn Maynard
2004-01-12 09:36:01 +00:00
parent 987db137cc
commit fea54929ac
4 changed files with 91 additions and 56 deletions
+27 -20
View File
@@ -358,33 +358,40 @@ ScreenEdit::~ScreenEdit()
} }
// play assist ticks // play assist ticks
bool ScreenEdit::PlayTicks() const void ScreenEdit::PlayTicks()
{ {
// Sound cards have a latency between when a sample is Play()ed and when the sound if( !GAMESTATE->m_SongOptions.m_bAssistTick || m_EditMode != MODE_PLAYING )
// will start coming out the speaker. Compensate for this by boosting return;
// fPositionSeconds ahead
if( !GAMESTATE->m_SongOptions.m_bAssistTick )
return false;
/* Sound cards have a latency between when a sample is Play()ed and when the sound
* will start coming out the speaker. Compensate for this by boosting fPositionSeconds
* ahead. This is just to make sure that we request the sound early enough for it to
* come out on time; the actual precise timing is handled by SetStartTime. */
float fPositionSeconds = GAMESTATE->m_fMusicSeconds; float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
fPositionSeconds += SOUND->GetPlayLatency() + (float)TICK_EARLY_SECONDS + 0.250f;
const float fSongBeat = GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
// HACK: Play the sound a little bit early to account for the fact that the middle of the tick sounds occurs 0.015 seconds into playing. const int iSongRow = max( 0, BeatToNoteRowNotRounded( fSongBeat ) );
fPositionSeconds += (SOUND->GetPlayLatency()+(float)TICK_EARLY_SECONDS) * m_soundMusic.GetPlaybackRate();
float fSongBeat=GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
int iRowNow = BeatToNoteRowNotRounded( fSongBeat );
iRowNow = max( 0, iRowNow );
static int iRowLastCrossed = 0; static int iRowLastCrossed = 0;
bool bAnyoneHasANote = false; // set this to true if any player has a note at one of the indicies we crossed int iTickRow = -1;
for( int r=iRowLastCrossed+1; r<=iSongRow; r++ ) // for each index we crossed since the last update
if( m_Player.IsThereATapOrHoldHeadAtRow( r ) )
iTickRow = r;
for( int r=iRowLastCrossed+1; r<=iRowNow; r++ ) // for each index we crossed since the last update iRowLastCrossed = iSongRow;
bAnyoneHasANote |= m_Player.IsThereATapOrHoldHeadAtRow( r );
iRowLastCrossed = iRowNow; if( iTickRow != -1 )
{
const float fTickBeat = NoteRowToBeat( iTickRow );
const float fTickSecond = GAMESTATE->m_pCurSong->m_Timing.GetElapsedTimeFromBeat( fTickBeat );
float fSecondsUntil = fTickSecond - GAMESTATE->m_fMusicSeconds;
fSecondsUntil /= m_soundMusic.GetPlaybackRate(); /* 2x music rate means the time until the tick is halved */
return bAnyoneHasANote; RageTimer when = GAMESTATE->m_LastBeatUpdate + (fSecondsUntil - (float)TICK_EARLY_SECONDS);
m_soundAssistTick.SetStartTime( when );
m_soundAssistTick.Play();
}
} }
void ScreenEdit::PlayPreviewMusic() void ScreenEdit::PlayPreviewMusic()
@@ -492,8 +499,7 @@ void ScreenEdit::Update( float fDeltaTime )
m_NoteFieldEdit.Update( fDeltaTime ); m_NoteFieldEdit.Update( fDeltaTime );
if(m_EditMode == MODE_PLAYING && PlayTicks()) PlayTicks();
m_soundAssistTick.Play();
static float fUpdateCounter = 0; static float fUpdateCounter = 0;
fUpdateCounter -= fDeltaTime; fUpdateCounter -= fDeltaTime;
@@ -1198,6 +1204,7 @@ void ScreenEdit::TransitionToEdit()
{ {
m_EditMode = MODE_EDITING; m_EditMode = MODE_EDITING;
m_soundMusic.StopPlaying(); m_soundMusic.StopPlaying();
m_soundAssistTick.StopPlaying(); /* Stop any queued assist ticks. */
m_rectRecordBack.StopTweening(); m_rectRecordBack.StopTweening();
m_rectRecordBack.BeginTweening( 0.5f ); m_rectRecordBack.BeginTweening( 0.5f );
m_rectRecordBack.SetDiffuse( RageColor(0,0,0,0) ); m_rectRecordBack.SetDiffuse( RageColor(0,0,0,0) );
+1 -1
View File
@@ -39,7 +39,7 @@ public:
protected: protected:
void TransitionFromRecordToEdit(); void TransitionFromRecordToEdit();
void TransitionToEdit(); void TransitionToEdit();
bool PlayTicks() const; void PlayTicks();
void PlayPreviewMusic(); void PlayPreviewMusic();
void UpdateTextInfo(); void UpdateTextInfo();
+61 -33
View File
@@ -60,8 +60,8 @@
#define SHOW_SCORE_IN_RAVE THEME->GetMetricB("ScreenGameplay","ShowScoreInRave") #define SHOW_SCORE_IN_RAVE THEME->GetMetricB("ScreenGameplay","ShowScoreInRave")
#define SONG_POSITION_METER_WIDTH THEME->GetMetricF("ScreenGameplay","SongPositionMeterWidth") #define SONG_POSITION_METER_WIDTH THEME->GetMetricF("ScreenGameplay","SongPositionMeterWidth")
CachedThemeMetricF SECONDS_BETWEEN_COMMENTS ("ScreenGameplay","SecondsBetweenComments"); static CachedThemeMetricF SECONDS_BETWEEN_COMMENTS ("ScreenGameplay","SecondsBetweenComments");
CachedThemeMetricF G_TICK_EARLY_SECONDS ("ScreenGameplay","TickEarlySeconds"); static CachedThemeMetricF TICK_EARLY_SECONDS ("ScreenGameplay","TickEarlySeconds");
/* Global, so it's accessible from ShowSavePrompt: */ /* Global, so it's accessible from ShowSavePrompt: */
static float g_fOldOffset; // used on offset screen to calculate difference static float g_fOldOffset; // used on offset screen to calculate difference
@@ -105,7 +105,7 @@ ScreenGameplay::ScreenGameplay( CString sName, bool bDemonstration ) : Screen("S
SOUND->HandleSongTimer( false ); SOUND->HandleSongTimer( false );
SECONDS_BETWEEN_COMMENTS.Refresh(); SECONDS_BETWEEN_COMMENTS.Refresh();
G_TICK_EARLY_SECONDS.Refresh(); TICK_EARLY_SECONDS.Refresh();
//need to initialize these before checking for demonstration mode //need to initialize these before checking for demonstration mode
//otherwise destructor will try to delete possibly invalid pointers //otherwise destructor will try to delete possibly invalid pointers
@@ -753,6 +753,7 @@ ScreenGameplay::~ScreenGameplay()
SAFE_DELETE( m_pCombinedLifeMeter ); SAFE_DELETE( m_pCombinedLifeMeter );
m_soundMusic.StopPlaying(); m_soundMusic.StopPlaying();
m_soundAssistTick.StopPlaying(); /* Stop any queued assist ticks. */
} }
bool ScreenGameplay::IsLastSong() bool ScreenGameplay::IsLastSong()
@@ -1016,36 +1017,40 @@ float ScreenGameplay::StartPlayingSong(float MinTimeToNotes, float MinTimeToMusi
} }
// play assist ticks // play assist ticks
bool ScreenGameplay::IsTimeToPlayTicks() const void ScreenGameplay::PlayTicks()
{ {
// Sound cards have a latency between when a sample is Play()ed and when the sound if( !GAMESTATE->m_SongOptions.m_bAssistTick )
// will start coming out the speaker. Compensate for this by boosting return;
// fPositionSeconds ahead
float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
fPositionSeconds += (SOUND->GetPlayLatency()+(float)G_TICK_EARLY_SECONDS) * m_soundMusic.GetPlaybackRate();
float fSongBeat = GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
int iRowNow = BeatToNoteRowNotRounded( fSongBeat ); /* Sound cards have a latency between when a sample is Play()ed and when the sound
iRowNow = max( 0, iRowNow ); * will start coming out the speaker. Compensate for this by boosting fPositionSeconds
* ahead. This is just to make sure that we request the sound early enough for it to
* come out on time; the actual precise timing is handled by SetStartTime. */
float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
fPositionSeconds += SOUND->GetPlayLatency() + (float)TICK_EARLY_SECONDS + 0.250f;
const float fSongBeat = GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
const int iSongRow = max( 0, BeatToNoteRowNotRounded( fSongBeat ) );
static int iRowLastCrossed = 0; static int iRowLastCrossed = 0;
bool bAnyoneHasANote = false; // set this to true if any player has a note at one of the indicies we crossed int iTickRow = -1;
for( int r=iRowLastCrossed+1; r<=iSongRow; r++ ) // for each index we crossed since the last update
if( m_Player[GAMESTATE->m_MasterPlayerNumber].IsThereATapOrHoldHeadAtRow( r ) )
iTickRow = r;
for( int r=iRowLastCrossed+1; r<=iRowNow; r++ ) // for each index we crossed since the last update iRowLastCrossed = iSongRow;
if( iTickRow != -1 )
{ {
for( int p=0; p<NUM_PLAYERS; p++ ) const float fTickBeat = NoteRowToBeat( iTickRow );
{ const float fTickSecond = GAMESTATE->m_pCurSong->m_Timing.GetElapsedTimeFromBeat( fTickBeat );
if( !GAMESTATE->IsPlayerEnabled( (PlayerNumber)p ) ) float fSecondsUntil = fTickSecond - GAMESTATE->m_fMusicSeconds;
continue; // skip fSecondsUntil /= m_soundMusic.GetPlaybackRate(); /* 2x music rate means the time until the tick is halved */
bAnyoneHasANote |= m_Player[p].IsThereATapOrHoldHeadAtRow( r ); RageTimer when = GAMESTATE->m_LastBeatUpdate + (fSecondsUntil - (float)TICK_EARLY_SECONDS);
break; // this will only play the tick for the first player that is joined m_soundAssistTick.SetStartTime( when );
} m_soundAssistTick.Play();
} }
iRowLastCrossed = iRowNow;
return bAnyoneHasANote;
} }
@@ -1302,20 +1307,41 @@ void ScreenGameplay::Update( float fDeltaTime )
} }
// //
// update assist ticks // play assist ticks
// //
bool bPlayTicks = IsTimeToPlayTicks(); PlayTicks();
if( bPlayTicks )
{
if( GAMESTATE->m_SongOptions.m_bAssistTick )
m_soundAssistTick.Play();
}
// //
// update lights // update lights
// //
bool bAnyoneHasANote = false; // set this to true if any player has a note at one of the indices we crossed
{
float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
fPositionSeconds += (SOUND->GetPlayLatency()) * m_soundMusic.GetPlaybackRate();
float fSongBeat = GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds );
int iRowNow = BeatToNoteRowNotRounded( fSongBeat );
iRowNow = max( 0, iRowNow );
static int iRowLastCrossed = 0;
for( int r=iRowLastCrossed+1; r<=iRowNow; r++ ) // for each index we crossed since the last update
{
for( int p=0; p<NUM_PLAYERS; p++ )
{
if( !GAMESTATE->IsPlayerEnabled( (PlayerNumber)p ) )
continue; // skip
bAnyoneHasANote |= m_Player[p].IsThereATapOrHoldHeadAtRow( r );
break; // this will only play the tick for the first player that is joined
}
}
iRowLastCrossed = iRowNow;
}
static float s_fSecsLeftOnUpperLights = 0; static float s_fSecsLeftOnUpperLights = 0;
if( bPlayTicks ) if( bAnyoneHasANote )
{ {
float fSecsPerBeat = 1.f/GAMESTATE->m_fCurBPS; float fSecsPerBeat = 1.f/GAMESTATE->m_fCurBPS;
float fSecsToLight = fSecsPerBeat*.2f; float fSecsToLight = fSecsPerBeat*.2f;
@@ -1478,6 +1504,7 @@ void ScreenGameplay::Input( const DeviceInput& DeviceI, const InputEventType typ
* We're doing #3. I'm not sure which is best. * We're doing #3. I'm not sure which is best.
*/ */
m_soundMusic.StopPlaying(); m_soundMusic.StopPlaying();
m_soundAssistTick.StopPlaying(); /* Stop any queued assist ticks. */
this->ClearMessageQueue(); this->ClearMessageQueue();
m_Back.StartTransitioning( SM_SaveChangedBeforeGoingBack ); m_Back.StartTransitioning( SM_SaveChangedBeforeGoingBack );
@@ -2115,6 +2142,7 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM )
case SM_BeginFailed: case SM_BeginFailed:
m_DancingState = STATE_OUTRO; m_DancingState = STATE_OUTRO;
m_soundMusic.StopPlaying(); m_soundMusic.StopPlaying();
m_soundAssistTick.StopPlaying(); /* Stop any queued assist ticks. */
TweenOffScreen(); TweenOffScreen();
m_Failed.StartTransitioning( SM_GoToScreenAfterFail ); m_Failed.StartTransitioning( SM_GoToScreenAfterFail );
+1 -1
View File
@@ -79,7 +79,7 @@ protected:
float StartPlayingSong(float MinTimeToNotes, float MinTimeToMusic); float StartPlayingSong(float MinTimeToNotes, float MinTimeToMusic);
void ShowSavePrompt( ScreenMessage SM_SendWhenDone ); void ShowSavePrompt( ScreenMessage SM_SendWhenDone );
bool IsTimeToPlayTicks() const; void PlayTicks();
void UpdateLyrics( float fDeltaTime ); void UpdateLyrics( float fDeltaTime );
void UpdateCheckFail(); void UpdateCheckFail();
void SongFinished(); void SongFinished();