Added lookup table system to TimingData so that GetBeatAndBPS and GetElapsedTime don't have to walk through the entire list of timing segments several times every frame during gameplay.
This commit is contained in:
+4
-5
@@ -712,16 +712,15 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus
|
||||
if( m_aBGChanges.size() == 0 )
|
||||
return;
|
||||
|
||||
float fBeat, fBPS, fThrowAway;
|
||||
bool bFreeze;
|
||||
int iThrowAway;
|
||||
pSong->m_SongTiming.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze, bFreeze, iThrowAway, fThrowAway );
|
||||
TimingData::GetBeatArgs beat_info;
|
||||
beat_info.elapsed_time= fCurrentTime;
|
||||
pSong->m_SongTiming.GetBeatAndBPSFromElapsedTime(beat_info);
|
||||
|
||||
// Calls to Update() should *not* be scaled by music rate; fCurrentTime is. Undo it.
|
||||
const float fRate = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
||||
|
||||
// Find the BGSegment we're in
|
||||
const int i = FindBGSegmentForBeat( fBeat );
|
||||
const int i = FindBGSegmentForBeat(beat_info.beat);
|
||||
|
||||
float fDeltaTime = fCurrentTime - fLastMusicSeconds;
|
||||
if( i != -1 && i != m_iCurBGChangeIndex ) // we're changing backgrounds
|
||||
|
||||
+6
-9
@@ -2701,20 +2701,17 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
||||
int iMissIfOlderThanThisRow;
|
||||
const float fEarliestTime = m_pPlayerState->m_Position.m_fMusicSeconds - fMissIfOlderThanSeconds;
|
||||
{
|
||||
bool bFreeze, bDelay;
|
||||
float fMissIfOlderThanThisBeat;
|
||||
float fThrowAway;
|
||||
int iWarpBeginRow;
|
||||
float fWarpLength;
|
||||
m_Timing->GetBeatAndBPSFromElapsedTime( fEarliestTime, fMissIfOlderThanThisBeat, fThrowAway, bFreeze, bDelay, iWarpBeginRow, fWarpLength );
|
||||
TimingData::GetBeatArgs beat_info;
|
||||
beat_info.elapsed_time= fEarliestTime;
|
||||
m_Timing->GetBeatAndBPSFromElapsedTime(beat_info);
|
||||
|
||||
iMissIfOlderThanThisRow = BeatToNoteRow( fMissIfOlderThanThisBeat );
|
||||
if( bFreeze || bDelay )
|
||||
iMissIfOlderThanThisRow = BeatToNoteRow(beat_info.beat);
|
||||
if(beat_info.freeze_out || beat_info.delay_out )
|
||||
{
|
||||
/* If there is a freeze on iMissIfOlderThanThisIndex, include this index too.
|
||||
* Otherwise we won't show misses for tap notes on freezes until the
|
||||
* freeze finishes. */
|
||||
if( !bDelay )
|
||||
if(!beat_info.delay_out)
|
||||
iMissIfOlderThanThisRow++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1486,6 +1486,13 @@ void ScreenGameplay::StartPlayingSong( float fMinTimeToNotes, float fMinTimeToMu
|
||||
UpdateSongPosition(0);
|
||||
|
||||
ASSERT( GAMESTATE->m_Position.m_fMusicSeconds > -4000 ); /* make sure the "fake timer" code doesn't trigger */
|
||||
FOREACH_EnabledPlayer(pn)
|
||||
{
|
||||
if(GAMESTATE->m_pCurSteps[pn])
|
||||
{
|
||||
GAMESTATE->m_pCurSteps[pn]->GetTimingData()->PrepareLookup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2548,6 +2555,13 @@ void ScreenGameplay::SaveStats()
|
||||
|
||||
void ScreenGameplay::SongFinished()
|
||||
{
|
||||
FOREACH_EnabledPlayer(pn)
|
||||
{
|
||||
if(GAMESTATE->m_pCurSteps[pn])
|
||||
{
|
||||
GAMESTATE->m_pCurSteps[pn]->GetTimingData()->ReleaseLookup();
|
||||
}
|
||||
}
|
||||
AdjustSync::HandleSongEnd();
|
||||
SaveStats(); // Let subclasses save the stats.
|
||||
/* Extremely important: if we don't remove attacks before moving on to the next
|
||||
|
||||
@@ -198,6 +198,19 @@ bool ScreenSyncOverlay::Input( const InputEventPlus &input )
|
||||
return true;
|
||||
}
|
||||
|
||||
// Release the lookup tables being used for the timing data because
|
||||
// changing the timing data invalidates them. -Kyz
|
||||
if(a != Action_Invalid)
|
||||
{
|
||||
FOREACH_EnabledPlayer(pn)
|
||||
{
|
||||
if(GAMESTATE->m_pCurSteps[pn])
|
||||
{
|
||||
GAMESTATE->m_pCurSteps[pn]->GetTimingData()->ReleaseLookup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch( a )
|
||||
{
|
||||
case RevertSyncChanges:
|
||||
|
||||
+12
-6
@@ -11,7 +11,15 @@ void SongPosition::UpdateSongPosition( float fPositionSeconds, const TimingData
|
||||
else
|
||||
m_LastBeatUpdate.Touch();
|
||||
|
||||
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_fWarpDestination );
|
||||
TimingData::GetBeatArgs beat_info;
|
||||
beat_info.elapsed_time= fPositionSeconds;
|
||||
timing.GetBeatAndBPSFromElapsedTime(beat_info);
|
||||
m_fSongBeat= beat_info.beat;
|
||||
m_fCurBPS= beat_info.bps_out;
|
||||
m_bFreeze= beat_info.freeze_out;
|
||||
m_bDelay= beat_info.delay_out;
|
||||
m_iWarpBeginRow= beat_info.warp_begin_out;
|
||||
m_fWarpDestination= beat_info.warp_dest_out;
|
||||
|
||||
// "Crash reason : -243478.890625 -48695.773438"
|
||||
// The question is why is -2000 used as the limit? -aj
|
||||
@@ -24,11 +32,9 @@ void SongPosition::UpdateSongPosition( float fPositionSeconds, const TimingData
|
||||
m_fSongBeatNoOffset = timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds );
|
||||
|
||||
m_fMusicSecondsVisible = fPositionSeconds - g_fVisualDelaySeconds.Get();
|
||||
float fThrowAway, fThrowAway2;
|
||||
bool bThrowAway;
|
||||
int iThrowAway;
|
||||
timing.GetBeatAndBPSFromElapsedTime( m_fMusicSecondsVisible, m_fSongBeatVisible, fThrowAway, bThrowAway, bThrowAway, iThrowAway, fThrowAway2 );
|
||||
|
||||
beat_info.elapsed_time= m_fMusicSecondsVisible;
|
||||
timing.GetBeatAndBPSFromElapsedTime(beat_info);
|
||||
m_fSongBeatVisible= beat_info.beat;
|
||||
}
|
||||
|
||||
void SongPosition::Reset()
|
||||
|
||||
+289
-203
@@ -51,6 +51,89 @@ TimingData::~TimingData()
|
||||
Clear();
|
||||
}
|
||||
|
||||
void TimingData::PrepareLookup()
|
||||
{
|
||||
// If multiple players have the same timing data, then adding to the
|
||||
// lookups would probably cause FindEntryInLookup to return the wrong
|
||||
// thing. So release the lookups. -Kyz
|
||||
ReleaseLookup();
|
||||
const unsigned int segments_per_lookup= 32;
|
||||
const vector<TimingSegment*>* segs= m_avpTimingSegments;
|
||||
const vector<TimingSegment*>& bpms= segs[SEGMENT_BPM];
|
||||
const vector<TimingSegment*>& warps= segs[SEGMENT_WARP];
|
||||
const vector<TimingSegment*>& stops= segs[SEGMENT_STOP];
|
||||
const vector<TimingSegment*>& delays= segs[SEGMENT_DELAY];
|
||||
|
||||
unsigned int total_segments= bpms.size() + warps.size() + stops.size() + delays.size();
|
||||
unsigned int lookup_entries= total_segments / segments_per_lookup;
|
||||
m_beat_start_lookup.reserve(lookup_entries);
|
||||
m_time_start_lookup.reserve(lookup_entries);
|
||||
for(unsigned int curr_segment= segments_per_lookup;
|
||||
curr_segment < total_segments; curr_segment+= segments_per_lookup)
|
||||
{
|
||||
GetBeatStarts beat_start;
|
||||
beat_start.last_time= -m_fBeat0OffsetInSeconds;
|
||||
GetBeatArgs args;
|
||||
args.elapsed_time= FLT_MAX;
|
||||
GetBeatInternal(beat_start, args, curr_segment);
|
||||
m_beat_start_lookup.push_back(lookup_item_t(args.elapsed_time, beat_start));
|
||||
|
||||
GetBeatStarts time_start;
|
||||
time_start.last_time= -m_fBeat0OffsetInSeconds;
|
||||
float time= GetElapsedTimeInternal(time_start, FLT_MAX, curr_segment);
|
||||
m_time_start_lookup.push_back(lookup_item_t(time, time_start));
|
||||
}
|
||||
}
|
||||
|
||||
void TimingData::ReleaseLookup()
|
||||
{
|
||||
// According to The C++ Programming Language 3rd Ed., decreasing the size
|
||||
// of a vector doesn't actually free the memory it has allocated. So this
|
||||
// small trick is required to actually free the memory. -Kyz
|
||||
#define CLEAR_LOOKUP(lookup) \
|
||||
{ \
|
||||
lookup.clear(); \
|
||||
beat_start_lookup_t tmp= lookup; \
|
||||
lookup.swap(tmp); \
|
||||
}
|
||||
CLEAR_LOOKUP(m_beat_start_lookup);
|
||||
CLEAR_LOOKUP(m_time_start_lookup);
|
||||
#undef CLEAR_LOOKUP
|
||||
}
|
||||
|
||||
TimingData::beat_start_lookup_t::const_iterator FindEntryInLookup(
|
||||
const TimingData::beat_start_lookup_t& lookup, float entry)
|
||||
{
|
||||
if(lookup.empty())
|
||||
{
|
||||
return lookup.end();
|
||||
}
|
||||
size_t lower= 0;
|
||||
size_t upper= lookup.size()-1;
|
||||
if(lookup[lower].first > entry)
|
||||
{
|
||||
return lookup.end();
|
||||
}
|
||||
while(upper - lower > 1)
|
||||
{
|
||||
size_t next= (upper + lower) / 2;
|
||||
if(lookup[next].first > entry)
|
||||
{
|
||||
upper= next;
|
||||
}
|
||||
else if(lookup[next].first < entry)
|
||||
{
|
||||
lower= next;
|
||||
}
|
||||
else
|
||||
{
|
||||
lower= next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lookup.begin() + lower;
|
||||
}
|
||||
|
||||
bool TimingData::empty() const
|
||||
{
|
||||
FOREACH_TimingSegmentType( tst )
|
||||
@@ -491,11 +574,10 @@ bool TimingData::DoesLabelExist( const RString& sLabel ) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const
|
||||
void TimingData::GetBeatAndBPSFromElapsedTime(GetBeatArgs& args) const
|
||||
{
|
||||
fElapsedTime += PREFSMAN->m_fGlobalOffsetSeconds;
|
||||
|
||||
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut, iWarpBeginOut, fWarpLengthOut );
|
||||
args.elapsed_time += PREFSMAN->m_fGlobalOffsetSeconds;
|
||||
GetBeatAndBPSFromElapsedTimeNoOffset(args);
|
||||
}
|
||||
|
||||
enum
|
||||
@@ -510,137 +592,226 @@ enum
|
||||
NOT_FOUND
|
||||
};
|
||||
|
||||
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const
|
||||
void FindEvent(int& event_row, int& event_type,
|
||||
TimingData::GetBeatStarts& start, float beat, bool find_marker,
|
||||
const vector<TimingSegment*>& bpms, const vector<TimingSegment*>& warps,
|
||||
const vector<TimingSegment*>& stops, const vector<TimingSegment*>& delays)
|
||||
{
|
||||
const vector<TimingSegment *> * segs = m_avpTimingSegments;
|
||||
vector<TimingSegment *>::const_iterator itBPMS = segs[SEGMENT_BPM].begin();
|
||||
vector<TimingSegment *>::const_iterator itWS = segs[SEGMENT_WARP].begin();
|
||||
vector<TimingSegment *>::const_iterator itSS = segs[SEGMENT_STOP].begin();
|
||||
vector<TimingSegment *>::const_iterator itDS = segs[SEGMENT_DELAY].begin();
|
||||
|
||||
bFreezeOut = false;
|
||||
bDelayOut = false;
|
||||
|
||||
iWarpBeginOut = -1;
|
||||
|
||||
int iLastRow = 0;
|
||||
float fLastTime = -m_fBeat0OffsetInSeconds;
|
||||
float fBPS = GetBPMAtRow(0) / 60.0f;
|
||||
|
||||
float bIsWarping = false;
|
||||
float fWarpDestination = 0;
|
||||
|
||||
for( ;; )
|
||||
if(start.is_warping && BeatToNoteRow(start.warp_destination) < event_row)
|
||||
{
|
||||
int iEventRow = INT_MAX;
|
||||
int iEventType = NOT_FOUND;
|
||||
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
|
||||
{
|
||||
iEventRow = BeatToNoteRow(fWarpDestination);
|
||||
iEventType = FOUND_WARP_DESTINATION;
|
||||
}
|
||||
if (itBPMS != segs[SEGMENT_BPM].end() &&
|
||||
(*itBPMS)->GetRow() < iEventRow )
|
||||
{
|
||||
iEventRow = (*itBPMS)->GetRow();
|
||||
iEventType = FOUND_BPM_CHANGE;
|
||||
}
|
||||
if (itDS != segs[SEGMENT_DELAY].end() &&
|
||||
(*itDS)->GetRow() < iEventRow)
|
||||
{
|
||||
iEventRow = (*itDS)->GetRow();
|
||||
iEventType = FOUND_DELAY;
|
||||
}
|
||||
if (itSS != segs[SEGMENT_STOP].end() &&
|
||||
(*itSS)->GetRow() < iEventRow ) // && iEventType != FOUND_DELAY )
|
||||
{
|
||||
int tmpRow = iEventRow;
|
||||
iEventRow = (*itSS)->GetRow();
|
||||
iEventType = (tmpRow == iEventRow) ? FOUND_STOP_DELAY : FOUND_STOP;
|
||||
}
|
||||
if (itWS != segs[SEGMENT_WARP].end() &&
|
||||
(*itWS)->GetRow() < iEventRow )
|
||||
{
|
||||
iEventRow = (*itWS)->GetRow();
|
||||
iEventType = FOUND_WARP;
|
||||
}
|
||||
if( iEventType == NOT_FOUND )
|
||||
event_row= BeatToNoteRow(start.warp_destination);
|
||||
event_type= FOUND_WARP_DESTINATION;
|
||||
}
|
||||
if(start.bpm < bpms.size() && bpms[start.bpm]->GetRow() < event_row)
|
||||
{
|
||||
event_row= bpms[start.bpm]->GetRow();
|
||||
event_type= FOUND_BPM_CHANGE;
|
||||
}
|
||||
if(start.delay < delays.size() && delays[start.delay]->GetRow() < event_row)
|
||||
{
|
||||
event_row= delays[start.delay]->GetRow();
|
||||
event_type= FOUND_DELAY;
|
||||
}
|
||||
if(find_marker && BeatToNoteRow(beat) < event_row)
|
||||
{
|
||||
event_row= BeatToNoteRow(beat);
|
||||
event_type= FOUND_MARKER;
|
||||
}
|
||||
if(start.stop < stops.size() && stops[start.stop]->GetRow() < event_row)
|
||||
{
|
||||
int tmp_row= event_row;
|
||||
event_row= stops[start.stop]->GetRow();
|
||||
event_type= (tmp_row == event_row) ? FOUND_STOP_DELAY : FOUND_STOP;
|
||||
}
|
||||
if(start.warp < warps.size() && warps[start.warp]->GetRow() < event_row)
|
||||
{
|
||||
event_row= warps[start.warp]->GetRow();
|
||||
event_type= FOUND_WARP;
|
||||
}
|
||||
}
|
||||
|
||||
void TimingData::GetBeatInternal(GetBeatStarts& start, GetBeatArgs& args,
|
||||
unsigned int max_segment) const
|
||||
{
|
||||
const vector<TimingSegment*>* segs= m_avpTimingSegments;
|
||||
const vector<TimingSegment*>& bpms= segs[SEGMENT_BPM];
|
||||
const vector<TimingSegment*>& warps= segs[SEGMENT_WARP];
|
||||
const vector<TimingSegment*>& stops= segs[SEGMENT_STOP];
|
||||
const vector<TimingSegment*>& delays= segs[SEGMENT_DELAY];
|
||||
unsigned int curr_segment= start.bpm+start.warp+start.stop+start.delay;
|
||||
|
||||
float bps= GetBPMAtRow(start.last_row) / 60.0f;
|
||||
#define INC_INDEX(index) ++curr_segment; ++index;
|
||||
|
||||
while(curr_segment < max_segment)
|
||||
{
|
||||
int event_row= INT_MAX;
|
||||
int event_type= NOT_FOUND;
|
||||
FindEvent(event_row, event_type, start, 0, false, bpms, warps, stops,
|
||||
delays);
|
||||
if(event_type == NOT_FOUND)
|
||||
{
|
||||
break;
|
||||
}
|
||||
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
|
||||
float fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
if ( fElapsedTime < fNextEventTime )
|
||||
float time_to_next_event= start.is_warping ? 0 :
|
||||
NoteRowToBeat(event_row - start.last_row) / bps;
|
||||
float next_event_time= start.last_time + time_to_next_event;
|
||||
if(args.elapsed_time < next_event_time)
|
||||
{
|
||||
break;
|
||||
}
|
||||
fLastTime = fNextEventTime;
|
||||
switch( iEventType )
|
||||
start.last_time= next_event_time;
|
||||
switch(event_type)
|
||||
{
|
||||
case FOUND_WARP_DESTINATION:
|
||||
bIsWarping = false;
|
||||
start.is_warping= false;
|
||||
break;
|
||||
case FOUND_BPM_CHANGE:
|
||||
fBPS = ToBPM(*itBPMS)->GetBPS();
|
||||
itBPMS ++;
|
||||
bps= ToBPM(bpms[start.bpm])->GetBPS();
|
||||
INC_INDEX(start.bpm);
|
||||
break;
|
||||
case FOUND_DELAY:
|
||||
case FOUND_STOP_DELAY:
|
||||
{
|
||||
const DelaySegment *ss = ToDelay(*itDS);
|
||||
fTimeToNextEvent = ss->GetPause();
|
||||
fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
if ( fElapsedTime < fNextEventTime )
|
||||
{
|
||||
bFreezeOut = false;
|
||||
bDelayOut = true;
|
||||
fBeatOut = ss->GetBeat();
|
||||
fBPSOut = fBPS;
|
||||
return;
|
||||
const DelaySegment* ss= ToDelay(delays[start.delay]);
|
||||
time_to_next_event= ss->GetPause();
|
||||
next_event_time= start.last_time + time_to_next_event;
|
||||
if(args.elapsed_time < next_event_time)
|
||||
{
|
||||
args.freeze_out= false;
|
||||
args.delay_out= true;
|
||||
args.beat= ss->GetBeat();
|
||||
args.bps_out= bps;
|
||||
return;
|
||||
}
|
||||
start.last_time= next_event_time;
|
||||
INC_INDEX(start.delay);
|
||||
if(event_type == FOUND_DELAY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
fLastTime = fNextEventTime;
|
||||
itDS ++;
|
||||
if (iEventType == FOUND_DELAY)
|
||||
break;
|
||||
}
|
||||
case FOUND_STOP:
|
||||
{
|
||||
const StopSegment *ss = ToStop(*itSS);
|
||||
fTimeToNextEvent = ss->GetPause();
|
||||
fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
if ( fElapsedTime < fNextEventTime )
|
||||
{
|
||||
bFreezeOut = true;
|
||||
bDelayOut = false;
|
||||
fBeatOut = ss->GetBeat();
|
||||
fBPSOut = fBPS;
|
||||
return;
|
||||
const StopSegment* ss= ToStop(stops[start.stop]);
|
||||
time_to_next_event= ss->GetPause();
|
||||
next_event_time= start.last_time + time_to_next_event;
|
||||
if(args.elapsed_time < next_event_time)
|
||||
{
|
||||
args.freeze_out= true;
|
||||
args.delay_out= false;
|
||||
args.beat= ss->GetBeat();
|
||||
args.bps_out= bps;
|
||||
return;
|
||||
}
|
||||
start.last_time= next_event_time;
|
||||
INC_INDEX(start.stop);
|
||||
break;
|
||||
}
|
||||
fLastTime = fNextEventTime;
|
||||
itSS ++;
|
||||
break;
|
||||
}
|
||||
case FOUND_WARP:
|
||||
{
|
||||
bIsWarping = true;
|
||||
const WarpSegment *ws = ToWarp(*itWS);
|
||||
float fWarpSum = ws->GetLength() + ws->GetBeat();
|
||||
if( fWarpSum > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = fWarpSum;
|
||||
start.is_warping= true;
|
||||
const WarpSegment* ws= ToWarp(warps[start.warp]);
|
||||
float warp_sum= ws->GetLength() + ws->GetBeat();
|
||||
if(warp_sum > start.warp_destination)
|
||||
{
|
||||
start.warp_destination= warp_sum;
|
||||
}
|
||||
args.warp_begin_out= event_row;
|
||||
args.warp_dest_out= start.warp_destination;
|
||||
INC_INDEX(start.warp);
|
||||
break;
|
||||
}
|
||||
iWarpBeginOut = iEventRow;
|
||||
fWarpDestinationOut = fWarpDestination;
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iLastRow = iEventRow;
|
||||
start.last_row= event_row;
|
||||
}
|
||||
|
||||
fBeatOut = NoteRowToBeat( iLastRow ) + (fElapsedTime - fLastTime) * fBPS;
|
||||
fBPSOut = fBPS;
|
||||
|
||||
#undef INC_INDEX
|
||||
if(args.elapsed_time == FLT_MAX)
|
||||
{
|
||||
args.elapsed_time= start.last_time;
|
||||
}
|
||||
args.beat= NoteRowToBeat(start.last_row) +
|
||||
(args.elapsed_time - start.last_time) * bps;
|
||||
args.bps_out= bps;
|
||||
}
|
||||
|
||||
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset(GetBeatArgs& args) const
|
||||
{
|
||||
GetBeatStarts start;
|
||||
start.last_time= -m_fBeat0OffsetInSeconds;
|
||||
beat_start_lookup_t::const_iterator looked_up_start=
|
||||
FindEntryInLookup(m_beat_start_lookup, args.elapsed_time);
|
||||
if(looked_up_start != m_beat_start_lookup.end())
|
||||
{
|
||||
start= looked_up_start->second;
|
||||
}
|
||||
GetBeatInternal(start, args, INT_MAX);
|
||||
}
|
||||
|
||||
float TimingData::GetElapsedTimeInternal(GetBeatStarts& start, float beat,
|
||||
unsigned int max_segment) const
|
||||
{
|
||||
const vector<TimingSegment*>* segs= m_avpTimingSegments;
|
||||
const vector<TimingSegment*>& bpms= segs[SEGMENT_BPM];
|
||||
const vector<TimingSegment*>& warps= segs[SEGMENT_WARP];
|
||||
const vector<TimingSegment*>& stops= segs[SEGMENT_STOP];
|
||||
const vector<TimingSegment*>& delays= segs[SEGMENT_DELAY];
|
||||
unsigned int curr_segment= start.bpm+start.warp+start.stop+start.delay;
|
||||
|
||||
float bps= GetBPMAtRow(start.last_row) / 60.0f;
|
||||
#define INC_INDEX(index) ++curr_segment; ++index;
|
||||
|
||||
while(curr_segment < max_segment)
|
||||
{
|
||||
int event_row= INT_MAX;
|
||||
int event_type= NOT_FOUND;
|
||||
FindEvent(event_row, event_type, start, beat, true, bpms, warps, stops,
|
||||
delays);
|
||||
float time_to_next_event= start.is_warping ? 0 :
|
||||
NoteRowToBeat(event_row - start.last_row) / bps;
|
||||
float next_event_time= start.last_time + time_to_next_event;
|
||||
start.last_time= next_event_time;
|
||||
switch(event_type)
|
||||
{
|
||||
case FOUND_WARP_DESTINATION:
|
||||
start.is_warping= false;
|
||||
break;
|
||||
case FOUND_BPM_CHANGE:
|
||||
bps= ToBPM(bpms[start.bpm])->GetBPS();
|
||||
INC_INDEX(start.bpm);
|
||||
break;
|
||||
case FOUND_STOP:
|
||||
case FOUND_STOP_DELAY:
|
||||
time_to_next_event= ToStop(stops[start.stop])->GetPause();
|
||||
next_event_time= start.last_time + time_to_next_event;
|
||||
start.last_time= next_event_time;
|
||||
INC_INDEX(start.stop);
|
||||
break;
|
||||
case FOUND_DELAY:
|
||||
time_to_next_event= ToDelay(delays[start.delay])->GetPause();
|
||||
next_event_time= start.last_time + time_to_next_event;
|
||||
start.last_time= next_event_time;
|
||||
INC_INDEX(start.delay);
|
||||
break;
|
||||
case FOUND_MARKER:
|
||||
return start.last_time;
|
||||
case FOUND_WARP:
|
||||
{
|
||||
start.is_warping= true;
|
||||
WarpSegment* ws= ToWarp(warps[start.warp]);
|
||||
float warp_sum= ws->GetLength() + ws->GetBeat();
|
||||
if(warp_sum > start.warp_destination)
|
||||
{
|
||||
start.warp_destination= warp_sum;
|
||||
}
|
||||
INC_INDEX(start.warp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
start.last_row= event_row;
|
||||
}
|
||||
#undef INC_INDEX
|
||||
return start.last_time;
|
||||
}
|
||||
|
||||
float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
|
||||
@@ -650,101 +821,16 @@ float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
|
||||
|
||||
float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
|
||||
{
|
||||
const vector<TimingSegment *> * segs = m_avpTimingSegments;
|
||||
vector<TimingSegment *>::const_iterator itBPMS = segs[SEGMENT_BPM].begin();
|
||||
vector<TimingSegment *>::const_iterator itWS = segs[SEGMENT_WARP].begin();
|
||||
vector<TimingSegment *>::const_iterator itSS = segs[SEGMENT_STOP].begin();
|
||||
vector<TimingSegment *>::const_iterator itDS = segs[SEGMENT_DELAY].begin();
|
||||
|
||||
int iLastRow = 0;
|
||||
float fLastTime = -m_fBeat0OffsetInSeconds;
|
||||
float fBPS = GetBPMAtRow(0) / 60.0f;
|
||||
|
||||
float bIsWarping = false;
|
||||
float fWarpDestination = 0;
|
||||
|
||||
for( ;; )
|
||||
GetBeatStarts start;
|
||||
start.last_time= -m_fBeat0OffsetInSeconds;
|
||||
beat_start_lookup_t::const_iterator looked_up_start=
|
||||
FindEntryInLookup(m_time_start_lookup, fBeat);
|
||||
if(looked_up_start != m_time_start_lookup.end())
|
||||
{
|
||||
int iEventRow = INT_MAX;
|
||||
int iEventType = NOT_FOUND;
|
||||
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
|
||||
{
|
||||
iEventRow = BeatToNoteRow(fWarpDestination);
|
||||
iEventType = FOUND_WARP_DESTINATION;
|
||||
}
|
||||
if (itBPMS != segs[SEGMENT_BPM].end() &&
|
||||
(*itBPMS)->GetRow() < iEventRow )
|
||||
{
|
||||
iEventRow = (*itBPMS)->GetRow();
|
||||
iEventType = FOUND_BPM_CHANGE;
|
||||
}
|
||||
if (itDS != segs[SEGMENT_DELAY].end() &&
|
||||
(*itDS)->GetRow() < iEventRow ) // delays (come before marker)
|
||||
{
|
||||
iEventRow = (*itDS)->GetRow();
|
||||
iEventType = FOUND_DELAY;
|
||||
}
|
||||
if( BeatToNoteRow(fBeat) < iEventRow )
|
||||
{
|
||||
iEventRow = BeatToNoteRow(fBeat);
|
||||
iEventType = FOUND_MARKER;
|
||||
}
|
||||
if (itSS != segs[SEGMENT_STOP].end() &&
|
||||
(*itSS)->GetRow() < iEventRow ) // stops (come after marker)
|
||||
{
|
||||
iEventRow = (*itSS)->GetRow();
|
||||
iEventType = FOUND_STOP;
|
||||
}
|
||||
if (itWS != segs[SEGMENT_WARP].end() &&
|
||||
(*itWS)->GetRow() < iEventRow )
|
||||
{
|
||||
iEventRow = (*itWS)->GetRow();
|
||||
iEventType = FOUND_WARP;
|
||||
}
|
||||
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
|
||||
float fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
fLastTime = fNextEventTime;
|
||||
switch( iEventType )
|
||||
{
|
||||
case FOUND_WARP_DESTINATION:
|
||||
bIsWarping = false;
|
||||
break;
|
||||
case FOUND_BPM_CHANGE:
|
||||
fBPS = ToBPM(*itBPMS)->GetBPS();
|
||||
itBPMS ++;
|
||||
break;
|
||||
case FOUND_STOP:
|
||||
fTimeToNextEvent = ToStop(*itSS)->GetPause();
|
||||
fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
fLastTime = fNextEventTime;
|
||||
itSS ++;
|
||||
break;
|
||||
case FOUND_DELAY:
|
||||
fTimeToNextEvent = ToDelay(*itDS)->GetPause();
|
||||
fNextEventTime = fLastTime + fTimeToNextEvent;
|
||||
fLastTime = fNextEventTime;
|
||||
itDS ++;
|
||||
break;
|
||||
case FOUND_MARKER:
|
||||
return fLastTime;
|
||||
case FOUND_WARP:
|
||||
{
|
||||
bIsWarping = true;
|
||||
WarpSegment *ws = ToWarp(*itWS);
|
||||
float fWarpSum = ws->GetLength() + ws->GetBeat();
|
||||
if( fWarpSum > fWarpDestination )
|
||||
{
|
||||
fWarpDestination = fWarpSum;
|
||||
}
|
||||
itWS ++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
iLastRow = iEventRow;
|
||||
start= looked_up_start->second;
|
||||
}
|
||||
|
||||
// won't reach here, unless BeatToNoteRow(fBeat == INT_MAX) (impossible)
|
||||
|
||||
GetElapsedTimeInternal(start, fBeat, INT_MAX);
|
||||
return start.last_time;
|
||||
}
|
||||
|
||||
float TimingData::GetDisplayedBeat( float fBeat ) const
|
||||
|
||||
+72
-14
@@ -71,6 +71,62 @@ public:
|
||||
TimingData( const TimingData &cpy ) { Copy(cpy); }
|
||||
TimingData& operator=( const TimingData &cpy ) { Copy(cpy); return *this; }
|
||||
|
||||
// GetBeatArgs, GetBeatStarts, m_beat_start_lookup, m_time_start_lookup,
|
||||
// PrepareLookup, and ReleaseLookup form a system for speeding up finding
|
||||
// the current beat and bps from the time, or finding the time from the
|
||||
// current beat.
|
||||
// The lookup tables contain indices for the beat and time finding
|
||||
// functions to start at so they don't have to walk through all the timing
|
||||
// segments.
|
||||
// PrepareLookup should be called before gameplay starts, so that the lookup
|
||||
// tables are populated. ReleaseLookup should be called after gameplay
|
||||
// finishes so that memory isn't wasted.
|
||||
// -Kyz
|
||||
struct GetBeatArgs
|
||||
{
|
||||
float elapsed_time;
|
||||
float beat;
|
||||
float bps_out;
|
||||
float warp_dest_out;
|
||||
int warp_begin_out;
|
||||
bool freeze_out;
|
||||
bool delay_out;
|
||||
GetBeatArgs() :elapsed_time(0), beat(0), bps_out(0), warp_dest_out(0),
|
||||
warp_begin_out(-1), freeze_out(false), delay_out(false) {}
|
||||
};
|
||||
struct GetBeatStarts
|
||||
{
|
||||
unsigned int bpm;
|
||||
unsigned int warp;
|
||||
unsigned int stop;
|
||||
unsigned int delay;
|
||||
int last_row;
|
||||
float last_time;
|
||||
float warp_destination;
|
||||
bool is_warping;
|
||||
GetBeatStarts() :bpm(0), warp(0), stop(0), delay(0), last_row(0),
|
||||
last_time(0), warp_destination(0), is_warping(false) {}
|
||||
};
|
||||
// map can't be used for the lookup table because its find or *_bound
|
||||
// functions would return the wrong entry.
|
||||
// In a map<int, int> with three entries, [-1]= 3, [6]= 1, [8]= 2,
|
||||
// lower_bound(0) and upper_bound(0) both returned the entry at [6]= 1.
|
||||
// So the lookup table is a vector of entries and FindEntryInLookup does a
|
||||
// binary search.
|
||||
// -Kyz
|
||||
struct lookup_item_t
|
||||
{
|
||||
float first;
|
||||
GetBeatStarts second;
|
||||
lookup_item_t(float f, GetBeatStarts& s) :first(f), second(s) {}
|
||||
};
|
||||
typedef vector<lookup_item_t> beat_start_lookup_t;
|
||||
beat_start_lookup_t m_beat_start_lookup;
|
||||
beat_start_lookup_t m_time_start_lookup;
|
||||
|
||||
void PrepareLookup();
|
||||
void ReleaseLookup();
|
||||
|
||||
int GetSegmentIndexAtRow(TimingSegmentType tst, int row) const;
|
||||
int GetSegmentIndexAtBeat(TimingSegmentType tst, float beat) const
|
||||
{
|
||||
@@ -290,25 +346,27 @@ public:
|
||||
|
||||
void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const;
|
||||
|
||||
void GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const;
|
||||
float GetBeatFromElapsedTime( float fElapsedTime ) const // shortcut for places that care only about the beat
|
||||
void GetBeatInternal(GetBeatStarts& start, GetBeatArgs& args,
|
||||
unsigned int max_segment) const;
|
||||
float GetElapsedTimeInternal(GetBeatStarts& start, float beat,
|
||||
unsigned int max_segment) const;
|
||||
void GetBeatAndBPSFromElapsedTime(GetBeatArgs& args) const;
|
||||
float GetBeatFromElapsedTime(float elapsed_time) const // shortcut for places that care only about the beat
|
||||
{
|
||||
float fBeat, fThrowAway, fThrowAway2;
|
||||
bool bThrowAway, bThrowAway2;
|
||||
int iThrowAway;
|
||||
GetBeatAndBPSFromElapsedTime( fElapsedTime, fBeat, fThrowAway, bThrowAway, bThrowAway2, iThrowAway, fThrowAway2 );
|
||||
return fBeat;
|
||||
GetBeatArgs args;
|
||||
args.elapsed_time= elapsed_time;
|
||||
GetBeatAndBPSFromElapsedTime(args);
|
||||
return args.beat;
|
||||
}
|
||||
float GetElapsedTimeFromBeat( float fBeat ) const;
|
||||
|
||||
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const;
|
||||
float GetBeatFromElapsedTimeNoOffset( float fElapsedTime ) const // shortcut for places that care only about the beat
|
||||
void GetBeatAndBPSFromElapsedTimeNoOffset(GetBeatArgs& args) const;
|
||||
float GetBeatFromElapsedTimeNoOffset(float elapsed_time) const // shortcut for places that care only about the beat
|
||||
{
|
||||
float fBeat, fThrowAway, fThrowAway2;
|
||||
bool bThrowAway, bThrowAway2;
|
||||
int iThrowAway;
|
||||
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeat, fThrowAway, bThrowAway, bThrowAway2, iThrowAway, fThrowAway2 );
|
||||
return fBeat;
|
||||
GetBeatArgs args;
|
||||
args.elapsed_time= elapsed_time;
|
||||
GetBeatAndBPSFromElapsedTimeNoOffset(args);
|
||||
return args.beat;
|
||||
}
|
||||
float GetElapsedTimeFromBeatNoOffset( float fBeat ) const;
|
||||
float GetDisplayedBeat( float fBeat ) const;
|
||||
|
||||
Reference in New Issue
Block a user