diff --git a/src/Background.cpp b/src/Background.cpp index d75abfbd49..e31d273708 100644 --- a/src/Background.cpp +++ b/src/Background.cpp @@ -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 diff --git a/src/Player.cpp b/src/Player.cpp index 08e5cff5b3..8c5582b1a9 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -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++; } } diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index e66e053800..44cdebc0cc 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -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 diff --git a/src/ScreenSyncOverlay.cpp b/src/ScreenSyncOverlay.cpp index 8c87f6bb0f..5299123ec8 100644 --- a/src/ScreenSyncOverlay.cpp +++ b/src/ScreenSyncOverlay.cpp @@ -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: diff --git a/src/SongPosition.cpp b/src/SongPosition.cpp index feb9df1a6f..2668d0043a 100644 --- a/src/SongPosition.cpp +++ b/src/SongPosition.cpp @@ -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() diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 55abde30f2..6906ff58ae 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -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* segs= m_avpTimingSegments; + const vector& bpms= segs[SEGMENT_BPM]; + const vector& warps= segs[SEGMENT_WARP]; + const vector& stops= segs[SEGMENT_STOP]; + const vector& 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& bpms, const vector& warps, + const vector& stops, const vector& delays) { - const vector * segs = m_avpTimingSegments; - vector::const_iterator itBPMS = segs[SEGMENT_BPM].begin(); - vector::const_iterator itWS = segs[SEGMENT_WARP].begin(); - vector::const_iterator itSS = segs[SEGMENT_STOP].begin(); - vector::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* segs= m_avpTimingSegments; + const vector& bpms= segs[SEGMENT_BPM]; + const vector& warps= segs[SEGMENT_WARP]; + const vector& stops= segs[SEGMENT_STOP]; + const vector& 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* segs= m_avpTimingSegments; + const vector& bpms= segs[SEGMENT_BPM]; + const vector& warps= segs[SEGMENT_WARP]; + const vector& stops= segs[SEGMENT_STOP]; + const vector& 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 * segs = m_avpTimingSegments; - vector::const_iterator itBPMS = segs[SEGMENT_BPM].begin(); - vector::const_iterator itWS = segs[SEGMENT_WARP].begin(); - vector::const_iterator itSS = segs[SEGMENT_STOP].begin(); - vector::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 diff --git a/src/TimingData.h b/src/TimingData.h index 7fd1acdcd1..f5996c7126 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -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 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 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;