diff --git a/src/AdjustSync.cpp b/src/AdjustSync.cpp index b6d16181d8..2422804a6f 100644 --- a/src/AdjustSync.cpp +++ b/src/AdjustSync.cpp @@ -267,28 +267,29 @@ void AdjustSync::AutosyncTempo() GAMESTATE->m_pCurSong->m_SongTiming.m_fBeat0OffsetInSeconds += fIntercept; const float fScaleBPM = 1.0f/(1.0f - fSlope); TimingData &timing = GAMESTATE->m_pCurSong->m_SongTiming; - vector &bpms = timing.m_avpTimingSegments[SEGMENT_BPM]; + + const vector &bpms = timing.GetTimingSegments(SEGMENT_BPM); for (unsigned i = 0; i < bpms.size(); i++) { - BPMSegment *b = static_cast(bpms[i]); - b->SetBPM(b->GetBPM() * fScaleBPM); + const BPMSegment *b = ToBPM( bpms[i] ); + timing.AddSegment( BPMSegment(b->GetRow(), b->GetBPM() * fScaleBPM) ); } /* We assume that the stops were measured as a number of beats. * Therefore, if we change the bpms, we need to make a similar * change to the stops. */ - vector &stops = timing.m_avpTimingSegments[SEGMENT_STOP]; + const vector &stops = timing.GetTimingSegments(SEGMENT_STOP); for (unsigned i = 0; i < stops.size(); i++) { - StopSegment *s = static_cast(stops[i]); - s->SetPause(s->GetPause() * (1.0f - fSlope)); + const StopSegment *s = ToStop( stops[i] ); + timing.AddSegment( StopSegment(s->GetRow(), s->GetPause() * (1.0f - fSlope)) ); } // Do the same for delays. - vector &delays = timing.m_avpTimingSegments[SEGMENT_DELAY]; + const vector &delays = timing.GetTimingSegments(SEGMENT_DELAY); for (unsigned i = 0; i < delays.size(); i++) { - DelaySegment *s = static_cast(delays[i]); - s->SetPause(s->GetPause() * (1.0f - fSlope)); + const DelaySegment *s = ToDelay( delays[i] ); + timing.AddSegment( DelaySegment(s->GetRow(), s->GetPause() * (1.0f - fSlope)) ); } SCREENMAN->SystemMessage( AUTOSYNC_CORRECTION_APPLIED.GetValue() ); @@ -336,12 +337,13 @@ void AdjustSync::GetSyncChangeTextGlobal( vector &vsAddTo ) } } +// XXX: needs cleanup still -- vyhd void AdjustSync::GetSyncChangeTextSong( vector &vsAddTo ) { if( GAMESTATE->m_pCurSong.Get() ) { unsigned int iOriginalSize = vsAddTo.size(); - TimingData original = s_vpTimingDataOriginal[0]; + TimingData &original = s_vpTimingDataOriginal[0]; TimingData &testing = GAMESTATE->m_pCurSong->m_SongTiming; { @@ -359,79 +361,74 @@ void AdjustSync::GetSyncChangeTextSong( vector &vsAddTo ) } } - vector &bpmTest = testing.m_avpTimingSegments[SEGMENT_BPM]; - vector &bpmOrig = original.m_avpTimingSegments[SEGMENT_BPM]; + const vector &bpmTest = testing.GetTimingSegments(SEGMENT_BPM); + const vector &bpmOrig = original.GetTimingSegments(SEGMENT_BPM); for( unsigned i=0; i< bpmTest.size(); i++ ) { - BPMSegment *bT = static_cast(bpmTest[i]); - BPMSegment *bO = static_cast(bpmOrig[i]); - float fOld = Quantize( bO->GetBPM(), 0.001f ); - float fNew = Quantize( bT->GetBPM(), 0.001f ); - float fDelta = fNew - fOld; + float fNew = Quantize( ToBPM(bpmTest[i])->GetBPM(), 0.001f ); + float fOld = Quantize( ToBPM(bpmOrig[i])->GetBPM(), 0.001f ); - if( fabsf(fDelta) > 0.0001f ) + if( fabsf(fNew - fOld) < 1e-4 ) + continue; + + if ( i >= 4 ) { - if ( i >= 4 ) - { - vsAddTo.push_back(ETC.GetValue()); - break; - } - vsAddTo.push_back( ssprintf( - TEMPO_SEGMENT_FROM.GetValue(), - FormatNumberAndSuffix(i+1).c_str(), - fOld, - fNew ) ); + vsAddTo.push_back(ETC.GetValue()); + break; } + + RString s = ssprintf( TEMPO_SEGMENT_FROM.GetValue(), + FormatNumberAndSuffix(i+1).c_str(), fOld, fNew ); + + vsAddTo.push_back( s ); } - vector &stopTest = testing.m_avpTimingSegments[SEGMENT_STOP]; - vector &stopOrig = original.m_avpTimingSegments[SEGMENT_STOP]; + const vector &stopTest = testing.GetTimingSegments(SEGMENT_STOP); + const vector &stopOrig = original.GetTimingSegments(SEGMENT_STOP); + for( unsigned i=0; i< stopTest.size(); i++ ) { - StopSegment *sT = static_cast(stopTest[i]); - StopSegment *sO = static_cast(stopOrig[i]); - float fOld = Quantize( sO->GetPause(), 0.001f ); - float fNew = Quantize( sT->GetPause(), 0.001f ); + float fOld = Quantize( ToStop(stopOrig[i])->GetPause(), 0.001f ); + float fNew = Quantize( ToStop(stopTest[i])->GetPause(), 0.001f ); float fDelta = fNew - fOld; - if( fabsf(fDelta) > 0.0001f ) + if( fabsf(fDelta) < 1e-4 ) + continue; + + if ( i >= 4 ) { - if ( i >= 4 ) - { - vsAddTo.push_back(ETC.GetValue()); - break; - } - vsAddTo.push_back( ssprintf( - CHANGED_STOP.GetValue(), - i+1, - fOld, - fNew ) ); + vsAddTo.push_back(ETC.GetValue()); + break; } + + RString s = ssprintf( CHANGED_STOP.GetValue(), i+1, fOld, fNew, fDelta ); + vsAddTo.push_back( s ); } - - vector &delyTest = testing.m_avpTimingSegments[SEGMENT_DELAY]; - vector &delyOrig = original.m_avpTimingSegments[SEGMENT_DELAY]; + + const vector &delyTest = testing.GetTimingSegments(SEGMENT_DELAY); + const vector &delyOrig = original.GetTimingSegments(SEGMENT_DELAY); + for( unsigned i=0; i< delyTest.size(); i++ ) { - DelaySegment *sT = static_cast(delyTest[i]); - DelaySegment *sO = static_cast(delyOrig[i]); - float fOld = Quantize( sO->GetPause(), 0.001f ); - float fNew = Quantize( sT->GetPause(), 0.001f ); + if( delyTest[i] == delyOrig[i] ) + continue; + + float fOld = Quantize( ToDelay(delyOrig[i])->GetPause(), 0.001f ); + float fNew = Quantize( ToDelay(delyTest[i])->GetPause(), 0.001f ); float fDelta = fNew - fOld; - - if( fabsf(fDelta) > 0.0001f ) + + if( fabsf(fDelta) < 1e-4 ) + continue; + + if ( i >= 4 ) { - if ( i >= 4 ) - { - vsAddTo.push_back(ETC.GetValue()); - break; - } - vsAddTo.push_back( ssprintf( - CHANGED_STOP.GetValue(), - i+1, - fOld, - fNew ) ); + vsAddTo.push_back(ETC.GetValue()); + break; } + + RString s = ssprintf( CHANGED_STOP.GetValue(), + i+1, fOld, fNew, fDelta ); + vsAddTo.push_back( s ); } if( vsAddTo.size() > iOriginalSize && s_fAverageError > 0.0f ) diff --git a/src/Background.cpp b/src/Background.cpp index 38214e5e50..1f5ff0b040 100644 --- a/src/Background.cpp +++ b/src/Background.cpp @@ -421,8 +421,8 @@ void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const Bac const TimingData &timing = m_pSong->m_SongTiming; // change BG every time signature change or 4 measures - const vector &tSigs = timing.m_avpTimingSegments[SEGMENT_TIME_SIG]; - + const vector &tSigs = timing.GetTimingSegments(SEGMENT_TIME_SIG); + for (unsigned i = 0; i < tSigs.size(); i++) { TimeSignatureSegment *ts = static_cast(tSigs[i]); @@ -448,7 +448,7 @@ void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const Bac } // change BG every BPM change that is at the beginning of a measure - const vector &bpms = timing.m_avpTimingSegments[SEGMENT_BPM]; + const vector &bpms = timing.GetTimingSegments(SEGMENT_BPM); for( unsigned i=0; iGetDisplayedTiming(); - const vector *segs = pTiming->m_avpTimingSegments; + const vector* segs[NUM_TimingSegmentType]; + + FOREACH_TimingSegmentType( tst ) + segs[tst] = &(pTiming->GetTimingSegments(tst)); + unsigned i = 0; // Draw beat bars if( ( GAMESTATE->IsEditing() || SHOW_BEAT_BARS ) && pTiming != NULL ) { - const vector &tSigs = segs[SEGMENT_TIME_SIG]; + const vector &tSigs = *segs[SEGMENT_TIME_SIG]; int iMeasureIndex = 0; for (i = 0; i < tSigs.size(); i++) { - TimeSignatureSegment *ts = static_cast(tSigs[i]); + const TimeSignatureSegment *ts = ToTimeSignature(tSigs[i]); int iSegmentEndRow = (i + 1 == tSigs.size()) ? iLastRowToDraw : tSigs[i+1]->GetRow(); // beat bars every 16th note @@ -874,9 +878,9 @@ void NoteField::DrawPrimitives() // Scroll text if( GAMESTATE->m_bIsUsingStepTiming ) { - for (i = 0; i < segs[SEGMENT_SCROLL].size(); i++) + for (i = 0; i < segs[SEGMENT_SCROLL]->size(); i++) { - ScrollSegment *seg = static_cast(segs[SEGMENT_SCROLL][i]); + ScrollSegment *seg = ToScroll( segs[SEGMENT_SCROLL]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -887,9 +891,9 @@ void NoteField::DrawPrimitives() } // BPM text - for (i = 0; i < segs[SEGMENT_BPM].size(); i++) + for (i = 0; i < segs[SEGMENT_BPM]->size(); i++) { - BPMSegment *seg = static_cast(segs[SEGMENT_BPM][i]); + const BPMSegment *seg = ToBPM( segs[SEGMENT_BPM]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -899,9 +903,9 @@ void NoteField::DrawPrimitives() } // Freeze text - for (i = 0; i < segs[SEGMENT_STOP].size(); i++) + for (i = 0; i < segs[SEGMENT_STOP]->size(); i++) { - StopSegment *seg = static_cast(segs[SEGMENT_STOP][i]); + const StopSegment *seg = ToStop( segs[SEGMENT_STOP]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -911,9 +915,9 @@ void NoteField::DrawPrimitives() } // Delay text - for (i = 0; i < segs[SEGMENT_DELAY].size(); i++) + for (i = 0; i < segs[SEGMENT_DELAY]->size(); i++) { - DelaySegment *seg = static_cast(segs[SEGMENT_DELAY][i]); + const DelaySegment *seg = ToDelay( segs[SEGMENT_DELAY]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -923,9 +927,9 @@ void NoteField::DrawPrimitives() } // Warp text - for (i = 0; i < segs[SEGMENT_WARP].size(); i++) + for (i = 0; i < segs[SEGMENT_WARP]->size(); i++) { - WarpSegment *seg = static_cast(segs[SEGMENT_WARP][i]); + const WarpSegment *seg = ToWarp( segs[SEGMENT_WARP]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -935,9 +939,9 @@ void NoteField::DrawPrimitives() } // Time Signature text - for (i = 0; i < segs[SEGMENT_TIME_SIG].size(); i++) + for (i = 0; i < segs[SEGMENT_TIME_SIG]->size(); i++) { - TimeSignatureSegment *seg = static_cast(segs[SEGMENT_TIME_SIG][i]); + const TimeSignatureSegment *seg = ToTimeSignature( segs[SEGMENT_TIME_SIG]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -947,9 +951,9 @@ void NoteField::DrawPrimitives() } // Tickcount text - for (i = 0; i < segs[SEGMENT_TICKCOUNT].size(); i++) + for (i = 0; i < segs[SEGMENT_TICKCOUNT]->size(); i++) { - TickcountSegment *seg = static_cast(segs[SEGMENT_TICKCOUNT][i]); + const TickcountSegment *seg = ToTickcount( segs[SEGMENT_TICKCOUNT]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -957,11 +961,11 @@ void NoteField::DrawPrimitives() DrawTickcountText( fBeat, seg->GetTicks() ); } } - + // Combo text - for (i = 0; i < segs[SEGMENT_COMBO].size(); i++) + for (i = 0; i < segs[SEGMENT_COMBO]->size(); i++) { - ComboSegment *seg = static_cast(segs[SEGMENT_COMBO][i]); + const ComboSegment *seg = ToCombo( segs[SEGMENT_COMBO]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -971,9 +975,9 @@ void NoteField::DrawPrimitives() } // Label text - for (i = 0; i < segs[SEGMENT_LABEL].size(); i++) + for (i = 0; i < segs[SEGMENT_LABEL]->size(); i++) { - LabelSegment *seg = static_cast(segs[SEGMENT_LABEL][i]); + const LabelSegment *seg = ToLabel( segs[SEGMENT_LABEL]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -983,9 +987,9 @@ void NoteField::DrawPrimitives() } // Speed text - for (i = 0; i < segs[SEGMENT_SPEED].size(); i++) + for (i = 0; i < segs[SEGMENT_SPEED]->size(); i++) { - SpeedSegment *seg = static_cast(segs[SEGMENT_SPEED][i]); + const SpeedSegment *seg = ToSpeed( segs[SEGMENT_SPEED]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -994,11 +998,11 @@ void NoteField::DrawPrimitives() seg->GetDelay(), seg->GetUnit() ); } } - + // Fake text - for (i = 0; i < segs[SEGMENT_FAKE].size(); i++) + for (i = 0; i < segs[SEGMENT_FAKE]->size(); i++) { - FakeSegment *seg = static_cast(segs[SEGMENT_FAKE][i]); + const FakeSegment *seg = ToFake( segs[SEGMENT_FAKE]->at(i) ); if( seg->GetRow() >= iFirstRowToDraw && seg->GetRow() <= iLastRowToDraw ) { float fBeat = seg->GetBeat(); @@ -1088,7 +1092,7 @@ void NoteField::DrawPrimitives() ASSERT( iter[j] == GAMESTATE->m_pCurSong->GetBackgroundChanges(j).end() ); break; } - + if( IS_ON_SCREEN(fLowestBeat) ) { vector vsBGChanges; diff --git a/src/NotesLoaderDWI.cpp b/src/NotesLoaderDWI.cpp index c0ca9e55d7..ea6aa58e28 100644 --- a/src/NotesLoaderDWI.cpp +++ b/src/NotesLoaderDWI.cpp @@ -585,24 +585,20 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla else if( sValueName.EqualsNoCase("BPM") ) { const float fBPM = StringToFloat( sParams[1] ); - - if( PREFSMAN->m_bQuirksMode ) + + if( unlikely(fBPM <= 0.0f && !PREFSMAN->m_bQuirksMode) ) { - out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, fBPM) ); + LOG->UserLog("Song file", sPath, "has an invalid BPM change at beat %f, BPM %f.", + 0.0f, fBPM ); } - else{ - if( fBPM > 0.0f ) - out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, fBPM) ); - else - LOG->UserLog("Song file", - sPath, - "has an invalid BPM change at beat %f, BPM %f.", - 0.0f, fBPM ); + else + { + out.m_SongTiming.AddSegment( BPMSegment(0, fBPM) ); } } else if( sValueName.EqualsNoCase("DISPLAYBPM") ) { - // #DISPLAYBPM:[xxx..xxx]|[xxx]|[*]; + // #DISPLAYBPM:[xxx..xxx]|[xxx]|[*]; int iMin, iMax; /* We can't parse this as a float with sscanf, since '.' is a valid * character in a float. (We could do it with a regex, but it's not @@ -626,7 +622,7 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla else if( sValueName.EqualsNoCase("GAP") ) // the units of GAP is 1/1000 second - out.m_SongTiming.m_fBeat0OffsetInSeconds = -StringToInt( sParams[1] ) / 1000.0f; + out.m_SongTiming.m_fBeat0OffsetInSeconds = -StringToInt(sParams[1]) / 1000.0f; else if( sValueName.EqualsNoCase("SAMPLESTART") ) out.m_fMusicSampleStartSeconds = ParseBrokenDWITimestamp(sParams[1], sParams[2], sParams[3]); @@ -650,8 +646,8 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla } int iFreezeRow = BeatToNoteRow( StringToFloat(arrayFreezeValues[0]) / 4.0f ); float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ) / 1000.0f; - - out.m_SongTiming.AddSegment( SEGMENT_STOP, new StopSegment(iFreezeRow, fFreezeSeconds) ); + + out.m_SongTiming.AddSegment( StopSegment(iFreezeRow, fFreezeSeconds) ); // LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", fFreezeBeat, fFreezeSeconds ); } } @@ -670,19 +666,14 @@ bool DWILoader::LoadFromDir( const RString &sPath_, Song &out, set &Bla LOG->UserLog( "Song file", sPath, "has an invalid CHANGEBPM: '%s'.", arrayBPMChangeExpressions[b].c_str() ); continue; } - + int iStartIndex = BeatToNoteRow( StringToFloat(arrayBPMChangeValues[0]) / 4.0f ); float fBPM = StringToFloat( arrayBPMChangeValues[1] ); if( fBPM > 0.0f ) - { - BPMSegment * bs = new BPMSegment( iStartIndex, fBPM ); - out.m_SongTiming.AddSegment( SEGMENT_BPM, bs ); - } + out.m_SongTiming.AddSegment( BPMSegment(iStartIndex, fBPM) ); else - { LOG->UserLog( "Song file", sPath, "has an invalid BPM change at beat %f, BPM %f.", NoteRowToBeat(iStartIndex), fBPM ); - } } } diff --git a/src/NotesLoaderJson.cpp b/src/NotesLoaderJson.cpp index 445ec41ee0..1195ea413f 100644 --- a/src/NotesLoaderJson.cpp +++ b/src/NotesLoaderJson.cpp @@ -15,13 +15,9 @@ void NotesLoaderJson::GetApplicableFiles( const RString &sPath, vector GetDirListing( sPath + RString("*.json"), out ); } -static void Deserialize(TimingSegment &seg_, const Json::Value &root) +static void Deserialize( TimingSegment *seg, const Json::Value &root ) { - TimingSegment *seg = &seg_; - - float fBeat = root["Beat"].asDouble(); - seg->SetBeat(fBeat); - switch (seg->GetType()) + switch( seg->GetType() ) { case SEGMENT_BPM: { @@ -39,10 +35,33 @@ static void Deserialize(TimingSegment &seg_, const Json::Value &root) } } +static void Deserialize(BPMSegment &seg, const Json::Value &root) +{ + Deserialize( static_cast(&seg), root ); +} + +static void Deserialize(StopSegment &seg, const Json::Value &root) +{ + Deserialize( static_cast(&seg), root ); +} + static void Deserialize(TimingData &td, const Json::Value &root) { - JsonUtil::DeserializeVectorPointers( td.m_avpTimingSegments[SEGMENT_BPM], Deserialize, root["BpmSegments"] ); - JsonUtil::DeserializeVectorPointers( td.m_avpTimingSegments[SEGMENT_STOP], Deserialize, root["StopSegments"] ); + vector vBPMs; + vector vStops; + JsonUtil::DeserializeVectorPointers( vBPMs, Deserialize, root["BpmSegments"] ); + JsonUtil::DeserializeVectorPointers( vStops, Deserialize, root["StopSegments"] ); + + for( unsigned i = 0; i < vBPMs.size(); ++i ) + { + td.AddSegment( *vBPMs[i] ); + delete vBPMs[i]; + } + for( unsigned i = 0; i < vStops.size(); ++i ) + { + td.AddSegment( *vStops[i] ); + delete vStops[i]; + } } static void Deserialize(LyricSegment &o, const Json::Value &root) diff --git a/src/NotesLoaderKSF.cpp b/src/NotesLoaderKSF.cpp index 82832b5cb4..06a21e0000 100644 --- a/src/NotesLoaderKSF.cpp +++ b/src/NotesLoaderKSF.cpp @@ -17,7 +17,7 @@ static void HandleBunki( TimingData &timing, const float fEarlyBPM, const float beat = (fPos + fGap) * BeatsPerSecond; LOG->Trace( "BPM %f, BPS %f, BPMPos %f, beat %f", fEarlyBPM, BeatsPerSecond, fPos, beat ); - timing.AddSegment( SEGMENT_BPM, new BPMSegment(BeatToNoteRow(beat), fCurBPM) ); + timing.AddSegment( BPMSegment(BeatToNoteRow(beat), fCurBPM) ); } static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool bKIUCompliant ) @@ -59,7 +59,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool else if( sValueName=="BPM" ) { BPM1 = StringToFloat(sParams[1]); - stepsTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, BPM1) ); + stepsTiming.AddSegment( BPMSegment(0, BPM1) ); } else if( sValueName=="BPM2" ) { @@ -136,7 +136,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool LOG->UserLog( "Song file", sPath, "has an invalid tick count: %d.", iTickCount ); return false; } - stepsTiming.AddSegment( SEGMENT_TICKCOUNT, new TickcountSegment(0, iTickCount)); + stepsTiming.AddSegment( TickcountSegment(0, iTickCount)); } else if( sValueName=="DIFFICULTY" ) @@ -332,7 +332,7 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool // be 0. if the tickcount is non a stepmania standard then it will be adapted, a scroll // segment will then be added based on approximations. -DaisuMaster // eh better do it considering the tickcount (high tickcounts) - + // I'm making some experiments, please spare me... //continue; @@ -367,7 +367,8 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool else if (BeginsWith(sRowString, "|M") || BeginsWith(sRowString, "|C")) { // multipliers/combo - stepsTiming.SetHitComboAtBeat( fCurBeat, static_cast(numTemp) ); + ComboSegment seg( BeatToNoteRow(fCurBeat), int(numTemp) ); + stepsTiming.AddSegment( seg ); } else if (BeginsWith(sRowString, "|S")) { @@ -380,7 +381,8 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, Song &song, bool else if (BeginsWith(sRowString, "|X")) { // scroll segments - stepsTiming.SetScrollAtBeat( fCurBeat, numTemp ); + ScrollSegment seg = ScrollSegment( BeatToNoteRow(fCurBeat), numTemp ); + stepsTiming.AddSegment( seg ); //return true; } @@ -511,12 +513,12 @@ static void ProcessTickcounts( const RString & value, int & ticks, TimingData & * and stops. It will be called again in LoadFromKSFFile for the * actual steps. */ ticks = StringToInt( value ); - ticks = ticks > 0 ? ticks : 4; - // add a tickcount for those using the [Player] - // CheckpointsUseTimeSignatures metric. -aj - // It's not with timesigs now -DaisuMaster - TickcountSegment * tcs = new TickcountSegment(0, ticks > ROWS_PER_BEAT ? ROWS_PER_BEAT : ticks); - timing.AddSegment( SEGMENT_TICKCOUNT, tcs ); + CLAMP( ticks, 0, ROWS_PER_BEAT ); + + if( ticks == 0 ) + ticks = TickcountSegment::DEFAULT_TICK_COUNT; + + timing.AddSegment( TickcountSegment(0, ticks) ); } static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant ) @@ -557,7 +559,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant else if( sValueName=="BPM" ) { BPM1 = StringToFloat(sParams[1]); - out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, BPM1) ); + out.m_SongTiming.AddSegment( BPMSegment(0, BPM1) ); } else if( sValueName=="BPM2" ) { @@ -584,7 +586,7 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant SMGap1 = -StringToFloat( sParams[1] )/100; out.m_SongTiming.m_fBeat0OffsetInSeconds = SMGap1; } - // This is currently required for more accurate KIU BPM changes. + // This is currently required for more accurate KIU BPM changes. else if( sValueName=="STARTTIME2" ) { bKIUCompliant = true; diff --git a/src/NotesLoaderPMS.cpp b/src/NotesLoaderPMS.cpp index 1b74ea3808..58415b55c5 100644 --- a/src/NotesLoaderPMS.cpp +++ b/src/NotesLoaderPMS.cpp @@ -592,7 +592,7 @@ static void ReadGlobalTags( const RString &sPath, const NameToData_t &mapNameToD if( fBPM > 0.0f ) { - out.m_SongTiming.AddSegment( SEGMENT_BPM, new BPMSegment(0, fBPM) ); + out.m_SongTiming.AddSegment( BPMSegment(0, fBPM) ); LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM ); } else @@ -697,9 +697,8 @@ static void ReadGlobalTags( const RString &sPath, const NameToData_t &mapNameToD if( fBPM > 0.0f ) { - BPMSegment * newSeg = new BPMSegment( BeatToNoteRow(fBeat), fBPM ); - out.m_SongTiming.AddSegment( SEGMENT_BPM, newSeg ); - LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", fBeat, newSeg->GetBPM() ); + out.m_SongTiming.AddSegment( BPMSegment(BeatToNoteRow(fBeat), fBPM) ); + LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", fBeat, fBPM ); } else { @@ -724,9 +723,8 @@ static void ReadGlobalTags( const RString &sPath, const NameToData_t &mapNameToD float fBeats = StringToFloat( sBeats ) / 48.0f; float fFreezeSecs = fBeats / fBPS; - StopSegment * newSeg = new StopSegment( BeatToNoteRow(fBeat), fFreezeSecs ); - out.m_SongTiming.AddSegment( SEGMENT_STOP, newSeg ); - LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg->GetPause() ); + out.m_SongTiming.AddSegment( StopSegment(BeatToNoteRow(fBeat), fFreezeSecs) ); + LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, fFreezeSecs ); } else { @@ -753,12 +751,9 @@ static void ReadGlobalTags( const RString &sPath, const NameToData_t &mapNameToD if( fBPM > 0.0f ) { - BPMSegment * newSeg = new BPMSegment( iStepIndex, fBPM ); - out.m_SongTiming.AddSegment( SEGMENT_BPM, newSeg ); + out.m_SongTiming.AddSegment( BPMSegment(iStepIndex, fBPM) ); LOG->Trace("Inserting new BPM change at beat %f, BPM %f", - newSeg->GetBeat(), - newSeg->GetBPM() ); - + NoteRowToBeat(iStepIndex), fBPM ); } else { diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index 5f3ee7f742..dd16e698e1 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -227,12 +227,12 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString line, const int rowsP arrayBPMChangeExpressions[b].c_str() ); continue; } - + bNotEmpty = true; - + const float fBeat = RowToBeat( arrayBPMChangeValues[0], rowsPerBeat ); const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] ); - + if( fNewBPM < 0.0f ) { negBeat = fBeat; @@ -244,9 +244,8 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString line, const int rowsP if( negBPM < 0 ) { float endBeat = fBeat + (fNewBPM / -negBPM) * (fBeat - negBeat); - out.AddSegment(SEGMENT_WARP, - new WarpSegment(BeatToNoteRow(negBeat), endBeat - negBeat)); - + out.AddSegment( WarpSegment(BeatToNoteRow(negBeat), endBeat - negBeat) ); + negBeat = -1; negBPM = 1; } @@ -260,18 +259,16 @@ bool SMLoader::ProcessBPMs( TimingData &out, const RString line, const int rowsP // add in a warp. if( highspeedBeat > 0 ) { - out.AddSegment(SEGMENT_WARP, - new WarpSegment(BeatToNoteRow(highspeedBeat), fBeat - highspeedBeat) ); + out.AddSegment( WarpSegment(BeatToNoteRow(highspeedBeat), fBeat - highspeedBeat) ); highspeedBeat = -1; } { - out.AddSegment(SEGMENT_BPM, - new BPMSegment(BeatToNoteRow(fBeat), fNewBPM)); + out.AddSegment( BPMSegment(BeatToNoteRow(fBeat), fNewBPM) ); } } } } - + return bNotEmpty; } @@ -283,7 +280,7 @@ void SMLoader::ProcessStops( TimingData &out, const RString line, const int rows // Prepare variables for negative stop conversion. float negBeat = -1; float negPause = 0; - + for( unsigned f=0; f arrayFreezeValues; @@ -296,26 +293,26 @@ void SMLoader::ProcessStops( TimingData &out, const RString line, const int rows arrayFreezeExpressions[f].c_str() ); continue; } - + const float fFreezeBeat = RowToBeat( arrayFreezeValues[0], rowsPerBeat ); const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ); - + // Process the prior stop. if( negPause > 0 ) { - BPMSegment * oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat)); - float fSecondsPerBeat = 60 / oldBPM->GetBPM(); + float oldBPM = out.GetBPMAtRow(BeatToNoteRow(negBeat)); + float fSecondsPerBeat = 60 / oldBPM; float fSkipBeats = negPause / fSecondsPerBeat; - + if( negBeat + fSkipBeats > fFreezeBeat ) fSkipBeats = fFreezeBeat - negBeat; - - out.AddSegment(SEGMENT_WARP, new WarpSegment(BeatToNoteRow(negBeat), fSkipBeats)); - + + out.AddSegment( WarpSegment(BeatToNoteRow(negBeat), fSkipBeats)); + negBeat = -1; negPause = 0; } - + if( fFreezeSeconds < 0.0f ) { negBeat = fFreezeBeat; @@ -323,20 +320,18 @@ void SMLoader::ProcessStops( TimingData &out, const RString line, const int rows } else if( fFreezeSeconds > 0.0f ) { - out.AddSegment(SEGMENT_STOP, - new StopSegment(BeatToNoteRow(fFreezeBeat), fFreezeSeconds)); + out.AddSegment( StopSegment(BeatToNoteRow(fFreezeBeat), fFreezeSeconds) ); } - } - + // Process the prior stop if there was one. if( negPause > 0 ) { - BPMSegment * oldBPM = out.GetBPMSegmentAtBeat(negBeat); - float fSecondsPerBeat = 60 / oldBPM->GetBPM(); + float oldBPM = out.GetBPMAtBeat(negBeat); + float fSecondsPerBeat = 60 / oldBPM; float fSkipBeats = negPause / fSecondsPerBeat; - - out.AddSegment(SEGMENT_WARP, new WarpSegment(BeatToNoteRow(negBeat), fSkipBeats)); + + out.AddSegment( WarpSegment(BeatToNoteRow(negBeat), fSkipBeats) ); } } @@ -344,7 +339,7 @@ void SMLoader::ProcessDelays( TimingData &out, const RString line, const int row { vector arrayDelayExpressions; split( line, ",", arrayDelayExpressions ); - + for( unsigned f=0; f arrayDelayValues; @@ -357,17 +352,12 @@ void SMLoader::ProcessDelays( TimingData &out, const RString line, const int row arrayDelayExpressions[f].c_str() ); continue; } - const float fFreezeBeat = RowToBeat( arrayDelayValues[0], rowsPerBeat ); const float fFreezeSeconds = StringToFloat( arrayDelayValues[1] ); - - DelaySegment * new_seg = new DelaySegment(BeatToNoteRow(fFreezeBeat), - fFreezeSeconds); - // LOG->Trace( "Adding a delay segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds ); - + if(fFreezeSeconds > 0.0f) - out.AddSegment( SEGMENT_DELAY, new_seg ); + out.AddSegment( DelaySegment(BeatToNoteRow(fFreezeBeat), fFreezeSeconds) ); else LOG->UserLog( "Song file", @@ -397,11 +387,8 @@ void SMLoader::ProcessTimeSignatures( TimingData &out, const RString line, const } const float fBeat = RowToBeat( vs2[0], rowsPerBeat ); - - TimeSignatureSegment * seg = - new TimeSignatureSegment( BeatToNoteRow(fBeat), - StringToInt( vs2[1] ), - StringToInt( vs2[2] )); + const int iNumerator = StringToInt( vs2[1] ); + const int iDenominator = StringToInt( vs2[2] ); if( fBeat < 0 ) { @@ -411,26 +398,26 @@ void SMLoader::ProcessTimeSignatures( TimingData &out, const RString line, const fBeat ); continue; } - - if( seg->GetNum() < 1 ) + + if( iNumerator < 1 ) { LOG->UserLog("Song file", this->GetSongTitle(), "has an invalid time signature change with beat %f, iNumerator %i.", - fBeat, seg->GetNum() ); + fBeat, iNumerator ); continue; } - - if( seg->GetDen() < 1 ) + + if( iDenominator < 1 ) { LOG->UserLog("Song file", this->GetSongTitle(), "has an invalid time signature change with beat %f, iDenominator %i.", - fBeat, seg->GetDen() ); + fBeat, iDenominator ); continue; } - - out.AddSegment( SEGMENT_TIME_SIG, seg ); + + out.AddSegment( TimeSignatureSegment(BeatToNoteRow(fBeat), iNumerator, iDenominator) ); } } @@ -438,7 +425,7 @@ void SMLoader::ProcessTickcounts( TimingData &out, const RString line, const int { vector arrayTickcountExpressions; split( line, ",", arrayTickcountExpressions ); - + for( unsigned f=0; f arrayTickcountValues; @@ -451,12 +438,11 @@ void SMLoader::ProcessTickcounts( TimingData &out, const RString line, const int arrayTickcountExpressions[f].c_str() ); continue; } - + const float fTickcountBeat = RowToBeat( arrayTickcountValues[0], rowsPerBeat ); int iTicks = clamp(atoi( arrayTickcountValues[1] ), 0, ROWS_PER_BEAT); - - out.AddSegment( SEGMENT_TICKCOUNT, - new TickcountSegment(BeatToNoteRow(fTickcountBeat), iTicks) ); + + out.AddSegment( TickcountSegment(BeatToNoteRow(fTickcountBeat), iTicks) ); } } @@ -464,22 +450,22 @@ void SMLoader::ProcessSpeeds( TimingData &out, const RString line, const int row { vector vs1; split( line, ",", vs1 ); - + FOREACH_CONST( RString, vs1, s1 ) { vector vs2; split( *s1, "=", vs2 ); - + if( vs2[0] == 0 && vs2.size() == 2 ) // First one always seems to have 2. { vs2.push_back("0"); } - + if( vs2.size() == 3 ) // use beats by default. { vs2.push_back("0"); } - + if( vs2.size() < 4 ) { LOG->UserLog("Song file", @@ -488,39 +474,35 @@ void SMLoader::ProcessSpeeds( TimingData &out, const RString line, const int row static_cast(vs2.size()) ); continue; } - + const float fBeat = RowToBeat( vs2[0], rowsPerBeat ); - - SpeedSegment * seg = new SpeedSegment( BeatToNoteRow(fBeat), - StringToFloat( vs2[1] ), - StringToFloat( vs2[2] )); + const float fRatio = StringToFloat( vs2[1] ); + const float fDelay = StringToFloat( vs2[2] ); // XXX: ugly... int iUnit = StringToInt(vs2[3]); SpeedSegment::BaseUnit unit = (iUnit == 0) ? SpeedSegment::UNIT_BEATS : SpeedSegment::UNIT_SECONDS; - seg->SetUnit( unit ); - if( fBeat < 0 ) { LOG->UserLog("Song file", this->GetSongTitle(), - "has an speed change with beat %f.", + "has an speed change with beat %f.", fBeat ); continue; } - - if( seg->GetDelay() < 0 ) + + if( fDelay < 0 ) { LOG->UserLog("Song file", this->GetSongTitle(), "has an speed change with beat %f, length %f.", - fBeat, seg->GetDelay() ); + fBeat, fDelay ); continue; } - - out.AddSegment( SEGMENT_SPEED, seg ); + + out.AddSegment( SpeedSegment(BeatToNoteRow(fBeat), fRatio, fDelay, unit) ); } } @@ -528,7 +510,7 @@ void SMLoader::ProcessFakes( TimingData &out, const RString line, const int rows { vector arrayFakeExpressions; split( line, ",", arrayFakeExpressions ); - + for( unsigned b=0; b arrayFakeValues; @@ -541,12 +523,12 @@ void SMLoader::ProcessFakes( TimingData &out, const RString line, const int rows arrayFakeExpressions[b].c_str() ); continue; } - + const float fBeat = RowToBeat( arrayFakeValues[0], rowsPerBeat ); const float fNewBeat = StringToFloat( arrayFakeValues[1] ); - + if(fNewBeat > 0) - out.AddSegment( SEGMENT_FAKE, new FakeSegment(BeatToNoteRow(fBeat), fNewBeat) ); + out.AddSegment( FakeSegment(BeatToNoteRow(fBeat), fNewBeat) ); else { LOG->UserLog("Song file", @@ -777,28 +759,27 @@ bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache { ProcessBPMs(out.m_SongTiming, sParams[1]); } - + else if( sValueName=="STOPS" || sValueName=="FREEZES" ) { ProcessStops(out.m_SongTiming, sParams[1]); } - + else if( sValueName=="DELAYS" ) { ProcessDelays(out.m_SongTiming, sParams[1]); } - + else if( sValueName=="TIMESIGNATURES" ) { ProcessTimeSignatures(out.m_SongTiming, sParams[1]); } - + else if( sValueName=="TICKCOUNTS" ) { ProcessTickcounts(out.m_SongTiming, sParams[1]); } - - + else if( sValueName=="INSTRUMENTTRACK" ) { ProcessInstrumentTracks( out, sParams[1] ); @@ -931,10 +912,9 @@ bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache else LOG->UserLog( "Song file", sPath, "has an unexpected value named \"%s\".", sValueName.c_str() ); } - + // Ensure all warps from negative time changes are in order. - vector &warps = out.m_SongTiming.m_avpTimingSegments[SEGMENT_WARP]; - sort(warps.begin(), warps.end()); + out.m_SongTiming.SortSegments( SEGMENT_WARP ); TidyUpData( out, bFromCache ); return true; } @@ -1105,7 +1085,6 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache ) if( !IsAFile( song.GetBackgroundPath() ) ) break; - bg.push_back( BackgroundChange(lastBeat,song.m_sBackgroundFile) ); } while(0); } diff --git a/src/NotesLoaderSMA.cpp b/src/NotesLoaderSMA.cpp index 4da2310520..01318e7f19 100644 --- a/src/NotesLoaderSMA.cpp +++ b/src/NotesLoaderSMA.cpp @@ -38,8 +38,7 @@ void SMALoader::ProcessMultipliers( TimingData &out, const int iRowsPerBeat, con const int iMisses = (size == 2 || size == 4 ? iCombos : StringToInt(arrayMultiplierValues[2])); - out.AddSegment(SEGMENT_COMBO, - new ComboSegment( BeatToNoteRow(fComboBeat), iCombos, iMisses )); + out.AddSegment( ComboSegment(BeatToNoteRow(fComboBeat), iCombos, iMisses) ); } } @@ -62,31 +61,26 @@ void SMALoader::ProcessBeatsPerMeasure( TimingData &out, const RString sParam ) continue; } const float fBeat = StringToFloat( vs2[0] ); - - TimeSignatureSegment * seg = new TimeSignatureSegment( - BeatToNoteRow(fBeat), - StringToInt(vs2[1]), - 4 ); + const int iNumerator = StringToInt( vs2[1] ); if( fBeat < 0 ) { LOG->UserLog("Song file", - this->GetSongTitle(), + this->GetSongTitle(), "has an invalid time signature change with beat %f.", fBeat ); continue; } - - if( seg->GetNum() < 1 ) + if( iNumerator < 1 ) { LOG->UserLog("Song file", this->GetSongTitle(), "has an invalid time signature change with beat %f, iNumerator %i.", - fBeat, seg->GetNum() ); + fBeat, iNumerator ); continue; } - - out.AddSegment( SEGMENT_TIME_SIG, seg ); + + out.AddSegment( TimeSignatureSegment(BeatToNoteRow(fBeat), iNumerator) ); } } @@ -116,19 +110,19 @@ void SMALoader::ProcessSpeeds( TimingData &out, const RString line, const int ro static_cast(vs2.size()) ); continue; } - + const float fBeat = RowToBeat( vs2[0], rowsPerBeat ); - + RString backup = vs2[2]; Trim(vs2[2], "s"); Trim(vs2[2], "S"); - + const float fRatio = StringToFloat( vs2[1] ); + const float fDelay = StringToFloat( vs2[2] ); + SpeedSegment::BaseUnit unit = ((backup != vs2[2]) ? SpeedSegment::UNIT_SECONDS : SpeedSegment::UNIT_BEATS); - SpeedSegment * seg = new SpeedSegment( BeatToNoteRow(fBeat), - StringToFloat( vs2[1] ), StringToFloat(vs2[2]), unit); if( fBeat < 0 ) { @@ -138,17 +132,17 @@ void SMALoader::ProcessSpeeds( TimingData &out, const RString line, const int ro fBeat ); continue; } - - if( seg->GetDelay() < 0 ) + + if( fDelay < 0 ) { LOG->UserLog("Song file", this->GetSongTitle(), "has an speed change with beat %f, length %f.", - fBeat, seg->GetDelay() ); + fBeat, fDelay ); continue; } - - out.AddSegment( SEGMENT_SPEED, seg ); + + out.AddSegment( SpeedSegment(BeatToNoteRow(fBeat), fRatio, fDelay, unit) ); } } @@ -348,7 +342,7 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach { vector aFGChangeExpressions; split( sParams[1], ",", aFGChangeExpressions ); - + for( unsigned b=0; bm_Timing : out.m_SongTiming); timing.m_fBeat0OffsetInSeconds = StringToFloat( sParams[1] ); } - + else if( sValueName=="BPMS" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); ProcessBPMs( timing, sParams[1], iRowsPerBeat ); } - + else if( sValueName=="STOPS" || sValueName=="FREEZES" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); ProcessStops( timing, sParams[1], iRowsPerBeat ); } - + else if( sValueName=="DELAYS" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); ProcessDelays( timing, sParams[1], iRowsPerBeat ); } - + else if( sValueName=="TICKCOUNT" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); ProcessTickcounts( timing, sParams[1], iRowsPerBeat ); } - + else if( sValueName=="SPEED" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); RString tmp = sParams[1]; Trim( tmp ); ProcessSpeeds( timing, tmp, iRowsPerBeat ); } - + else if( sValueName=="MULTIPLIER" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); ProcessMultipliers( timing, iRowsPerBeat, sParams[1] ); } - + else if( sValueName=="FAKES" ) { - TimingData &timing = (state == SMA_GETTING_STEP_INFO + TimingData &timing = (state == SMA_GETTING_STEP_INFO ? pNewNotes->m_Timing : out.m_SongTiming); ProcessFakes( timing, sParams[1], iRowsPerBeat ); } - + else if( sValueName=="METERTYPE" ) { ; // We don't use this...yet. } - + else if( sValueName=="KEYSOUNDS" ) { split( sParams[1], ",", out.m_vsKeysoundFile ); } - + // Attacks loaded from file else if( sValueName=="ATTACKS" ) { ProcessAttackString(out.m_sAttackString, sParams); ProcessAttacks(out.m_Attacks, sParams); } - + else if( sValueName=="NOTES" || sValueName=="NOTES2" ) { if( iNumParams < 7 ) @@ -442,7 +436,7 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach iNumParams ); continue; } - + LoadFromTokens( sParams[1], sParams[2], diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 80e64345e0..eb8d76ca8f 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -37,10 +37,10 @@ void SSCLoader::ProcessWarps( TimingData &out, const RString sParam, const float // Early versions were absolute in beats. They should be relative. if( ( fVersion < VERSION_SPLIT_TIMING && fNewBeat > fBeat ) ) { - out.AddSegment( SEGMENT_WARP, new WarpSegment(BeatToNoteRow(fBeat), fNewBeat - fBeat) ); + out.AddSegment( WarpSegment(BeatToNoteRow(fBeat), fNewBeat - fBeat) ); } else if( fNewBeat > 0 ) - out.AddSegment( SEGMENT_WARP, new WarpSegment(BeatToNoteRow(fBeat), fNewBeat) ); + out.AddSegment( WarpSegment(BeatToNoteRow(fBeat), fNewBeat) ); else { LOG->UserLog("Song file", @@ -73,7 +73,7 @@ void SSCLoader::ProcessLabels( TimingData &out, const RString sParam ) RString sLabel = arrayLabelValues[1]; TrimRight(sLabel); if( fBeat >= 0.0f ) - out.AddSegment( SEGMENT_LABEL, new LabelSegment(BeatToNoteRow(fBeat), sLabel) ); + out.AddSegment( LabelSegment(BeatToNoteRow(fBeat), sLabel) ); else { LOG->UserLog("Song file", @@ -106,7 +106,7 @@ void SSCLoader::ProcessCombos( TimingData &out, const RString line, const int ro const float fComboBeat = StringToFloat( arrayComboValues[0] ); const int iCombos = StringToInt( arrayComboValues[1] ); const int iMisses = (size == 2 ? iCombos : StringToInt(arrayComboValues[2])); - out.AddSegment( SEGMENT_COMBO, new ComboSegment( BeatToNoteRow(fComboBeat), iCombos, iMisses ) ); + out.AddSegment( ComboSegment( BeatToNoteRow(fComboBeat), iCombos, iMisses ) ); } } @@ -128,11 +128,10 @@ void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam ) static_cast(vs2.size()) ); continue; } - + const float fBeat = StringToFloat( vs2[0] ); - - ScrollSegment * seg = new ScrollSegment(BeatToNoteRow(fBeat), StringToFloat( vs2[1] ) ); - + const float fRatio = StringToFloat( vs2[1] ); + if( fBeat < 0 ) { LOG->UserLog("Song file", @@ -141,8 +140,8 @@ void SSCLoader::ProcessScrolls( TimingData &out, const RString sParam ) fBeat ); continue; } - - out.AddSegment( SEGMENT_SCROLL, seg ); + + out.AddSegment( ScrollSegment(BeatToNoteRow(fBeat), fRatio) ); } } @@ -741,7 +740,7 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach ProcessAttacks(pNewNotes->m_Attacks, sParams); } } - + else if( sValueName=="OFFSET" ) { if (out.m_fVersion >= VERSION_SPLIT_TIMING) @@ -750,7 +749,7 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach bHasOwnTiming = true; } } - + else if( sValueName=="DISPLAYBPM" ) { // #DISPLAYBPM:[xxx][xxx:xxx]|[*]; diff --git a/src/NotesWriterDWI.cpp b/src/NotesWriterDWI.cpp index a8e1c35545..b512a53345 100644 --- a/src/NotesWriterDWI.cpp +++ b/src/NotesWriterDWI.cpp @@ -353,9 +353,8 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out ) /* Write transliterations, if we have them, since DWI doesn't support UTF-8. */ f.PutLine( ssprintf("#TITLE:%s;", DwiEscape(out.GetTranslitFullTitle()).c_str()) ); f.PutLine( ssprintf("#ARTIST:%s;", DwiEscape(out.GetTranslitArtist()).c_str()) ); - - const vector &bpms = out.m_SongTiming.m_avpTimingSegments[SEGMENT_BPM]; - + + const vector &bpms = out.m_SongTiming.GetTimingSegments(SEGMENT_BPM); ASSERT_M(bpms[0]->GetRow() == 0, ssprintf("The first BPM Segment must be defined at row 0, not %d!", bpms[0]->GetRow()) ); f.PutLine( ssprintf("#FILE:%s;", DwiEscape(out.m_sMusicFile).c_str()) ); @@ -382,7 +381,7 @@ bool NotesWriterDWI::Write( RString sPath, const Song &out ) } // TODO: Also check for delays, add them as stops minus one row? - const vector &stops = out.m_SongTiming.m_avpTimingSegments[SEGMENT_STOP]; + const vector &stops = out.m_SongTiming.GetTimingSegments(SEGMENT_STOP); if( !stops.empty() ) { f.Write( "#FREEZE:" ); diff --git a/src/NotesWriterJson.cpp b/src/NotesWriterJson.cpp index 927bbf5c25..e1b056a9f0 100644 --- a/src/NotesWriterJson.cpp +++ b/src/NotesWriterJson.cpp @@ -24,8 +24,8 @@ static void Serialize(const TimingSegment &seg, Json::Value &root) static void Serialize(const TimingData &td, Json::Value &root) { - JsonUtil::SerializeVectorPointers( td.m_avpTimingSegments[SEGMENT_BPM], Serialize, root["BpmSegments"] ); - JsonUtil::SerializeVectorPointers( td.m_avpTimingSegments[SEGMENT_STOP], Serialize, root["StopSegments"] ); + JsonUtil::SerializeVectorPointers( td.GetTimingSegments(SEGMENT_BPM), Serialize, root["BpmSegments"] ); + JsonUtil::SerializeVectorPointers( td.GetTimingSegments(SEGMENT_STOP), Serialize, root["StopSegments"] ); } static void Serialize(const LyricSegment &o, Json::Value &root) diff --git a/src/NotesWriterSM.cpp b/src/NotesWriterSM.cpp index 8bdffafac4..cf113f0df7 100644 --- a/src/NotesWriterSM.cpp +++ b/src/NotesWriterSM.cpp @@ -75,18 +75,18 @@ static void WriteGlobalTags( RageFile &f, Song &out ) f.Write( "#BPMS:" ); - vector &bpms = timing.m_avpTimingSegments[SEGMENT_BPM]; + const vector &bpms = timing.GetTimingSegments(SEGMENT_BPM); for( unsigned i=0; i(bpms[i]); + const BPMSegment *bs = ToBPM(bpms[i]); f.PutLine( ssprintf( "%.3f=%.3f", bs->GetBeat(), bs->GetBPM() ) ); if( i != bpms.size()-1 ) f.Write( "," ); } f.PutLine( ";" ); - - vector &warps = timing.m_avpTimingSegments[SEGMENT_WARP]; + + const vector &warps = timing.GetTimingSegments(SEGMENT_WARP); unsigned wSize = warps.size(); if( wSize > 0 ) { @@ -96,34 +96,33 @@ static void WriteGlobalTags( RageFile &f, Song &out ) int iRow = ws->GetRow(); float fBPS = 60 / out.m_SongTiming.GetBPMAtRow(iRow); float fSkip = fBPS * ws->GetLength(); - out.m_SongTiming.AddSegment(SEGMENT_STOP, - new StopSegment(iRow, -fSkip) ); + out.m_SongTiming.AddSegment( StopSegment(iRow, -fSkip) ); } } // TODO: make Delays into Stops that start one row before. - vector &stops = timing.m_avpTimingSegments[SEGMENT_STOP]; - vector &delays = timing.m_avpTimingSegments[SEGMENT_DELAY]; - + const vector &stops = timing.GetTimingSegments(SEGMENT_STOP); + const vector &delays = timing.GetTimingSegments(SEGMENT_DELAY); + map allPauses; for( unsigned i=0; i(stops[i]); - allPauses.insert(pair(fs->GetBeat(), - fs->GetPause())); + const StopSegment *fs = ToStop( stops[i] ); + + allPauses.insert(pair(fs->GetBeat(), fs->GetPause())); + + // erase stops with negative length if( fs->GetPause() < 0 ) - { - stops.erase(stops.begin()+i,stops.begin()+i+1 ); - i--; - } + timing.AddSegment( StopSegment(fs->GetRow(), 0) ); } // Delays can't be negative: thus, no effect. - FOREACH(TimingSegment *, delays, ss) + FOREACH_CONST(TimingSegment *, delays, ss) { - allPauses.insert(pair(NoteRowToBeat((*ss)->GetRow() - 1), - static_cast(*ss)->GetPause())); + float fBeat = NoteRowToBeat( (*ss)->GetRow()-1 ); + float fPause = ToDelay(*ss)->GetPause(); + allPauses.insert( pair(fBeat, fPause) ); } - + f.Write( "#STOPS:" ); vector stopLines; FOREACHM(float, float, allPauses, ap) @@ -133,7 +132,7 @@ static void WriteGlobalTags( RageFile &f, Song &out ) f.PutLine(join(",\n", stopLines)); f.PutLine( ";" ); - + FOREACH_BackgroundLayer( b ) { if( b==0 ) diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index 039195e9fe..a86f2f4e6a 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -60,115 +60,115 @@ struct TimingTagWriter { }; -static void GetTimingTags( vector &lines, TimingData timing, bool bIsSong = false ) +static void GetTimingTags( vector &lines, const TimingData &timing, bool bIsSong = false ) { TimingTagWriter w ( &lines ); - - timing.TidyUpData(); + + // timing.TidyUpData(); // UGLY: done via const_cast. do we really -need- this here? unsigned i = 0; w.Init( "BPMS" ); - vector &bpms = timing.m_avpTimingSegments[SEGMENT_BPM]; + const vector &bpms = timing.GetTimingSegments(SEGMENT_BPM); for (; i < bpms.size(); i++) { - BPMSegment *bs = static_cast(bpms[i]); + const BPMSegment *bs = ToBPM( bpms[i] ); w.Write( bs->GetRow(), bs->GetBPM() ); } w.Finish(); - + w.Init( "STOPS" ); - vector &stops = timing.m_avpTimingSegments[SEGMENT_STOP]; + const vector &stops = timing.GetTimingSegments(SEGMENT_STOP); for (i = 0; i < stops.size(); i++) { - StopSegment *ss = static_cast(stops[i]); + const StopSegment *ss = ToStop( stops[i] ); w.Write( ss->GetRow(), ss->GetPause() ); } w.Finish(); - + w.Init( "DELAYS" ); - vector &delays = timing.m_avpTimingSegments[SEGMENT_DELAY]; + const vector &delays = timing.GetTimingSegments(SEGMENT_DELAY); for (i = 0; i < delays.size(); i++) { - DelaySegment *ss = static_cast(delays[i]); + const DelaySegment *ss = ToDelay( delays[i] ); w.Write( ss->GetRow(), ss->GetPause() ); } w.Finish(); - + w.Init( "WARPS" ); - vector &warps = timing.m_avpTimingSegments[SEGMENT_WARP]; + const vector &warps = timing.GetTimingSegments(SEGMENT_WARP); for (i = 0; i < warps.size(); i++) { - WarpSegment *ws = static_cast(warps[i]); + const WarpSegment *ws = ToWarp( warps[i] ); w.Write( ws->GetRow(), ws->GetLength() ); } w.Finish(); - - vector &tSigs = timing.m_avpTimingSegments[SEGMENT_TIME_SIG]; + + const vector &tSigs = timing.GetTimingSegments(SEGMENT_TIME_SIG); ASSERT( !tSigs.empty() ); w.Init( "TIMESIGNATURES" ); for (i = 0; i < tSigs.size(); i++) { - TimeSignatureSegment *ts = static_cast(tSigs[i]); + const TimeSignatureSegment *ts = ToTimeSignature( tSigs[i] ); w.Write( ts->GetRow(), ts->GetNum(), ts->GetDen() ); } w.Finish(); - vector &ticks = timing.m_avpTimingSegments[SEGMENT_TICKCOUNT]; + const vector &ticks = timing.GetTimingSegments(SEGMENT_TICKCOUNT); ASSERT( !ticks.empty() ); w.Init( "TICKCOUNTS" ); for (i = 0; i < ticks.size(); i++) { - TickcountSegment *ts = static_cast(ticks[i]); + const TickcountSegment *ts = ToTickcount( ticks[i] ); w.Write( ts->GetRow(), ts->GetTicks() ); } w.Finish(); - - vector &combos = timing.m_avpTimingSegments[SEGMENT_COMBO]; + + const vector &combos = timing.GetTimingSegments(SEGMENT_COMBO); ASSERT( !combos.empty() ); w.Init( "COMBOS" ); for (i = 0; i < combos.size(); i++) { - ComboSegment *cs = static_cast(combos[i]); + const ComboSegment *cs = ToCombo( combos[i] ); if (cs->GetCombo() == cs->GetMissCombo()) w.Write( cs->GetRow(), cs->GetCombo() ); else w.Write( cs->GetRow(), cs->GetCombo(), cs->GetMissCombo() ); } w.Finish(); - + // Song Timing should only have the initial value. - vector &speeds = timing.m_avpTimingSegments[SEGMENT_SPEED]; + const vector &speeds = timing.GetTimingSegments(SEGMENT_SPEED); w.Init( "SPEEDS" ); for (i = 0; i < speeds.size(); i++) { - SpeedSegment *ss = static_cast(speeds[i]); + SpeedSegment *ss = ToSpeed( speeds[i] ); w.Write( ss->GetRow(), ss->GetRatio(), ss->GetDelay(), ss->GetUnit() ); } w.Finish(); - + w.Init( "SCROLLS" ); - vector &scrolls = timing.m_avpTimingSegments[SEGMENT_SCROLL]; + const vector &scrolls = timing.GetTimingSegments(SEGMENT_SCROLL); for (i = 0; i < scrolls.size(); i++) { - ScrollSegment *ss = static_cast(scrolls[i]); + ScrollSegment *ss = ToScroll( scrolls[i] ); w.Write( ss->GetRow(), ss->GetRatio() ); } w.Finish(); - + if( !bIsSong ) - { - vector &fakes = timing.m_avpTimingSegments[SEGMENT_FAKE]; + { + const vector &fakes = timing.GetTimingSegments(SEGMENT_FAKE); w.Init( "FAKES" ); for (i = 0; i < fakes.size(); i++) { - FakeSegment *fs = static_cast(fakes[i]); + FakeSegment *fs = ToFake( fakes[i] ); w.Write( fs->GetRow(), fs->GetLength() ); } w.Finish(); } - + w.Init( "LABELS" ); - vector &labels = timing.m_avpTimingSegments[SEGMENT_LABEL]; + const vector &labels = timing.GetTimingSegments(SEGMENT_LABEL); for (i = 0; i < labels.size(); i++) { LabelSegment *ls = static_cast(labels[i]); @@ -344,7 +344,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa lines.push_back( ssprintf( "#RADARVALUES:%s;", join(",",asRadarValues).c_str() ) ); lines.push_back( ssprintf( "#CREDIT:%s;", SmEscape(in.GetCredit()).c_str() ) ); - + // XXX: Is there a better way to write this? if (const_cast(song.m_SongTiming) != in.m_Timing) { @@ -353,7 +353,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa } if (song.GetAttackString() != in.GetAttackString()) lines.push_back( ssprintf("#ATTACKS:%s;", in.GetAttackString().c_str())); - + switch( in.GetDisplayBPM() ) { case DISPLAY_BPM_ACTUAL: diff --git a/src/Player.cpp b/src/Player.cpp index ce7146f9df..dc6d2bf2ac 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -752,24 +752,23 @@ void Player::SendComboMessages( int iOldCombo, int iOldMissCombo ) } static void GenerateCacheDataStructure(PlayerState *pPlayerState, NoteData ¬es) { - + pPlayerState->m_CacheDisplayedBeat.clear(); - - const vector *segs = pPlayerState->GetDisplayedTiming().m_avpTimingSegments; - + + const vector vScrolls = pPlayerState->GetDisplayedTiming().GetTimingSegments( SEGMENT_SCROLL ); + float displayedBeat = 0.0f; float lastRealBeat = 0.0f; float lastRatio = 1.0f; - for ( unsigned i = 0; i < segs[SEGMENT_SCROLL].size(); i++ ) + for ( unsigned i = 0; i < vScrolls.size(); i++ ) { - ScrollSegment *seg = static_cast(segs[SEGMENT_SCROLL][i]); + ScrollSegment *seg = ToScroll( vScrolls[i] ); displayedBeat += ( seg->GetBeat() - lastRealBeat ) * lastRatio; lastRealBeat = seg->GetBeat(); lastRatio = seg->GetRatio(); CacheDisplayedBeat c = { seg->GetBeat(), displayedBeat, seg->GetRatio() }; pPlayerState->m_CacheDisplayedBeat.push_back( c ); } - } void Player::Update( float fDeltaTime ) diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index baf74ce279..6fe4859305 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -1864,7 +1864,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) else fDelta *= 40; } - + float fNewBPM = fBPM + fDelta; GetAppropriateTiming().SetBPMAtBeat( GetBeat(), fNewBPM ); (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); @@ -1893,19 +1893,21 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) fDelta *= 40; } unsigned i; - vector &stops = GetAppropriateTiming().m_avpTimingSegments[SEGMENT_STOP]; - for( i=0; iGetRow() == GetRow() ) - break; - } +#if 0 + // is there a StopSegment on the current row? + const StopSegment *seg = GetAppropriateTiming().GetStopSegmentAtRow( GetRow() ); + + // a stop already exists here; change its value by the delta + if( seg->GetRow() == GetRow() ) + { + float fSeconds = seg->GetPause() + fDelta; + GetAppropriateTiming().AddSegment if( i == stops.size() ) // there is no StopSegment at the current beat { // create a new StopSegment if( fDelta > 0 ) - GetAppropriateTiming().AddSegment(SEGMENT_STOP, - new StopSegment( GetRow(), fDelta) ); + GetAppropriateTiming().AddSegment( StopSegment(GetRow(), fDelta) ); } else // StopSegment being modified is m_SongTiming.m_StopSegments[i] { @@ -1914,6 +1916,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) if( s->GetPause() <= 0 ) stops.erase( stops.begin()+i, stops.begin()+i+1); } +#endif (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); SetDirty( true ); } @@ -2962,59 +2965,67 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) else if( SM == SM_BackFromBPMChange && !ScreenTextEntry::s_bCancelledLast ) { float fBPM = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + if( fBPM > 0 ) - GetAppropriateTiming().SetBPMAtBeat( GetBeat(), fBPM ); + GetAppropriateTiming().AddSegment( BPMSegment(GetRow(), fBPM) ); + SetDirty( true ); } else if( SM == SM_BackFromStopChange && !ScreenTextEntry::s_bCancelledLast ) { float fStop = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + if( fStop >= 0 ) - GetAppropriateTiming().SetStopAtBeat( GetBeat(), fStop ); + GetAppropriateTiming().AddSegment( StopSegment(GetRow(), fStop) ); + SetDirty( true ); } else if( SM == SM_BackFromDelayChange && !ScreenTextEntry::s_bCancelledLast ) { float fDelay = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + if( fDelay >= 0 ) - GetAppropriateTiming().SetDelayAtBeat( GetBeat(), fDelay ); + GetAppropriateTiming().AddSegment( DelaySegment(GetRow(), fDelay) ); + SetDirty( true ); } else if( SM == SM_BackFromTimeSignatureChange && !ScreenTextEntry::s_bCancelledLast ) { int iNum, iDen; + if( sscanf( ScreenTextEntry::s_sLastAnswer.c_str(), " %d / %d ", &iNum, &iDen ) == 2 ) - { - GetAppropriateTiming().SetTimeSignatureAtBeat( GetBeat(), iNum, iDen ); - } + GetAppropriateTiming().AddSegment( TimeSignatureSegment(GetRow(), iNum, iDen) ); + SetDirty( true ); } else if ( SM == SM_BackFromTickcountChange && !ScreenTextEntry::s_bCancelledLast ) { int iTick = StringToInt( ScreenTextEntry::s_sLastAnswer ); + if ( iTick >= 0 && iTick <= ROWS_PER_BEAT ) - { - GetAppropriateTiming().SetTickcountAtBeat( GetBeat(), iTick ); - } + GetAppropriateTiming().AddSegment( TickcountSegment( GetRow(), iTick) ); + SetDirty( true ); } else if ( SM == SM_BackFromComboChange && !ScreenTextEntry::s_bCancelledLast ) { int iCombo, iMiss; + if (sscanf(ScreenTextEntry::s_sLastAnswer.c_str(), " %d / %d ", &iCombo, &iMiss) == 2) - { - GetAppropriateTiming().SetComboAtBeat( GetBeat(), iCombo, iMiss ); - } + GetAppropriateTiming().AddSegment( ComboSegment(GetRow(), iCombo, iMiss) ); + SetDirty( true ); } else if ( SM == SM_BackFromLabelChange && !ScreenTextEntry::s_bCancelledLast ) { RString sLabel = ScreenTextEntry::s_sLastAnswer; + if ( !GetAppropriateTiming().DoesLabelExist(sLabel) ) { + // XXX: these should be in the NotesWriters where they're needed. sLabel.Replace("=", "_"); sLabel.Replace(",", "_"); - GetAppropriateTiming().SetLabelAtBeat( GetBeat(), sLabel ); + GetAppropriateTiming().AddSegment( LabelSegment(GetRow(), sLabel) ); SetDirty( true ); } } @@ -3074,7 +3085,7 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) float fFake = StringToFloat( ScreenTextEntry::s_sLastAnswer ); if( fFake >= 0 ) // allow 0 to kill a warp. { - GetAppropriateTiming().SetFakeAtBeat( GetBeat(), fFake ); + GetAppropriateTiming().AddSegment( FakeSegment(GetRow(), fFake) ); SetDirty( true ); } } @@ -3472,12 +3483,13 @@ static void ChangeArtistTranslit( const RString &sNew ) static void ChangeBeat0Offset( const RString &sNew ) { - TimingData &timing = (GAMESTATE->m_bIsUsingStepTiming ? - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : + TimingData &timing = (GAMESTATE->m_bIsUsingStepTiming ? + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : GAMESTATE->m_pCurSong->m_SongTiming); float old = timing.m_fBeat0OffsetInSeconds; - timing.m_fBeat0OffsetInSeconds = StringToFloat( sNew ); + timing.m_fBeat0OffsetInSeconds = StringToFloat(sNew); float delta = timing.m_fBeat0OffsetInSeconds - old; + if (GAMESTATE->m_bIsUsingStepTiming) { GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(delta); @@ -3578,14 +3590,19 @@ void ScreenEdit::DisplayTimingMenu() float fBeat = GetBeat(); TimingData &pTime = GetAppropriateTiming(); bool bHasSpeedOnThisRow = pTime.GetSpeedSegmentAtBeat( fBeat )->GetBeat() == fBeat; - + g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( ssprintf("%.6f", pTime.m_fBeat0OffsetInSeconds) ); g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetBPMAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetStopAtBeat( fBeat ) ) ) ; g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetDelayAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[time_signature].SetOneUnthemedChoice(ssprintf("%d / %d", - pTime.GetTimeSignatureNumeratorAtBeat( fBeat ), - pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) ); + + g_TimingDataInformation.rows[time_signature].SetOneUnthemedChoice( + ssprintf( "%d / %d", + pTime.GetTimeSignatureSegmentAtBeat(fBeat)->GetNum(), + pTime.GetTimeSignatureSegmentAtBeat(fBeat)->GetDen() + ) + ); + g_TimingDataInformation.rows[label].SetOneUnthemedChoice( pTime.GetLabelAtBeat( fBeat ).c_str() ); g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d / %d", @@ -3594,16 +3611,16 @@ void ScreenEdit::DisplayTimingMenu() g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetWarpAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.6f", pTime.GetSpeedPercentAtBeat( fBeat ) ) : "---" ); g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.6f", pTime.GetSpeedWaitAtBeat( fBeat ) ) : "---" ); - + RString starting = ( pTime.GetSpeedModeAtBeat( fBeat ) == 1 ? "Seconds" : "Beats" ); g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); - + g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetScrollAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetFakeAtBeat( fBeat ) ) ); - + g_TimingDataInformation.rows[speed_wait].bEnabled = bHasSpeedOnThisRow; g_TimingDataInformation.rows[speed_mode].bEnabled = bHasSpeedOnThisRow; - + EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation ); } @@ -4167,9 +4184,9 @@ void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAn } case convert_to_fake: { - float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); - float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - startBeat; - GetAppropriateTiming().SetFakeAtBeat(startBeat,lengthBeat); + int startRow = m_NoteFieldEdit.m_iBeginMarker; + float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - NoteRowToBeat(startRow); + GetAppropriateTiming().AddSegment( FakeSegment(startRow,lengthBeat) ); SetDirty(true); break; } @@ -4279,7 +4296,7 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns switch( c ) { DEFAULT_FAIL( c ); - + case paste_at_current_beat: case paste_at_begin_marker: { @@ -4302,82 +4319,28 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns break; case paste_partial_timing_at_beat: { +#if 0 int firstRow = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); - FOREACH_ENUM(TimingSegmentType, tst) + + FOREACH_TimingSegmentType( tst ) { /* TODO: Maybe wipe out the already there timing data first? * We need to identify the max row within the timing data first. */ - for (unsigned i = 0; i < this->clipboardTiming.m_avpTimingSegments[tst].size(); i++) + const vector &vSegs = clipboardTiming.GetTimingSegments(tst); + + /* TODO: this is an exact dupe of TimingData::CopyRange... */ + for (unsigned i = 0; i < vSegs.size(); i++) { - // TODO: This REALLY needs improving. - TimingSegment * org = this->clipboardTiming.m_avpTimingSegments[tst][i]; - TimingSegment * cpy; - - switch (tst) - { - case SEGMENT_BPM: - { - cpy = new BPMSegment(*(static_cast(org))); - break; - } - case SEGMENT_STOP: - { - cpy = new StopSegment(*(static_cast(org))); - break; - } - case SEGMENT_DELAY: - { - cpy = new DelaySegment(*(static_cast(org))); - break; - } - case SEGMENT_TIME_SIG: - { - cpy = new TimeSignatureSegment(*(static_cast(org))); - break; - } - case SEGMENT_WARP: - { - cpy = new WarpSegment(*(static_cast(org))); - break; - } - case SEGMENT_LABEL: - { - cpy = new LabelSegment(*(static_cast(org))); - break; - } - case SEGMENT_TICKCOUNT: - { - cpy = new TickcountSegment(*(static_cast(org))); - break; - } - case SEGMENT_COMBO: - { - cpy = new ComboSegment(*(static_cast(org))); - break; - } - case SEGMENT_SPEED: - { - cpy = new SpeedSegment(*(static_cast(org))); - break; - } - case SEGMENT_SCROLL: - { - cpy = new ScrollSegment(*(static_cast(org))); - break; - } - case SEGMENT_FAKE: - { - cpy = new FakeSegment(*(static_cast(org))); - break; - } - default: FAIL_M(ssprintf("An unknown timing segment type %d can't be copied over!", tst)); - } - int oldRow = cpy->GetRow(); + const TimingSegment *seg = vSegs[i]; + TimingSegment *cpy = seg->Copy(); + + int oldRow = cpy->GetRow() + firstRow; int newRow = oldRow + firstRow; cpy->SetRow(newRow); - GetAppropriateTiming().AddSegment(tst, cpy); + GetAppropriateTiming().AddSegment( cpy ); } } +#endif break; } case insert_and_shift: @@ -4410,9 +4373,9 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns TimingData &timing = GetAppropriateTiming(); float pause = timing.GetDelayAtRow(GetRow()); timing.SetDelayAtRow(GetRow(), 0); - + float pauseBeats = pause * timing.GetBPMAtBeat(GetBeat()) / 60; - + NoteDataUtil::InsertRows(m_NoteDataEdit, GetRow(), BeatToNoteRow(pauseBeats)); timing.InsertRows(GetRow(), BeatToNoteRow(pauseBeats)); break; @@ -4991,7 +4954,8 @@ void ScreenEdit::CheckNumberOfNotesAndUndo() if( EDIT_MODE.GetValue() != EditMode_Home ) return; - const TimeSignatureSegment * curTime = GAMESTATE->m_pCurSong->m_SongTiming.GetTimeSignatureSegmentAtBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat ); + const float fBeat = GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; + const TimeSignatureSegment * curTime = GAMESTATE->m_pCurSong->m_SongTiming.GetTimeSignatureSegmentAtBeat( fBeat ); int rowsPerMeasure = curTime->GetDen() * curTime->GetNum(); for( int row=0; row<=m_NoteDataEdit.GetLastRow(); row+=rowsPerMeasure ) diff --git a/src/TimingData.cpp b/src/TimingData.cpp index 63b7d90356..5db5e49f51 100644 --- a/src/TimingData.cpp +++ b/src/TimingData.cpp @@ -13,101 +13,53 @@ TimingData::TimingData(float fOffset) : m_fBeat0OffsetInSeconds(fOffset) TimingData::~TimingData() { +// This is causing weird crashes, probably due to someone hanging onto pointers +// for too long. Commenting this out until we can track it down... -- vyhd +#if 0 + /* Delete all pointers owned by this TimingData. */ + FOREACH_TimingSegmentType( tst ) + { + vector &vSegs = m_avpTimingSegments[tst]; + for( unsigned i = 0; i < vSegs.size(); ++i ) + delete vSegs[i]; + vSegs.clear(); + } +#endif } bool TimingData::empty() const { - for (unsigned i = 0; i < NUM_TimingSegmentType; i++) - { - if (m_avpTimingSegments[i].size() > 0) + FOREACH_TimingSegmentType( tst ) + if( !GetTimingSegments(tst).empty() ) return false; - } + return true; } TimingData TimingData::CopyRange(int startRow, int endRow) const { TimingData ret; - - FOREACH_ENUM(TimingSegmentType, tst) + + FOREACH_TimingSegmentType( tst ) { - unsigned cnt = 0; - for (unsigned j = 0; j < m_avpTimingSegments[tst].size(); j++) + const vector &vSegs = GetTimingSegments(tst); + + for (unsigned i = 0; i < vSegs.size(); i++) { - int row = m_avpTimingSegments[tst][j]->GetRow(); + const TimingSegment *seg = vSegs[i]; + int row = seg->GetRow(); + if (row >= startRow && row < endRow) { - // TODO: This REALLY needs improving. - TimingSegment * org = m_avpTimingSegments[tst][j]; - TimingSegment * cpy; - - switch (tst) - { - case SEGMENT_BPM: - { - cpy = new BPMSegment(*(ToBPM(org))); - break; - } - case SEGMENT_STOP: - { - cpy = new StopSegment(*(ToStop(org))); - break; - } - case SEGMENT_DELAY: - { - cpy = new DelaySegment(*(ToDelay(org))); - break; - } - case SEGMENT_TIME_SIG: - { - cpy = new TimeSignatureSegment(*(ToTimeSignature(org))); - break; - } - case SEGMENT_WARP: - { - cpy = new WarpSegment(*(ToWarp(org))); - break; - } - case SEGMENT_LABEL: - { - cpy = new LabelSegment(*(ToLabel(org))); - break; - } - case SEGMENT_TICKCOUNT: - { - cpy = new TickcountSegment(*(ToTickcount(org))); - break; - } - case SEGMENT_COMBO: - { - cpy = new ComboSegment(*(ToCombo(org))); - break; - } - case SEGMENT_SPEED: - { - cpy = new SpeedSegment(*(ToSpeed(org))); - break; - } - case SEGMENT_SCROLL: - { - cpy = new ScrollSegment(*(ToScroll(org))); - break; - } - case SEGMENT_FAKE: - { - cpy = new FakeSegment(*(ToFake(org))); - break; - } - default: FAIL_M(ssprintf("An unknown timing segment type %d can't be copied over!", tst)); - } - // reset the rows as if startRow was beat 0. - cpy->SetRow(org->GetRow() - startRow); - ret.AddSegment(tst, cpy); - cnt++; + TimingSegment *cpy = seg->Copy(); + + // offset rows as though startRow were beat 0. + cpy->SetRow(seg->GetRow() - startRow); + ret.AddSegment(cpy); } } } - + return ret; } @@ -115,32 +67,19 @@ void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut, float highe { fMinBPMOut = FLT_MAX; fMaxBPMOut = 0; - const vector &bpms = m_avpTimingSegments[SEGMENT_BPM]; + const vector &bpms = GetTimingSegments(SEGMENT_BPM); + for (unsigned i = 0; i < bpms.size(); i++) { - BPMSegment *seg = ToBPM( bpms[i] ); - const float fBPM = seg->GetBPM(); + const float fBPM = ToBPM(bpms[i])->GetBPM(); fMaxBPMOut = clamp(max( fBPM, fMaxBPMOut ), 0, highest); fMinBPMOut = min( fBPM, fMinBPMOut ); } } -struct ts_less : binary_function { - bool operator() (const TimingSegment *x, const TimingSegment *y) const { - return (*x) < (*y); - } -}; - -void TimingData::AddSegment(TimingSegmentType tst, TimingSegment * seg) -{ - vector &segs = m_avpTimingSegments[tst]; - // Unsure if this uses the proper comparison. - segs.insert(upper_bound(segs.begin(), segs.end(), seg, ts_less()), seg); -} - float TimingData::GetNextSegmentBeatAtRow(TimingSegmentType tst, int row) const { - const vector segs = m_avpTimingSegments[tst]; + const vector segs = GetTimingSegments(tst); for (unsigned i = 0; i < segs.size(); i++ ) { if( segs[i]->GetRow() <= row ) @@ -155,7 +94,7 @@ float TimingData::GetNextSegmentBeatAtRow(TimingSegmentType tst, int row) const float TimingData::GetPreviousSegmentBeatAtRow(TimingSegmentType tst, int row) const { float backup = -1; - const vector segs = m_avpTimingSegments[tst]; + const vector segs = GetTimingSegments(tst); for (unsigned i = 0; i < segs.size(); i++ ) { if( segs[i]->GetRow() >= row ) @@ -167,477 +106,36 @@ float TimingData::GetPreviousSegmentBeatAtRow(TimingSegmentType tst, int row) co return (backup > -1) ? backup : NoteRowToBeat(row); } -unsigned TimingData::GetSegmentIndexAtRow(TimingSegmentType tst, int iRow ) const -{ - const vector &vSegments = m_avpTimingSegments[tst]; +static const int INVALID_INDEX = -1; - unsigned i = 0; +int TimingData::GetSegmentIndexAtRow(TimingSegmentType tst, int iRow ) const +{ + const vector &vSegs = GetTimingSegments(tst); + + int i = 0; + + if( vSegs.empty() ) + return INVALID_INDEX; // seek to the last segment that goes into effect before iRow. - // OPTIMIZATION OPPORTUNITY: use std::upper_bound instead? - for( ; i < vSegments.size() - 1; ++i ) - if( iRow < vSegments[i+1]->GetRow() ) + // UGLY: vSegs.size() is cast to an int because its normal return type + // is size_t, but when it equals zero, subtracting 1 wraps around. + for( ; i < int(vSegs.size()) - 1; ++i ) + { + if( iRow < vSegs[i+1]->GetRow() ) break; + } return i; } -// TODO: Find a way to combine all of these SetAtRows to one. - -/* Change an existing BPM segment, merge identical segments together or insert a new one. */ -void TimingData::SetBPMAtRow( int iNoteRow, float fBPM ) +struct ts_less : binary_function { - unsigned i; - vector &bpms = m_avpTimingSegments[SEGMENT_BPM]; - for( i=0; iGetRow() >= iNoteRow ) - break; - - if( i == bpms.size() || bpms[i]->GetRow() != iNoteRow ) + bool operator() (const TimingSegment *x, const TimingSegment *y) const { - // There is no BPMSegment at the specified beat. If the BPM being set differs - // from the last BPMSegment's BPM, create a new BPMSegment. - if (i == 0 || - fabsf( ToBPM(bpms[i-1])->GetBPM() - fBPM) > 1e-5f ) - AddSegment( SEGMENT_BPM, new BPMSegment(iNoteRow, fBPM) ); + return (*x) < (*y); } - else // BPMSegment being modified is m_BPMSegments[i] - { - if (i > 0 && - fabsf(ToBPM(bpms[i-1])->GetBPM() - fBPM) < 1e-5f ) - bpms.erase( bpms.begin()+i, bpms.begin()+i+1 ); - else - ToBPM(bpms[i])->SetBPM(fBPM); - } -} - -void TimingData::SetStopAtRow( int iRow, float fSeconds ) -{ - unsigned i; - vector &stops = m_avpTimingSegments[SEGMENT_STOP]; - for( i=0; iGetRow() == iRow) - break; - - if( i == stops.size() ) // there is no StopSegment at the current beat - { - // create a new StopSegment - if( fSeconds > 0 ) - { - AddSegment( SEGMENT_STOP, new StopSegment(iRow, fSeconds) ); - } - } - else // StopSegment being modified is m_StopSegments[i] - { - StopSegment *ss = ToStop(stops[i]); - if( fSeconds > 0 ) - { - ss->SetPause(fSeconds); - } - else - stops.erase( stops.begin()+i, stops.begin()+i+1 ); - } -} - -void TimingData::SetDelayAtRow( int iRow, float fSeconds ) -{ - unsigned i; - vector &stops = m_avpTimingSegments[SEGMENT_DELAY]; - for( i=0; iGetRow() == iRow) - break; - - if( i == stops.size() ) // there is no DelaySegment at the current beat - { - // create a new DelaySegment - if( fSeconds > 0 ) - { - AddSegment( SEGMENT_DELAY, new DelaySegment(iRow, fSeconds) ); - } - } - else // DelaySegment being modified is present one - { - DelaySegment *ss = ToDelay(stops[i]); - if( fSeconds > 0 ) - { - ss->SetPause(fSeconds); - } - else - stops.erase( stops.begin()+i, stops.begin()+i+1 ); - } -} - -void TimingData::SetTimeSignatureAtRow( int iRow, int iNumerator, int iDenominator ) -{ - unsigned i; - vector &tSigs = m_avpTimingSegments[SEGMENT_TIME_SIG]; - for( i = 0; i < tSigs.size(); i++ ) - { - if( tSigs[i]->GetRow() >= iRow) - break; // We found our segment. - } - - if ( i == tSigs.size() || tSigs[i]->GetRow() != iRow ) - { - // No specific segment here: place one if it differs. - if (i == 0 || - (ToTimeSignature(tSigs[i-1])->GetNum() != iNumerator || - ToTimeSignature(tSigs[i-1])->GetDen() != iDenominator ) ) - AddSegment( SEGMENT_TIME_SIG, new TimeSignatureSegment(iRow, iNumerator, iDenominator) ); - } - else // TimeSignatureSegment being modified is m_vTimeSignatureSegments[i] - { - if (i > 0 && - ToTimeSignature(tSigs[i-1])->GetNum() == iNumerator && - ToTimeSignature(tSigs[i-1])->GetDen() == iDenominator ) - tSigs.erase( tSigs.begin()+i, tSigs.begin()+i+1 ); - else - { - ToTimeSignature(tSigs[i])->SetNum(iNumerator); - ToTimeSignature(tSigs[i])->SetDen(iDenominator); - } - } -} - -void TimingData::SetTimeSignatureNumeratorAtRow( int iRow, int iNum ) -{ - unsigned iDenom = GetTimeSignatureSegmentAtRow(iRow)->GetDen(); - SetTimeSignatureAtRow(iRow, iNum, iDenom ); -} - -void TimingData::SetTimeSignatureDenominatorAtRow( int iRow, int iDenom ) -{ - unsigned iNum = GetTimeSignatureSegmentAtRow(iRow)->GetNum(); - SetTimeSignatureAtRow( iRow, iNum, iDenom ); -} - -void TimingData::SetWarpAtRow( int iRow, float fNew ) -{ - unsigned i; - vector &warps = m_avpTimingSegments[SEGMENT_WARP]; - for( i=0; iGetRow() == iRow ) - break; - bool valid = iRow > 0 && fNew > 0; - if( i == warps.size() ) - { - if( valid ) - { - AddSegment( SEGMENT_WARP, new WarpSegment(iRow, fNew) ); - } - } - else - { - if( valid ) - { - ToWarp(warps[i])->SetLength(fNew); - } - else - warps.erase( warps.begin()+i, warps.begin()+i+1 ); - } -} - -/* Change an existing Tickcount segment, merge identical segments together or insert a new one. */ -void TimingData::SetTickcountAtRow( int iRow, int iTicks ) -{ - unsigned i = 0; - vector &ticks = m_avpTimingSegments[SEGMENT_TICKCOUNT]; - for( i=0; iGetRow() >= iRow ) - break; - - if( i == ticks.size() || ticks[i]->GetRow() != iRow ) - { - // No TickcountSegment here. Make a new segment if required. - if (i == 0 || - ToTickcount(ticks[i-1])->GetTicks() != iTicks ) - AddSegment( SEGMENT_TICKCOUNT, new TickcountSegment(iRow, iTicks ) ); - } - else // TickcountSegment being modified is m_TickcountSegments[i] - { - if (i > 0 && - ToTickcount(ticks[i-1])->GetTicks() == iTicks ) - ticks.erase( ticks.begin()+i, ticks.begin()+i+1 ); - else - ToTickcount(ticks[i])->SetTicks(iTicks); - } -} - -void TimingData::SetComboAtRow( int iRow, int iCombo, int iMiss ) -{ - unsigned i; - vector &combos = m_avpTimingSegments[SEGMENT_COMBO]; - for( i=0; iGetRow() >= iRow ) - break; - - if( i == combos.size() || combos[i]->GetRow() != iRow ) - { - if (i == 0 || - ToCombo(combos[i-1])->GetCombo() != iCombo || - ToCombo(combos[i-1])->GetMissCombo() != iMiss) - AddSegment( SEGMENT_COMBO, new ComboSegment(iRow, iCombo, iMiss ) ); - } - else - { - if (i > 0 && - ToCombo(combos[i-1])->GetCombo() == iCombo && - ToCombo(combos[i-1])->GetMissCombo() == iMiss) - combos.erase( combos.begin()+i, combos.begin()+i+1 ); - else - { - ToCombo(combos[i])->SetCombo(iCombo); - ToCombo(combos[i])->SetMissCombo(iMiss); - } - } -} - -void TimingData::SetHitComboAtRow(int iRow, int iCombo) -{ - SetComboAtRow(iRow, - iCombo, - GetComboSegmentAtRow(iRow)->GetMissCombo()); -} - -void TimingData::SetMissComboAtRow(int iRow, int iMiss) -{ - SetComboAtRow(iRow, - GetComboSegmentAtRow(iRow)->GetCombo(), - iMiss); -} - -void TimingData::SetLabelAtRow( int iRow, const RString sLabel ) -{ - unsigned i; - vector &labels = m_avpTimingSegments[SEGMENT_LABEL]; - for( i=0; iGetRow() >= iRow ) - break; - - if( i == labels.size() || labels[i]->GetRow() != iRow ) - { - if (i == 0 || - ToLabel(labels[i-1])->GetLabel() != sLabel ) - AddSegment( SEGMENT_LABEL, new LabelSegment(iRow, sLabel ) ); - } - else - { - if (i > 0 && - ( ToLabel(labels[i-1])->GetLabel() == sLabel || - sLabel == "" ) ) - labels.erase( labels.begin()+i, labels.begin()+i+1 ); - else - ToLabel(labels[i])->SetLabel(sLabel); - } -} - -void TimingData::SetSpeedAtRow( int iRow, float fPercent, float fWait, SpeedSegment::BaseUnit unit ) -{ - unsigned i; - vector &speeds = m_avpTimingSegments[SEGMENT_SPEED]; - for( i = 0; i < speeds.size(); i++ ) - { - if( speeds[i]->GetRow() >= iRow) - break; - } - - if ( i == speeds.size() || speeds[i]->GetRow() != iRow ) - { - // the core mod itself matters the most for comparisons. - if (i == 0 || - ToSpeed(speeds[i-1])->GetRatio() != fPercent ) - AddSegment( SEGMENT_SPEED, new SpeedSegment(iRow, fPercent, fWait, unit) ); - } - else - { - // The others aren't compared: only the mod itself matters. - if (i > 0 && - ToSpeed(speeds[i-1])->GetRatio() == fPercent ) - speeds.erase( speeds.begin()+i, speeds.begin()+i+1 ); - else - { - ToSpeed(speeds[i])->SetRatio(fPercent); - ToSpeed(speeds[i])->SetDelay(fWait); - ToSpeed(speeds[i])->SetUnit(unit); - } - } -} - -void TimingData::SetScrollAtRow( int iRow, float fPercent ) -{ - unsigned i; - vector &scrolls = m_avpTimingSegments[SEGMENT_SCROLL]; - for( i = 0; i < scrolls.size(); i++ ) - { - if( scrolls[i]->GetRow() >= iRow) - break; - } - - if ( i == scrolls.size() || scrolls[i]->GetRow() != iRow ) - { - // the core mod itself matters the most for comparisons. - if (i == 0 || - ToScroll(scrolls[i-1])->GetRatio() != fPercent ) - AddSegment( SEGMENT_SCROLL, new ScrollSegment(iRow, fPercent) ); - } - else - { - // The others aren't compared: only the mod itself matters. - if (i > 0 && - ToScroll(scrolls[i-1])->GetRatio() == fPercent ) - scrolls.erase( scrolls.begin()+i, scrolls.begin()+i+1 ); - else - { - ToScroll(scrolls[i])->SetRatio(fPercent); - } - } -} - -void TimingData::SetFakeAtRow( int iRow, float fNew ) -{ - unsigned i; - vector &fakes = m_avpTimingSegments[SEGMENT_FAKE]; - for( i=0; iGetRow() == iRow ) - break; - bool valid = iRow > 0 && fNew > 0; - if( i == fakes.size() ) - { - if( valid ) - { - AddSegment( SEGMENT_FAKE, new FakeSegment(iRow, fNew) ); - } - } - else - { - if( valid ) - { - ToFake(fakes[i])->SetLength(fNew); - } - else - fakes.erase( fakes.begin()+i, fakes.begin()+i+1 ); - } -} - -void TimingData::SetSpeedPercentAtRow( int iRow, float fPercent ) -{ - SetSpeedAtRow( iRow, - fPercent, - GetSpeedSegmentAtRow( iRow )->GetDelay(), - GetSpeedSegmentAtRow( iRow )->GetUnit()); -} - -void TimingData::SetSpeedWaitAtRow( int iRow, float fWait ) -{ - SetSpeedAtRow( iRow, - GetSpeedSegmentAtRow( iRow )->GetRatio(), - fWait, - GetSpeedSegmentAtRow( iRow )->GetUnit()); -} - -void TimingData::SetSpeedModeAtRow( int iRow, SpeedSegment::BaseUnit unit ) -{ - SetSpeedAtRow( iRow, - GetSpeedSegmentAtRow( iRow )->GetRatio(), - GetSpeedSegmentAtRow( iRow )->GetDelay(), - unit ); -} - -float TimingData::GetStopAtRow( int iRow ) const -{ - const vector &stops = m_avpTimingSegments[SEGMENT_STOP]; - for( unsigned i=0; iGetRow() == iRow ) - { - return s->GetPause(); - } - } - return 0; -} - - -float TimingData::GetDelayAtRow( int iRow ) const -{ - const vector &stops = m_avpTimingSegments[SEGMENT_DELAY]; - for( unsigned i=0; iGetRow() == iRow ) - { - return s->GetPause(); - } - } - return 0; -} - -int TimingData::GetComboAtRow( int iNoteRow ) const -{ - const vector &c = m_avpTimingSegments[SEGMENT_COMBO]; - const int index = GetSegmentIndexAtRow(SEGMENT_COMBO, iNoteRow); - return ToCombo(c[index])->GetCombo(); -} - -int TimingData::GetMissComboAtRow(int iNoteRow) const -{ - const vector &c = m_avpTimingSegments[SEGMENT_COMBO]; - const int index = GetSegmentIndexAtRow(SEGMENT_COMBO, iNoteRow); - return ToCombo(c[index])->GetMissCombo(); -} - -RString TimingData::GetLabelAtRow( int iRow ) const -{ - const vector &l = m_avpTimingSegments[SEGMENT_LABEL]; - const int index = GetSegmentIndexAtRow(SEGMENT_LABEL, iRow); - return ToLabel(l[index])->GetLabel(); -} - -float TimingData::GetWarpAtRow( int iWarpRow ) const -{ - const vector &warps = m_avpTimingSegments[SEGMENT_WARP]; - for( unsigned i=0; iGetRow() == iWarpRow ) - { - return ToWarp(warps[i])->GetLength(); - } - } - return 0; -} - -float TimingData::GetSpeedPercentAtRow( int iRow ) -{ - return GetSpeedSegmentAtRow( iRow )->GetRatio(); -} - -float TimingData::GetSpeedWaitAtRow( int iRow ) -{ - return GetSpeedSegmentAtRow( iRow )->GetDelay(); -} - -SpeedSegment::BaseUnit TimingData::GetSpeedModeAtRow( int iRow ) -{ - return GetSpeedSegmentAtRow( iRow )->GetUnit(); -} - -float TimingData::GetScrollAtRow( int iRow ) -{ - return GetScrollSegmentAtRow( iRow )->GetRatio(); -} - -float TimingData::GetFakeAtRow( int iFakeRow ) const -{ - const vector &fakes = m_avpTimingSegments[SEGMENT_FAKE]; - for( unsigned i=0; iGetRow() == iFakeRow ) - { - return ToFake(fakes[i])->GetLength(); - } - } - return 0; -} +}; // Multiply the BPM in the range [fStartBeat,fEndBeat) by fFactor. void TimingData::MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor ) @@ -658,7 +156,6 @@ void TimingData::MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float f * split it into two. */ if( iStartIndexThisSegment < iStartIndex && iStartIndexNextSegment > iStartIndex ) { - BPMSegment * b = new BPMSegment(iStartIndexNextSegment, bs->GetBPS()); bpms.insert(bpms.begin()+i+1, b); @@ -682,19 +179,9 @@ void TimingData::MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float f } } -float TimingData::GetBPMAtRow( int iNoteRow ) const -{ - unsigned i; - const vector &bpms = m_avpTimingSegments[SEGMENT_BPM]; - for( i=0; iGetRow() > iNoteRow ) - break; - return ToBPM(bpms[i])->GetBPM(); -} - bool TimingData::IsWarpAtRow( int iNoteRow ) const { - const vector &warps = m_avpTimingSegments[SEGMENT_WARP]; + const vector &warps = GetTimingSegments(SEGMENT_WARP); if( warps.empty() ) return false; @@ -704,7 +191,7 @@ bool TimingData::IsWarpAtRow( int iNoteRow ) const if( s->GetBeat() <= beatRow && beatRow < (s->GetBeat() + s->GetLength() ) ) { // Allow stops inside warps to allow things like stop, warp, stop, warp, stop, and so on. - if( m_avpTimingSegments[SEGMENT_STOP].empty() && m_avpTimingSegments[SEGMENT_DELAY].empty() ) + if( GetTimingSegments(SEGMENT_STOP).empty() && GetTimingSegments(SEGMENT_DELAY).empty() ) { return true; } @@ -719,7 +206,7 @@ bool TimingData::IsWarpAtRow( int iNoteRow ) const bool TimingData::IsFakeAtRow( int iNoteRow ) const { - const vector &fakes = m_avpTimingSegments[SEGMENT_FAKE]; + const vector &fakes = GetTimingSegments(SEGMENT_FAKE); if( fakes.empty() ) return false; @@ -733,52 +220,167 @@ bool TimingData::IsFakeAtRow( int iNoteRow ) const return false; } -// TODO: make this part of the TimingSegment struct -enum SegmentEffectArea +/* DummySegments: since our model relies on being able to get a segment at will, + * whether one exists or not, we have a bunch of dummies to return if there is + * no segment. It's kind of kludgy, but when we have functions making + * indiscriminate calls to get segments at arbitrary rows, I think it's the + * best solution we've got for now. + * + * Note that types whose SegmentEffectAreas are "Indefinite" are NULL here, + * because they should never need to be used; we always have at least one such + * segment in the TimingData, and if not, we'll crash anyway. -- vyhd */ +static const TimingSegment* DummySegments[NUM_TimingSegmentType] = { - EffectArea_Row, // takes effect on a single row - EffectArea_Range, // takes effect until the next segment of its type - NUM_EffectArea, - EffectArea_Invalid, + NULL, // BPMSegment + new StopSegment, + new DelaySegment, + NULL, // TimeSignatureSegment + new WarpSegment, + NULL, // LabelSegment + NULL, // TickcountSegment + NULL, // ComboSegment + NULL, // SpeedSegment + NULL, // ScrollSegment + new FakeSegment }; -TimingSegment* TimingData::GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ) +const TimingSegment* TimingData::GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ) const { - vector vSegments = m_avpTimingSegments[tst]; + const vector &vSegments = GetTimingSegments(tst); if( vSegments.empty() ) - FAIL_M( ssprintf("GetSegmentAtRow: %s is empty (blame vyhd)", TimingSegmentTypeToString(tst).c_str()) ); + return DummySegments[tst]; - unsigned index = -1; + int index = GetSegmentIndexAtRow( tst, iNoteRow ); + const TimingSegment *seg = vSegments[index]; + + switch( seg->GetEffectType() ) + { + case SegmentEffectType_Indefinite: + { + // this segment is in effect at this row + return seg; + } + default: + { + // if the returned segment isn't exactly on this row, + // we don't want it, return a dummy instead + if( seg->GetRow() == iNoteRow ) + return seg; + else + return DummySegments[tst]; + } + } + + ASSERT( 0 ); +} + +TimingSegment* GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ) +{ + return const_cast( GetSegmentAtRow(iNoteRow, tst) ); +} + +static void EraseSegment( vector &vSegs, int index, TimingSegment *cur ) +{ + LOG->Trace( "EraseSegment(%d, %p)", index, cur ); + cur->DebugPrint(); + + vSegs.erase( vSegs.begin() + index ); + SAFE_DELETE( cur ); +} + +// NOTE: the pointer we're passed is a reference to a temporary, +// so we must deep-copy it (with ::Copy) for new allocations. +void TimingData::AddSegment( const TimingSegment *seg ) +{ + LOG->Trace( "AddSegment( %s )", TimingSegmentTypeToString(seg->GetType()).c_str() ); + seg->DebugPrint(); + + TimingSegmentType tst = seg->GetType(); + vector &vSegs = m_avpTimingSegments[tst]; + + // OPTIMIZATION: if this is our first segment, push and return. + if( vSegs.empty() ) + { + vSegs.push_back( seg->Copy() ); + return; + } + + int index = GetSegmentIndexAtRow( tst, seg->GetRow() ); + ASSERT( index != INVALID_INDEX ); + TimingSegment *cur = vSegs[index]; + + bool bIsNotable = seg->IsNotable(); + bool bOnSameRow = seg->GetRow() == cur->GetRow(); + + // ignore changes that are zero and don't overwrite an existing segment + if( !bIsNotable && !bOnSameRow ) + return; + + switch( seg->GetEffectType() ) + { + case SegmentEffectType_Row: + case SegmentEffectType_Range: + { + // if we're overwriting a change with a non-notable + // one, take it to mean deleting the existing segment + if( bOnSameRow && !bIsNotable ) + { + EraseSegment( vSegs, index, cur ); + return; + } - // seek to the last segment before this row - for( index = 0; index < vSegments.size() - 1; ++index ) - if( iNoteRow < vSegments[index+1]->GetRow() ) break; + } + case SegmentEffectType_Indefinite: + { + TimingSegment *prev = cur; - return vSegments[index]; + // get the segment before last; if we're on the same + // row, get the segment in effect before 'cur' + if( bOnSameRow && index > 0 ) + prev = vSegs[index - 1]; + + // if true, this is redundant segment change + if( (*prev) == (*seg) ) + { + EraseSegment( vSegs, index, cur ); + return; + } + + break; + } + } + + // the segment at or before this row is equal to the new one; ignore it + if( bOnSameRow && (*cur) == (*seg) ) + { + LOG->Trace( "equals previous segment, ignoring" ); + return; + } + + // Copy() the segment (which allocates a new segment), assign it + // to the position of the old one, then delete the old pointer. + TimingSegment *cpy = seg->Copy(); + + if( bOnSameRow ) + { + // delete the existing pointer and replace it + SAFE_DELETE( cur ); + vSegs[index] = cpy; + } + else + { + // copy and insert a new segment + vector::iterator it; + it = upper_bound( vSegs.begin(), vSegs.end(), cpy, ts_less() ); + vSegs.insert( it, cpy ); + } } -int TimingData::GetTimeSignatureNumeratorAtRow( int iRow ) +bool TimingData::DoesLabelExist( const RString& sLabel ) const { - return GetTimeSignatureSegmentAtRow( iRow )->GetNum(); -} - -int TimingData::GetTimeSignatureDenominatorAtRow( int iRow ) -{ - return GetTimeSignatureSegmentAtRow( iRow )->GetDen(); -} - -int TimingData::GetTickcountAtRow( int iRow ) const -{ - const vector &ticks = m_avpTimingSegments[SEGMENT_TICKCOUNT]; - const int index = GetSegmentIndexAtRow( SEGMENT_TICKCOUNT, iRow ); - return ToTickcount(ticks[index])->GetTicks(); -} - -bool TimingData::DoesLabelExist( RString sLabel ) const -{ - const vector &labels = m_avpTimingSegments[SEGMENT_LABEL]; + const vector &labels = GetTimingSegments(SEGMENT_LABEL); for (unsigned i = 0; i < labels.size(); i++) { if (ToLabel(labels[i])->GetLabel() == sLabel) @@ -813,19 +415,19 @@ void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float 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( ;; ) { int iEventRow = INT_MAX; @@ -1063,25 +665,21 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool ASSERT( fScale > 0 ); ASSERT( iStartIndex >= 0 ); ASSERT( iStartIndex < iEndIndex ); - + int length = iEndIndex - iStartIndex; int newLength = lrintf( fScale * length ); - - for (unsigned i = 0; i < NUM_TimingSegmentType; i++) - { - for (unsigned j = 0; j < m_avpTimingSegments[i].size(); j++) - { - m_avpTimingSegments[i][j]->Scale(iStartIndex, length, newLength); - } - } - + + FOREACH_TimingSegmentType( tst ) + for (unsigned j = 0; j < m_avpTimingSegments[tst].size(); j++) + m_avpTimingSegments[tst][j]->Scale(iStartIndex, length, newLength); + // adjust BPM changes to preserve timing if( bAdjustBPM ) { int iNewEndIndex = iStartIndex + newLength; float fEndBPMBeforeScaling = GetBPMAtRow(iNewEndIndex); vector &bpms = m_avpTimingSegments[SEGMENT_BPM]; - + // adjust BPM changes "between" iStartIndex and iNewEndIndex for ( unsigned i = 0; i < bpms.size(); i++ ) { @@ -1094,19 +692,19 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool else bpm->SetBPM( bpm->GetBPM() * fScale ); } - + // set BPM at iStartIndex and iNewEndIndex. SetBPMAtRow( iStartIndex, GetBPMAtRow(iStartIndex) * fScale ); SetBPMAtRow( iNewEndIndex, fEndBPMBeforeScaling ); } - + } void TimingData::InsertRows( int iStartRow, int iRowsToAdd ) { - for (unsigned i = 0; i < NUM_TimingSegmentType; i++) + FOREACH_TimingSegmentType( tst ) { - vector &segs = m_avpTimingSegments[i]; + vector &segs = m_avpTimingSegments[tst]; for (unsigned j = 0; j < segs.size(); j++) { TimingSegment *seg = segs[j]; @@ -1134,16 +732,15 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) /* We're moving rows up. Delete any BPM changes and stops in the region * being deleted. */ - for (unsigned i = 0; i < NUM_TimingSegmentType; i++) + FOREACH_TimingSegmentType( tst ) { - vector &segs = m_avpTimingSegments[i]; + vector &segs = m_avpTimingSegments[tst]; for (unsigned j = 0; j < segs.size(); j++) { TimingSegment *seg = segs[j]; // Before deleted region: if (seg->GetRow() < iStartRow) continue; - // Inside deleted region: if (seg->GetRow() < iStartRow + iRowsToDelete) { @@ -1151,12 +748,12 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete ) --j; continue; } - + // After deleted regions: seg->SetRow(seg->GetRow() - iRowsToDelete); } } - + SetBPMAtRow( iStartRow, fNewBPM ); } @@ -1166,50 +763,56 @@ float TimingData::GetDisplayedSpeedPercent( float fSongBeat, float fMusicSeconds * TimingData to work with. This seems to happen the most upon * leaving the editor. Still, cover our butts in case this instance * isn't existing. */ - if (!this) return 1.0f; - - const vector &speeds = m_avpTimingSegments[SEGMENT_SPEED]; + /* ...but force a crash, so debuggers will catch it and stop here. + * That'll make us keep this bug in mind. -- vyhd */ + if( !this ) + { + DEBUG_ASSERT( this ); + return 1.0f; + } + + const vector &speeds = GetTimingSegments(SEGMENT_SPEED); if( speeds.size() == 0 ) return 1.0f; const int index = GetSegmentIndexAtBeat( SEGMENT_SPEED, fSongBeat ); - + const SpeedSegment *seg = ToSpeed(speeds[index]); float fStartBeat = seg->GetBeat(); float fStartTime = GetElapsedTimeFromBeat( fStartBeat ) - GetDelayAtBeat( fStartBeat ); float fEndTime; float fCurTime = fMusicSeconds; - - if( seg->GetUnit() == 1 ) // seconds + + if( seg->GetUnit() == SpeedSegment::UNIT_SECONDS ) { fEndTime = fStartTime + seg->GetDelay(); } else { - fEndTime = GetElapsedTimeFromBeat( fStartBeat + seg->GetDelay() ) - - GetDelayAtBeat( fStartBeat + seg->GetDelay() ); + fEndTime = GetElapsedTimeFromBeat( fStartBeat + seg->GetDelay() ) + - GetDelayAtBeat( fStartBeat + seg->GetDelay() ); } - + SpeedSegment *first = ToSpeed(speeds[0]); - + if( ( index == 0 && first->GetDelay() > 0.0 ) && fCurTime < fStartTime ) { return 1.0f; } else if( fEndTime >= fCurTime && ( index > 0 || first->GetDelay() > 0.0 ) ) { - const float fPriorSpeed = (index == 0 ? - 1 : - ToSpeed(speeds[index - 1])->GetRatio() ); + const float fPriorSpeed = (index == 0) ? 1 : + ToSpeed(speeds[index-1])->GetRatio(); + float fTimeUsed = fCurTime - fStartTime; float fDuration = fEndTime - fStartTime; float fRatioUsed = fDuration == 0.0 ? 1 : fTimeUsed / fDuration; - + float fDistance = fPriorSpeed - seg->GetRatio(); float fRatioNeed = fRatioUsed * -fDistance; return (fPriorSpeed + fRatioNeed); } - else + else { return seg->GetRatio(); } @@ -1223,105 +826,68 @@ void TimingData::TidyUpData() if( segs[SEGMENT_BPM].empty() ) { LOG->UserLog( "Song file", m_sFile, "has no BPM segments, default provided." ); - - AddSegment( SEGMENT_BPM, new BPMSegment(0, 60) ); + AddSegment( BPMSegment(0, 60) ); } // Make sure the first BPM segment starts at beat 0. if( segs[SEGMENT_BPM][0]->GetRow() != 0 ) segs[SEGMENT_BPM][0]->SetRow(0); - // If no time signature specified, assume 4/4 time for the whole song. + // If no time signature specified, assume default time for the whole song. if( segs[SEGMENT_TIME_SIG].empty() ) - { - segs[SEGMENT_TIME_SIG].push_back( new TimeSignatureSegment(0, 4, 4) ); - } - + AddSegment( TimeSignatureSegment(0) ); + // Likewise, if no tickcount signature is specified, assume 4 ticks - //per beat for the entire song. The default of 4 is chosen more - //for compatibility with the main Pump series than anything else. + // per beat for the entire song. The default of 4 is chosen more + // for compatibility with the main Pump series than anything else. + // (TickcountSegment's constructor handles that now. -- vyhd) if( segs[SEGMENT_TICKCOUNT].empty() ) - { - segs[SEGMENT_TICKCOUNT].push_back( new TickcountSegment(0, 4) ); - } - + AddSegment( TickcountSegment(0) ); + // Have a default combo segment of one just in case. if( segs[SEGMENT_COMBO].empty() ) - { - segs[SEGMENT_COMBO].push_back( new ComboSegment(0, 1, 1) ); - } - + AddSegment( ComboSegment(0) ); + // Have a default label segment just in case. if( segs[SEGMENT_LABEL].empty() ) - { - segs[SEGMENT_LABEL].push_back( new LabelSegment(0, "Song Start") ); - } - + AddSegment( LabelSegment(0, "Song Start") ); + // Always be sure there is a starting speed. if( segs[SEGMENT_SPEED].empty() ) - { - segs[SEGMENT_SPEED].push_back( new SpeedSegment(0, 1, 0) ); - } - + AddSegment( SpeedSegment(0) ); + // Always be sure there is a starting scrolling factor. if( segs[SEGMENT_SCROLL].empty() ) - { - segs[SEGMENT_SCROLL].push_back( new ScrollSegment(0, 1) ); - } + AddSegment( ScrollSegment(0) ); } - - - - -bool TimingData::HasBpmChanges() const +void TimingData::SortSegments( TimingSegmentType tst ) { - return m_avpTimingSegments[SEGMENT_BPM].size()>1; -} - -bool TimingData::HasStops() const -{ - return m_avpTimingSegments[SEGMENT_STOP].size()>0; -} - -bool TimingData::HasDelays() const -{ - return m_avpTimingSegments[SEGMENT_DELAY].size()>0; -} - -bool TimingData::HasWarps() const -{ - return m_avpTimingSegments[SEGMENT_WARP].size()>0; -} - -bool TimingData::HasFakes() const -{ - return m_avpTimingSegments[SEGMENT_FAKE].size()>0; + vector &vSegments = m_avpTimingSegments[tst]; + sort( vSegments.begin(), vSegments.end() ); } bool TimingData::HasSpeedChanges() const { - const vector &speeds = m_avpTimingSegments[SEGMENT_SPEED]; - return (speeds.size()>1 || - ToSpeed(speeds[0])->GetRatio() != 1); + const vector &speeds = GetTimingSegments(SEGMENT_SPEED); + return (speeds.size()>1 || ToSpeed(speeds[0])->GetRatio() != 1); } bool TimingData::HasScrollChanges() const { - const vector &scrolls = m_avpTimingSegments[SEGMENT_SCROLL]; - return (scrolls.size()>1 || - ToScroll(scrolls[0])->GetRatio() != 1); + const vector &scrolls = GetTimingSegments(SEGMENT_SCROLL); + return (scrolls.size()>1 || ToScroll(scrolls[0])->GetRatio() != 1); } void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const { iMeasureIndexOut = 0; - const vector &tSigs = m_avpTimingSegments[SEGMENT_TIME_SIG]; + const vector &tSigs = GetTimingSegments(SEGMENT_TIME_SIG); for (unsigned i = 0; i < tSigs.size(); i++) { TimeSignatureSegment *curSig = ToTimeSignature(tSigs[i]); int iSegmentEndRow = (i + 1 == tSigs.size()) ? INT_MAX : curSig->GetRow(); - + int iRowsPerMeasureThisSegment = curSig->GetNoteRowsPerMeasure(); if( iNoteRow >= curSig->GetRow() ) @@ -1350,9 +916,9 @@ void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, i vector TimingData::ToVectorString(TimingSegmentType tst, int dec) const { - const vector segs = m_avpTimingSegments[tst]; + const vector segs = GetTimingSegments(tst); vector ret; - + for (unsigned i = 0; i < segs.size(); i++) { ret.push_back(segs[i]->ToString(dec)); @@ -1363,7 +929,7 @@ vector TimingData::ToVectorString(TimingSegmentType tst, int dec) const // lua start #include "LuaBinding.h" -/** @brief Allow Lua to have access to the TimingData. */ +/** @brief Allow Lua to have access to the TimingData. */ class LunaTimingData: public Luna { public: @@ -1422,13 +988,10 @@ public: static int GetBPMs( T* p, lua_State *L ) { vector vBPMs; - vector &bpms = p->m_avpTimingSegments[SEGMENT_BPM]; + const vector &bpms = p->GetTimingSegments(SEGMENT_BPM); + for (unsigned i = 0; i < bpms.size(); i++) - { - BPMSegment *seg = ToBPM(bpms[i]); - const float fBPM = seg->GetBPM(); - vBPMs.push_back( fBPM ); - } + vBPMs.push_back( ToBPM(bpms[i])->GetBPM() ); LuaHelpers::CreateTableFromArray(vBPMs, L); return 1; diff --git a/src/TimingData.h b/src/TimingData.h index 106534f690..67504b3e35 100644 --- a/src/TimingData.h +++ b/src/TimingData.h @@ -10,16 +10,29 @@ struct lua_State; /** @brief Compare a TimingData segment's properties with one another. */ #define COMPARE(x) if(this->x!=other.x) return false; - /* convenience functions to handle static casting */ -template T* ToDerived( TimingSegment *t, TimingSegmentType tst ) +template +inline T ToDerived( const TimingSegment *t, TimingSegmentType tst ) { - ASSERT( t->GetType() == tst ); // type checking - return static_cast( t ); + ASSERT_M( t && tst == t->GetType(), + ssprintf("type mismatch (expected %s, got %s)", + TimingSegmentTypeToString(tst).c_str(), + TimingSegmentTypeToString(t->GetType()).c_str() ) ); + + return static_cast( t ); } #define TimingSegmentToXWithName(Seg, SegName, SegType) \ - inline Seg* To##SegName( TimingSegment *t ) { return ToDerived(t, SegType); } + inline const Seg* To##SegName( const TimingSegment *t ) \ + { \ + ASSERT( t->GetType() == SegType ); \ + return static_cast( t ); \ + } \ + inline Seg* To##SegName( TimingSegment *t ) \ + { \ + ASSERT( t->GetType() == SegType ); \ + return static_cast( t ); \ + } #define TimingSegmentToX(Seg, SegType) \ TimingSegmentToXWithName(Seg##Segment, Seg, SEGMENT_##SegType) @@ -46,10 +59,25 @@ TimingSegmentToX( Fake, FAKE ); class TimingData { public: - void AddSegment(TimingSegmentType tst, TimingSegment * seg); + /** + * @brief Sets up initial timing data with a defined offset. + * @param fOffset the offset from the 0th beat. */ + TimingData( float fOffset = 0 ); + ~TimingData(); - unsigned GetSegmentIndexAtRow(TimingSegmentType tst, int row) const; - unsigned GetSegmentIndexAtBeat(TimingSegmentType tst, float beat) const + TimingData( const TimingData& rhs ) : m_sFile(rhs.m_sFile), + m_fBeat0OffsetInSeconds(rhs.m_fBeat0OffsetInSeconds) + { + const vector* avpSegs = rhs.m_avpTimingSegments; + + // deep-copy the TimingSegment pointers + FOREACH_TimingSegmentType( tst ) + for( unsigned i = 0; i < avpSegs[tst].size(); ++i ) + m_avpTimingSegments[tst].push_back( avpSegs[tst][i]->Copy() ); + } + + int GetSegmentIndexAtRow(TimingSegmentType tst, int row) const; + int GetSegmentIndexAtBeat(TimingSegmentType tst, float beat) const { return GetSegmentIndexAtRow( tst, BeatToNoteRow(beat) ); } @@ -68,12 +96,6 @@ public: bool empty() const; - /** - * @brief Sets up initial timing data with a defined offset. - * @param fOffset the offset from the 0th beat. */ - TimingData(float fOffset = 0); - ~TimingData(); - TimingData CopyRange(int startRow, int endRow) const; /** * @brief Gets the actual BPM of the song, @@ -85,30 +107,6 @@ public: * @param highest the highest allowed max BPM. */ void GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut, float highest = FLT_MAX ) const; - /** - * @brief Retrieve the BPM at the given row. - * @param iNoteRow the row in question. - * @return the BPM. - */ - float GetBPMAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the BPM at the given beat. - * @param fBeat the beat in question. - * @return the BPM. - */ - float GetBPMAtBeat( float fBeat ) const { return GetBPMAtRow( BeatToNoteRow(fBeat)); } - /** - * @brief Set the row to have the new BPM. - * @param iNoteRow the row to have the new BPM. - * @param fBPM the BPM. - */ - void SetBPMAtRow( int iNoteRow, float fBPM ); - /** - * @brief Set the beat to have the new BPM. - * @param fBeat the beat to have the new BPM. - * @param fBPM the BPM. - */ - void SetBPMAtBeat( float fBeat, float fBPM ) { SetBPMAtRow( BeatToNoteRow(fBeat), fBPM ); } /** * @brief Retrieve the TimingSegment at the specified row. @@ -116,6 +114,7 @@ public: * @param tst the TimingSegmentType requested. * @return the segment in question. */ + const TimingSegment* GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ) const; TimingSegment* GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ); /** @@ -124,511 +123,176 @@ public: * @param tst the TimingSegmentType requested. * @return the segment in question. */ - TimingSegment* GetSegmentAtBeat( float fBeat, TimingSegmentType tst ) + const TimingSegment* GetSegmentAtBeat( float fBeat, TimingSegmentType tst ) const { return GetSegmentAtRow( BeatToNoteRow(fBeat), tst ); } + TimingSegment* GetSegmentAtBeat( float fBeat, TimingSegmentType tst ) + { + return const_cast( GetSegmentAtBeat(fBeat, tst) ); + } - void SetTimingSegmentAtRow( TimingSegment *seg, int iNoteRow ); - - - /* XXX: convenience shortcuts. We should get rid of these later. */ - #define GetAndSetSegmentWithName(Seg, SegName, SegType) \ + #define DefineSegmentWithName(Seg, SegName, SegType) \ + const Seg* Get##Seg##AtRow( int iNoteRow ) const \ + { \ + const TimingSegment *t = GetSegmentAtRow( iNoteRow, SegType ); \ + return To##SegName( t ); \ + } \ Seg* Get##Seg##AtRow( int iNoteRow ) \ { \ - TimingSegment *t = GetSegmentAtRow( iNoteRow, SegType ); \ - return To##SegName( t ); \ + return const_cast (((const TimingData*)this)->Get##Seg##AtRow(iNoteRow) ); \ + } \ + const Seg* Get##Seg##AtBeat( float fBeat ) const \ + { \ + return Get##Seg##AtRow( BeatToNoteRow(fBeat) ); \ } \ Seg* Get##Seg##AtBeat( float fBeat ) \ { \ - TimingSegment *t = GetSegmentAtBeat( fBeat, SegType ); \ - return To##SegName( t ); \ + return const_cast (((const TimingData*)this)->Get##Seg##AtBeat(fBeat) ); \ } \ - void Set##SegName##AtRow( Seg &seg, int iNoteRow ) \ + void AddSegment( const Seg &seg ) \ { \ - SetTimingSegmentAtRow( &seg, iNoteRow ); \ + AddSegment( &seg ); \ } + // "XXX: this comment (and quote mark) exists so nano won't + // display the rest of this file as one giant string + // (TimeSignature,TIME_SIG) -> (TimeSignatureSegment,SEGMENT_TIME_SIG) - #define GetAndSetSegment(Seg, SegType ) \ - GetAndSetSegmentWithName( Seg##Segment, Seg, SEGMENT_##SegType ) + #define DefineSegment(Seg, SegType ) \ + DefineSegmentWithName( Seg##Segment, Seg, SEGMENT_##SegType ) - GetAndSetSegment( BPM, BPM ); - GetAndSetSegment( Stop, STOP ); - GetAndSetSegment( Delay, DELAY ); - GetAndSetSegment( Warp, WARP ); - GetAndSetSegment( Label, LABEL ); - GetAndSetSegment( Tickcount, TICKCOUNT ); - GetAndSetSegment( Combo, COMBO ); - GetAndSetSegment( Speed, SPEED ); - GetAndSetSegment( Scroll, SCROLL ); - GetAndSetSegment( Fake, FAKE ); - GetAndSetSegment( TimeSignature, TIME_SIG ); + DefineSegment( BPM, BPM ); + DefineSegment( Stop, STOP ); + DefineSegment( Delay, DELAY ); + DefineSegment( Warp, WARP ); + DefineSegment( Label, LABEL ); + DefineSegment( Tickcount, TICKCOUNT ); + DefineSegment( Combo, COMBO ); + DefineSegment( Speed, SPEED ); + DefineSegment( Scroll, SCROLL ); + DefineSegment( Fake, FAKE ); + DefineSegment( TimeSignature, TIME_SIG ); - #undef GetAndSetSegmentWithName - #undef GetAndSetSegment + #undef DefineSegmentWithName + #undef DefineSegment + /* convenience aliases (Set functions are deprecated) */ + float GetBPMAtRow( int iNoteRow ) const { return GetBPMSegmentAtRow(iNoteRow)->GetBPM(); } + float GetBPMAtBeat( float fBeat ) const { return GetBPMAtRow( BeatToNoteRow(fBeat) ); } + void SetBPMAtRow( int iNoteRow, float fBPM ) { AddSegment( BPMSegment(iNoteRow, fBPM) ); } + void SetBPMAtBeat( float fBeat, float fBPM ) { SetBPMAtRow( BeatToNoteRow(fBeat), fBPM ); } - /** - * @brief Retrieve the stop time at the given row. - * @param iNoteRow the row in question. - * @return the stop time. - */ - float GetStopAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the stop time at the given beat. - * @param fBeat the beat in question. - * @return the stop time. - */ + float GetStopAtRow( int iNoteRow ) const { return GetStopSegmentAtRow(iNoteRow)->GetPause(); } float GetStopAtBeat( float fBeat ) const { return GetStopAtRow( BeatToNoteRow(fBeat) ); } + void SetStopAtRow( int iNoteRow, float fSeconds ) { AddSegment( StopSegment(iNoteRow, fSeconds) ); } + void SetStopAtBeat( float fBeat, float fSeconds ) { SetStopAtRow( BeatToNoteRow(fBeat), fSeconds ); } - /** - * @brief Set the row to have the new stop time. - * @param iNoteRow the row to have the new stop time. - * @param fSeconds the new stop time. - */ - void SetStopAtRow( int iNoteRow, float fSeconds ); - /** - * @brief Set the beat to have the new stop time. - * @param fBeat to have the new stop time. - * @param fSeconds the new stop time. - */ - void SetStopAtBeat( float fBeat, float fSeconds ) { SetStopAtRow( BeatToNoteRow(fBeat), fSeconds); } - - /** - * @brief Retrieve the delay time at the given row. - * @param iNoteRow the row in question. - * @return the delay time. - */ - float GetDelayAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the delay time at the given beat. - * @param fBeat the beat in question. - * @return the delay time. - */ + float GetDelayAtRow( int iNoteRow ) const { return GetDelaySegmentAtRow(iNoteRow)->GetPause(); } float GetDelayAtBeat( float fBeat ) const { return GetDelayAtRow( BeatToNoteRow(fBeat) ); } + void SetDelayAtRow( int iNoteRow, float fSeconds ) { AddSegment( DelaySegment(iNoteRow, fSeconds) ); } + void SetDelayAtBeat( float fBeat, float fSeconds ) { SetDelayAtRow( BeatToNoteRow(fBeat), fSeconds ); } - /** - * @brief Set the row to have the new delay time. - * - * This function was added specifically for sm-ssc. - * @param iNoteRow the row to have the new delay time. - * @param fSeconds the new delay time. - */ - void SetDelayAtRow( int iNoteRow, float fSeconds ); - /** - * @brief Set the beat to have the new delay time. - * - * This function was added specifically for sm-ssc. - * @param fBeat the beat to have the new delay time. - * @param fSeconds the new delay time. - */ - void SetDelayAtBeat( float fBeat, float fSeconds ) { SetDelayAtRow( BeatToNoteRow(fBeat), fSeconds); } + void SetTimeSignatureAtRow( int iNoteRow, int iNum, int iDen ) + { + AddSegment( TimeSignatureSegment(iNoteRow, iNum, iDen) ); + } - /** - * @brief Retrieve the Time Signature's numerator at the given row. - * @param iNoteRow the row in question. - * @return the numerator. - */ - int GetTimeSignatureNumeratorAtRow( int iNoteRow ); - /** - * @brief Retrieve the Time Signature's numerator at the given beat. - * @param fBeat the beat in question. - * @return the numerator. - */ - int GetTimeSignatureNumeratorAtBeat( float fBeat ) { return GetTimeSignatureNumeratorAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Retrieve the Time Signature's denominator at the given row. - * @param iNoteRow the row in question. - * @return the denominator. - */ - int GetTimeSignatureDenominatorAtRow( int iNoteRow ); - /** - * @brief Retrieve the Time Signature's denominator at the given beat. - * @param fBeat the beat in question. - * @return the denominator. - */ - int GetTimeSignatureDenominatorAtBeat( float fBeat ) { return GetTimeSignatureDenominatorAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Set the row to have the new Time Signature. - * @param iNoteRow the row to have the new Time Signature. - * @param iNumerator the numerator. - * @param iDenominator the denominator. - */ - void SetTimeSignatureAtRow( int iNoteRow, int iNumerator, int iDenominator ); - /** - * @brief Set the beat to have the new Time Signature. - * @param fBeat the beat to have the new Time Signature. - * @param iNumerator the numerator. - * @param iDenominator the denominator. - */ - void SetTimeSignatureAtBeat( float fBeat, int iNumerator, int iDenominator ) { SetTimeSignatureAtRow( BeatToNoteRow(fBeat), iNumerator, iDenominator ); } - /** - * @brief Set the row to have the new Time Signature numerator. - * @param iNoteRow the row to have the new Time Signature numerator. - * @param iNumerator the numerator. - */ - void SetTimeSignatureNumeratorAtRow( int iNoteRow, int iNumerator ); - /** - * @brief Set the beat to have the new Time Signature numerator. - * @param fBeat the beat to have the new Time Signature numerator. - * @param iNumerator the numerator. - */ - void SetTimeSignatureNumeratorAtBeat( float fBeat, int iNumerator ) { SetTimeSignatureNumeratorAtRow( BeatToNoteRow(fBeat), iNumerator); } - /** - * @brief Set the row to have the new Time Signature denominator. - * @param iNoteRow the row to have the new Time Signature denominator. - * @param iDenominator the denominator. - */ - void SetTimeSignatureDenominatorAtRow( int iNoteRow, int iDenominator ); - /** - * @brief Set the beat to have the new Time Signature denominator. - * @param fBeat the beat to have the new Time Signature denominator. - * @param iDenominator the denominator. - */ - void SetTimeSignatureDenominatorAtBeat( float fBeat, int iDenominator ) { SetTimeSignatureDenominatorAtRow( BeatToNoteRow(fBeat), iDenominator); } + void SetTimeSignatureAtBeat( float fBeat, int iNum, int iDen ) + { + SetTimeSignatureAtRow( BeatToNoteRow(fBeat), iNum, iDen ); + } - /** - * @brief Determine the beat to warp to. - * @param iRow The row you start on. - * @return the beat you warp to. - */ - float GetWarpAtRow( int iRow ) const; - /** - * @brief Determine the beat to warp to. - * @param fBeat The beat you start on. - * @return the beat you warp to. - */ - float GetWarpAtBeat( float fBeat ) const { return GetWarpAtRow( BeatToNoteRow( fBeat ) ); } - /** - * @brief Set the beat to warp to given a starting row. - * @param iRow The row to start on. - * @param fNew The destination beat. - */ - void SetWarpAtRow( int iRow, float fNew ); - /** - * @brief Set the beat to warp to given a starting beat. - * @param fBeat The beat to start on. - * @param fNew The destination beat. - */ - void SetWarpAtBeat( float fBeat, float fNew ) { SetWarpAtRow( BeatToNoteRow( fBeat ), fNew ); } + float GetWarpAtRow( int iNoteRow ) const { return GetWarpSegmentAtRow(iNoteRow)->GetLength(); } + float GetWarpAtBeat( float fBeat ) const { return GetWarpAtRow( BeatToNoteRow(fBeat) ); } + /* Note: fLength is in beats, not rows */ + void SetWarpAtRow( int iRow, float fLength ) { AddSegment( WarpSegment(iRow, fLength) ); } + void SetWarpAtBeat( float fBeat, float fLength ) { AddSegment( WarpSegment(BeatToNoteRow(fBeat), fLength) ); } - /** - * @brief Checks if the row is inside a warp. - * @param iRow the row to focus on. - * @return true if the row is inside a warp, false otherwise. - */ - bool IsWarpAtRow( int iRow ) const; - /** - * @brief Checks if the beat is inside a warp. - * @param fBeat the beat to focus on. - * @return true if the row is inside a warp, false otherwise. - */ - bool IsWarpAtBeat( float fBeat ) const { return IsWarpAtRow( BeatToNoteRow( fBeat ) ); } - - /** - * @brief Retrieve the Tickcount at the given row. - * @param iNoteRow the row in question. - * @return the Tickcount. - */ - int GetTickcountAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the Tickcount at the given beat. - * @param fBeat the beat in question. - * @return the Tickcount. - */ + int GetTickcountAtRow( int iNoteRow ) const { return GetTickcountSegmentAtRow(iNoteRow)->GetTicks(); } int GetTickcountAtBeat( float fBeat ) const { return GetTickcountAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Set the row to have the new tickcount. - * @param iNoteRow the row to have the new tickcount. - * @param iTicks the tickcount. - */ - void SetTickcountAtRow( int iNoteRow, int iTicks ); - /** - * @brief Set the beat to have the new tickcount. - * @param fBeat the beat to have the new tickcount. - * @param iTicks the tickcount. - */ + void SetTickcountAtRow( int iNoteRow, int iTicks ) { AddSegment( TickcountSegment(iNoteRow, iTicks) ); } void SetTickcountAtBeat( float fBeat, int iTicks ) { SetTickcountAtRow( BeatToNoteRow( fBeat ), iTicks ); } - /** - * @brief Retrieve the Combo at the given row. - * @param iNoteRow the row in question. - * @return the Combo. - */ - int GetComboAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the Combo at the given beat. - * @param fBeat the beat in question. - * @return the Combo. - */ + int GetComboAtRow( int iNoteRow ) const { return GetComboSegmentAtRow(iNoteRow)->GetCombo(); } int GetComboAtBeat( float fBeat ) const { return GetComboAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Retrieve the Miss Combo at the given row. - * @param iNoteRow the row in question. - * @return the Miss Combo. - */ - int GetMissComboAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the Miss Combo at the given beat. - * @param fBeat the beat in question. - * @return the Miss Combo. - */ + int GetMissComboAtRow( int iNoteRow ) const { return GetComboSegmentAtRow(iNoteRow)->GetMissCombo(); } int GetMissComboAtBeat( float fBeat ) const { return GetMissComboAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Set the row to have the new Combo. - * @param iNoteRow the row to have the new Combo. - * @param iCombo the Combo. - */ - void SetComboAtRow( int iNoteRow, int iCombo ); - /** - * @brief Set the beat to have the new Combo. - * @param fBeat the beat to have the new Combo. - * @param iCombo the Combo. - */ - void SetComboAtBeat( float fBeat, int iCombo ) { SetComboAtRow( BeatToNoteRow( fBeat ), iCombo ); } - /** - * @brief Set the row to have the new Combo and Miss Combo. - * @param iNoteRow the row to have the new Combo and Miss Combo. - * @param iCombo the Combo. - * @param iMiss the Miss Combo. - */ - void SetComboAtRow( int iNoteRow, int iCombo, int iMiss ); - /** - * @brief Set the beat to have the new Combo and Miss Combo. - * @param fBeat the beat to have the new Combo and Miss Combo. - * @param iCombo the Combo. - * @param iMiss the Miss Combo. - */ - void SetComboAtBeat( float fBeat, int iCombo, int iMiss ) { SetComboAtRow( BeatToNoteRow( fBeat ), iCombo, iMiss ); } - /** - * @brief Set the row to have the new Combo. - * @param iNoteRow the row to have the new Combo. - * @param iCombo the Combo. - */ - void SetHitComboAtRow( int iNoteRow, int iCombo ); - /** - * @brief Set the beat to have the new Combo. - * @param fBeat the beat to have the new Combo. - * @param iCombo the Combo. - */ - void SetHitComboAtBeat( float fBeat, int iCombo ) { SetHitComboAtRow( BeatToNoteRow( fBeat ), iCombo ); } - /** - * @brief Set the row to have the new Miss Combo. - * @param iNoteRow the row to have the new Miss Combo. - * @param iCombo the Miss Combo. - */ - void SetMissComboAtRow( int iNoteRow, int iCombo ); - /** - * @brief Set the beat to have the new Miss Combo. - * @param fBeat the beat to have the new Miss Combo. - * @param iCombo the Miss Combo. - */ - void SetMissComboAtBeat( float fBeat, int iCombo ) { SetMissComboAtRow( BeatToNoteRow( fBeat ), iCombo ); } - /** - * @brief Retrieve the Label at the given row. - * @param iNoteRow the row in question. - * @return the Label. - */ - RString GetLabelAtRow( int iNoteRow ) const; - /** - * @brief Retrieve the Label at the given beat. - * @param fBeat the beat in question. - * @return the Label. - */ - RString GetLabelAtBeat( float fBeat ) const { return GetLabelAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Set the row to have the new Label. - * @param iNoteRow the row to have the new Label. - * @param sLabel the Label. - */ - void SetLabelAtRow( int iNoteRow, const RString sLabel ); - /** - * @brief Set the beat to have the new Label. - * @param fBeat the beat to have the new Label. - * @param sLabel the Label. - */ + const RString& GetLabelAtRow( int iNoteRow ) const { return GetLabelSegmentAtRow(iNoteRow)->GetLabel(); } + const RString& GetLabelAtBeat( float fBeat ) const { return GetLabelAtRow( BeatToNoteRow(fBeat) ); } + void SetLabelAtRow( int iNoteRow, const RString& sLabel ) { AddSegment( LabelSegment(iNoteRow,sLabel) ); } void SetLabelAtBeat( float fBeat, const RString sLabel ) { SetLabelAtRow( BeatToNoteRow( fBeat ), sLabel ); } + bool DoesLabelExist( const RString& sLabel ) const; - /** - * @brief Determine if the requisite label already exists. - * @param sLabel the label to check. - * @return true if it exists, false otherwise. */ - bool DoesLabelExist( RString sLabel ) const; - /** - * @brief Retrieve the Speed's percent at the given row. - * @param iNoteRow the row in question. - * @return the percent. - */ - float GetSpeedPercentAtRow( int iNoteRow ); - /** - * @brief Retrieve the Speed's percent at the given beat. - * @param fBeat the beat in question. - * @return the percent. - */ + float GetSpeedPercentAtRow( int iNoteRow ) { return GetSpeedSegmentAtRow(iNoteRow)->GetRatio(); } float GetSpeedPercentAtBeat( float fBeat ) { return GetSpeedPercentAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Retrieve the Speed's wait at the given row. - * @param iNoteRow the row in question. - * @return the wait. - */ - float GetSpeedWaitAtRow( int iNoteRow ); - /** - * @brief Retrieve the Speed's wait at the given beat. - * @param fBeat the beat in question. - * @return the wait. - */ + + float GetSpeedWaitAtRow( int iNoteRow ) { return GetSpeedSegmentAtRow(iNoteRow)->GetDelay(); } float GetSpeedWaitAtBeat( float fBeat ) { return GetSpeedWaitAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Retrieve the Speed's mode at the given row. - * @param iNoteRow the row in question. - * @return the mode. - */ - SpeedSegment::BaseUnit GetSpeedModeAtRow( int iNoteRow ); - /** - * @brief Retrieve the Speed's mode at the given beat. - * @param fBeat the beat in question. - * @return the mode. - */ + + // XXX: is there any point to having specific unit types? + SpeedSegment::BaseUnit GetSpeedModeAtRow( int iNoteRow ) const { return GetSpeedSegmentAtRow(iNoteRow)->GetUnit(); } SpeedSegment::BaseUnit GetSpeedModeAtBeat( float fBeat ) { return GetSpeedModeAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Set the row to have the new Speed. - * @param iNoteRow the row to have the new Speed. - * @param fPercent the percent. - * @param fWait the wait. - * @param usMode the mode. - */ - void SetSpeedAtRow( int iNoteRow, float fPercent, float fWait, SpeedSegment::BaseUnit unit ); - /** - * @brief Set the beat to have the new Speed. - * @param fBeat the beat to have the new Speed. - * @param fPercent the percent. - * @param fWait the wait. - * @param usMode the mode. - */ - void SetSpeedAtBeat( float fBeat, float fPercent, float fWait, SpeedSegment::BaseUnit unit ) { SetSpeedAtRow( BeatToNoteRow(fBeat), fPercent, fWait, unit ); } - /** - * @brief Set the row to have the new Speed percent. - * @param iNoteRow the row to have the new Speed percent. - * @param fPercent the percent. - */ - void SetSpeedPercentAtRow( int iNoteRow, float fPercent ); - /** - * @brief Set the beat to have the new Speed percent. - * @param fBeat the beat to have the new Speed percent. - * @param fPercent the percent. - */ + + void SetSpeedAtRow( int iNoteRow, float fPercent, float fWait, SpeedSegment::BaseUnit unit ) + { + AddSegment( SpeedSegment(iNoteRow, fPercent, fWait, unit) ); + } + + void SetSpeedAtBeat( float fBeat, float fPercent, float fWait, SpeedSegment::BaseUnit unit ) + { + SetSpeedAtRow( BeatToNoteRow(fBeat), fPercent, fWait, unit ); + } + + void SetSpeedPercentAtRow( int iNoteRow, float fPercent ) + { + const SpeedSegment* seg = GetSpeedSegmentAtRow(iNoteRow); + SetSpeedAtRow( iNoteRow, fPercent, seg->GetDelay(), seg->GetUnit() ); + } + + void SetSpeedWaitAtRow( int iNoteRow, float fWait ) + { + const SpeedSegment* seg = GetSpeedSegmentAtRow(iNoteRow); + SetSpeedAtRow( iNoteRow, seg->GetRatio(), fWait, seg->GetUnit() ); + } + + void SetSpeedModeAtRow( int iNoteRow, SpeedSegment::BaseUnit unit ) + { + const SpeedSegment* seg = GetSpeedSegmentAtRow(iNoteRow); + SetSpeedAtRow( iNoteRow, seg->GetRatio(), seg->GetDelay(), unit ); + } + void SetSpeedPercentAtBeat( float fBeat, float fPercent ) { SetSpeedPercentAtRow( BeatToNoteRow(fBeat), fPercent); } - /** - * @brief Set the row to have the new Speed wait. - * @param iNoteRow the row to have the new Speed wait. - * @param fWait the wait. - */ - void SetSpeedWaitAtRow( int iNoteRow, float fWait ); - /** - * @brief Set the beat to have the new Speed wait. - * @param fBeat the beat to have the new Speed wait. - * @param fWait the wait. - */ void SetSpeedWaitAtBeat( float fBeat, float fWait ) { SetSpeedWaitAtRow( BeatToNoteRow(fBeat), fWait); } - /** - * @brief Set the row to have the new Speed mode. - * @param iNoteRow the row to have the new Speed mode. - * @param usMode the mode. - */ - void SetSpeedModeAtRow( int iNoteRow, SpeedSegment::BaseUnit unit ); - /** - * @brief Set the beat to have the new Speed mode. - * @param fBeat the beat to have the new Speed mode. - * @param usMode the mode. - */ void SetSpeedModeAtBeat( float fBeat, SpeedSegment::BaseUnit unit ) { SetSpeedModeAtRow( BeatToNoteRow(fBeat), unit); } float GetDisplayedSpeedPercent( float fBeat, float fMusicSeconds ) const; - /** - * @brief Retrieve the scrolling factor at the given row. - * @param iNoteRow the row in question. - * @return the percent. - */ - float GetScrollAtRow( int iNoteRow ); - /** - * @brief Retrieve the scrolling factor at the given beat. - * @param fBeat the beat in question. - * @return the percent. - */ + + float GetScrollAtRow( int iNoteRow ) const { return GetScrollSegmentAtRow(iNoteRow)->GetRatio(); } float GetScrollAtBeat( float fBeat ) { return GetScrollAtRow( BeatToNoteRow(fBeat) ); } - /** - * @brief Set the row to have the new Scrolling factor. - * @param iNoteRow the row to have the new Speed. - * @param fPercent the scrolling factor. - */ - void SetScrollAtRow( int iNoteRow, float fPercent ); - /** - * @brief Set the row to have the new Scrolling factor. - * @param iNoteRow the row to have the new Speed. - * @param fPercent the scrolling factor. - */ + void SetScrollAtRow( int iNoteRow, float fPercent ) { AddSegment( ScrollSegment(iNoteRow, fPercent) ); } void SetScrollAtBeat( float fBeat, float fPercent ) { SetScrollAtRow( BeatToNoteRow(fBeat), fPercent ); } - /** - * @brief Determine when the fakes end. - * @param iRow The row you start on. - * @return the time when the fakes end. - */ - float GetFakeAtRow( int iRow ) const; - /** - * @brief Determine when the fakes end. - * @param fBeat The beat you start on. - * @return the time when the fakes end. - */ + float GetFakeAtRow( int iRow ) const { return GetFakeSegmentAtRow(iRow)->GetLength(); } float GetFakeAtBeat( float fBeat ) const { return GetFakeAtRow( BeatToNoteRow( fBeat ) ); } - /** - * @brief Set the beat to indicate when the FakeSegment ends. - * @param iRow The row to start on. - * @param fNew The destination beat. - */ - void SetFakeAtRow( int iRow, float fNew ); - /** - * @brief Set the beat to indicate when the FakeSegment ends. - * @param fBeat The beat to start on. - * @param fNew The destination beat. - */ - void SetFakeAtBeat( float fBeat, float fNew ) { SetFakeAtRow( BeatToNoteRow( fBeat ), fNew ); } - /** - * @brief Checks if the row is inside a fake. - * @param iRow the row to focus on. - * @return true if the row is inside a fake, false otherwise. - */ + bool IsWarpAtRow( int iRow ) const; + bool IsWarpAtBeat( float fBeat ) const { return IsWarpAtRow( BeatToNoteRow( fBeat ) ); } bool IsFakeAtRow( int iRow ) const; - /** - * @brief Checks if the beat is inside a fake. - * @param fBeat the beat to focus on. - * @return true if the row is inside a fake, false otherwise. - */ bool IsFakeAtBeat( float fBeat ) const { return IsFakeAtRow( BeatToNoteRow( fBeat ) ); } - + /** * @brief Determine if this notes on this row can be judged. * @param row the row to focus on. * @return true if the row can be judged, false otherwise. */ - bool IsJudgableAtRow( int row ) const - { - return !(IsWarpAtRow(row) || IsFakeAtRow(row)); - } - /** - * @brief Determine if this notes on this beat can be judged. - * @param beat the beat to focus on. - * @return true if the row can be judged, false otherwise. */ + bool IsJudgableAtRow( int row ) const { return !IsWarpAtRow(row) && !IsFakeAtRow(row); } bool IsJudgableAtBeat( float beat ) const { return IsJudgableAtRow( BeatToNoteRow( beat ) ); } - - - + void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor ); - + 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; @@ -653,38 +317,16 @@ public: } float GetElapsedTimeFromBeatNoOffset( float fBeat ) const; float GetDisplayedBeat( float fBeat ) const; - /** - * @brief View the TimingData to see if a song changes its BPM at any point. - * @return true if there is at least one change, false otherwise. - */ - bool HasBpmChanges() const; - /** - * @brief View the TimingData to see if there is at least one stop at any point. - * @return true if there is at least one stop, false otherwise. - */ - bool HasStops() const; - /** - * @brief View the TimingData to see if there is at least one delay at any point. - * @return true if there is at least one delay, false otherwise. - */ - bool HasDelays() const; - /** - * @brief View the TimingData to see if there is at least one warp at any point. - * @return true if there is at least one warp, false otherwise. - */ - bool HasWarps() const; - /** - * @brief View the TimingData to see if there is at least one fake segment involved. - * @return true if there is at least one fake segment, false otherwise. */ - bool HasFakes() const; - /** - * @brief View the TimingData to see if a song changes its speed scrolling at any point. - * @return true if there is at least one change, false otherwise. */ + + bool HasBpmChanges() const { return GetTimingSegments(SEGMENT_BPM).size() > 1; } + bool HasStops() const { return !GetTimingSegments(SEGMENT_STOP).empty(); } + bool HasDelays() const { return !GetTimingSegments(SEGMENT_DELAY).empty(); } + bool HasWarps() const { return !GetTimingSegments(SEGMENT_WARP).empty(); } + bool HasFakes() const { return !GetTimingSegments(SEGMENT_FAKE).empty(); } + bool HasSpeedChanges() const; - /** - * @brief View the TimingData to see if a song changes its speed scrolling at any point. - * @return true if there is at least one change, false otherwise. */ bool HasScrollChanges() const; + /** * @brief Compare two sets of timing data to see if they are equal. * @param other the other TimingData. @@ -720,6 +362,13 @@ public: void InsertRows( int iStartRow, int iRowsToAdd ); void DeleteRows( int iStartRow, int iRowsToDelete ); + void SortSegments( TimingSegmentType tst ); + + const vector &GetTimingSegments( TimingSegmentType tst ) const + { + return m_avpTimingSegments[tst]; + } + /** * @brief Tidy up the timing data, e.g. provide default BPMs, labels, tickcounts. */ @@ -727,6 +376,7 @@ public: // Lua void PushSelf( lua_State *L ); + /** * @brief The file of the song/steps that use this TimingData. * @@ -734,15 +384,17 @@ public: */ RString m_sFile; - // All of the following vectors must be sorted before gameplay. - vector m_avpTimingSegments[NUM_TimingSegmentType]; - - /** - * @brief The initial offset of a song. - */ + /** @brief The initial offset of a song. */ float m_fBeat0OffsetInSeconds; + // XXX: this breaks encapsulation. get rid of it ASAP vector ToVectorString(TimingSegmentType tst, int dec = 6) const; +protected: + // don't call this directly; use the derived-type overloads. + void AddSegment( const TimingSegment *seg ); + + // All of the following vectors must be sorted before gameplay. + vector m_avpTimingSegments[NUM_TimingSegmentType]; }; #undef COMPARE @@ -751,7 +403,7 @@ public: /** * @file - * @author Chris Danford, Glenn Maynard (c) 2001-2004 + * @author Chris Danford, Glenn Maynard (c) 2001-2004 * @section LICENSE * All rights reserved. * diff --git a/src/TimingSegments.cpp b/src/TimingSegments.cpp index e5cf18aeda..8990a1f400 100644 --- a/src/TimingSegments.cpp +++ b/src/TimingSegments.cpp @@ -24,6 +24,99 @@ void TimingSegment::Scale( int start, int length, int newLength ) SetRow( ScalePosition( start, length, newLength, this->GetRow() ) ); } +void TimingSegment::DebugPrint() const +{ + LOG->Trace( "\tTimingSegment(%d [%f])", GetRow(), GetBeat() ); +} + +void BPMSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %f)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetBPM() + ); +} + +void StopSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %f)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetPause() + ); +} + +void DelaySegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %f)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetPause() + ); +} + +void TimeSignatureSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %d/%d)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetNum(), GetDen() + ); +} + +void WarpSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %d [%f])", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetLengthRows(), GetLengthBeats() + ); +} + +void LabelSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %s)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetLabel().c_str() + ); +} + +void TickcountSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %d)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetTicks() + ); +} + +void ComboSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %d, %d)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetCombo(), GetMissCombo() + ); +} + +void SpeedSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %f, %f, %d)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetRatio(), GetDelay(), GetUnit() + ); +} + +void ScrollSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %f)", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetRatio() + ); +} + +void FakeSegment::DebugPrint() const +{ + LOG->Trace( "\t%s(%d [%f], %d [%f])", + TimingSegmentTypeToString(GetType()).c_str(), + GetRow(), GetBeat(), GetLengthRows(), GetLengthBeats() + ); +} + RString FakeSegment::ToString(int dec) const { RString str = "%.0" + IntToString(dec) diff --git a/src/TimingSegments.h b/src/TimingSegments.h index c0fd47943e..e8a8484129 100644 --- a/src/TimingSegments.h +++ b/src/TimingSegments.h @@ -20,26 +20,48 @@ enum TimingSegmentType TimingSegmentType_Invalid, }; +// XXX: dumb names +enum SegmentEffectType +{ + SegmentEffectType_Row, // takes effect on a single row + SegmentEffectType_Range, // takes effect for a definite amount of rows + SegmentEffectType_Indefinite, // takes effect until the next segment of its type + NUM_SegmentEffectType, + SegmentEffectType_Invalid, +}; + +#define FOREACH_TimingSegmentType(tst) FOREACH_ENUM(TimingSegmentType, tst) + const RString& TimingSegmentTypeToString( TimingSegmentType tst ); const int ROW_INVALID = -1; +#define COMPARE(x) if( this->x!=other.x ) return false +#define COMPARE_FLOAT(x) if( fabsf(this->x - other.x) > EPSILON ) return false + /** * @brief The base timing segment for make glorious benefit wolfman + * XXX: this should be an abstract class. */ struct TimingSegment { - virtual TimingSegmentType GetType() const - { - return TimingSegmentType_Invalid; - } + virtual TimingSegmentType GetType() const { return TimingSegmentType_Invalid; } + virtual SegmentEffectType GetEffectType() const { return SegmentEffectType_Invalid; } + virtual TimingSegment* Copy() const = 0; + virtual bool IsNotable() const = 0; + virtual void DebugPrint() const; + + // don't allow base TimingSegments to be instantiated directly TimingSegment( int iRow = ROW_INVALID ) : m_iStartRow(iRow) { } TimingSegment( float fBeat ) : m_iStartRow(ToNoteRow(fBeat)) { } TimingSegment(const TimingSegment &other) : m_iStartRow( other.GetRow() ) { } + // for our purposes, two floats within this level of error are equal + static const double EPSILON = 1e-4f; + virtual ~TimingSegment() { } /** @@ -66,14 +88,17 @@ struct TimingSegment return GetRow() < other.GetRow(); } + // overloads should not call this base version; derived classes + // should only compare contents, and this compares position. virtual bool operator==( const TimingSegment &other ) const { + LOG->Trace( __PRETTY_FUNCTION__ ); return GetRow() == other.GetRow(); } virtual bool operator!=( const TimingSegment &other ) const { - return !operator==(other); + return !this->operator==(other); } private: @@ -95,6 +120,12 @@ private: struct FakeSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_FAKE; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Range; } + + TimingSegment* Copy() const { return new FakeSegment(*this); } + + bool IsNotable() const { return m_iLengthRows > 0; } + void DebugPrint() const; FakeSegment() : TimingSegment(), m_iLengthRows(-1) { } @@ -118,6 +149,23 @@ struct FakeSegment : public TimingSegment void Scale( int start, int length, int newLength ); RString ToString( int dec ) const; + + bool operator==( const FakeSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE( m_iLengthRows ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } + private: /** @brief The number of rows the FakeSegment is alive for. */ int m_iLengthRows; @@ -133,6 +181,11 @@ private: struct WarpSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_WARP; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Range; } + TimingSegment* Copy() const { return new WarpSegment(*this); } + + bool IsNotable() const { return m_iLengthRows > 0; } + void DebugPrint() const; WarpSegment() : TimingSegment(), m_iLengthRows(0) { } @@ -154,8 +207,24 @@ struct WarpSegment : public TimingSegment void SetLength( float fBeats ) { m_iLengthRows = ToNoteRow(fBeats); } void Scale( int start, int length, int newLength ); - RString ToString( int dec ) const; + + bool operator==( const WarpSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE( m_iLengthRows ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } + private: /** @brief The number of rows the WarpSegment will warp past. */ int m_iLengthRows; @@ -170,12 +239,19 @@ private: * represent how many ticks can be counted in one beat. */ -/** @brief The default amount of ticks per beat. */ -const unsigned DEFAULT_TICK_COUNT = 4; struct TickcountSegment : public TimingSegment { + /** @brief The default amount of ticks per beat. */ + static const unsigned DEFAULT_TICK_COUNT = 4; + TimingSegmentType GetType() const { return SEGMENT_TICKCOUNT; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new TickcountSegment(*this); } TickcountSegment( int iStartRow = ROW_INVALID, int iTicks = DEFAULT_TICK_COUNT ) : TimingSegment(iStartRow), m_iTicksPerBeat(iTicks) { } @@ -188,6 +264,22 @@ struct TickcountSegment : public TimingSegment void SetTicks( int iTicks ) { m_iTicksPerBeat = iTicks; } RString ToString( int dec ) const; + + bool operator==( const TickcountSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE( m_iTicksPerBeat ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } private: /** @brief The amount of hold checkpoints counted per beat */ int m_iTicksPerBeat; @@ -202,6 +294,12 @@ private: struct ComboSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_COMBO; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new ComboSegment(*this); } ComboSegment( int iStartRow = ROW_INVALID, int iCombo = 1, int iMissCombo = 1 ) : TimingSegment(iStartRow), m_iCombo(iCombo), @@ -219,6 +317,23 @@ struct ComboSegment : public TimingSegment void SetMissCombo( int iCombo ) { m_iMissCombo = iCombo; } RString ToString( int dec ) const; + + bool operator==( const ComboSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE( m_iCombo ); + COMPARE( m_iMissCombo ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } private: /** @brief The amount the combo increases at this point. */ int m_iCombo; @@ -237,6 +352,12 @@ private: struct LabelSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_LABEL; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new LabelSegment(*this); } LabelSegment( int iStartRow = ROW_INVALID, const RString& sLabel = RString() ) : TimingSegment(iStartRow), m_sLabel(sLabel) { } @@ -249,6 +370,22 @@ struct LabelSegment : public TimingSegment void SetLabel( const RString& sLabel ) { m_sLabel.assign(sLabel); } RString ToString( int dec ) const; + + bool operator==( const LabelSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE( m_sLabel ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } private: /** @brief The label/section name for this point. */ RString m_sLabel; @@ -260,6 +397,12 @@ private: struct BPMSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_BPM; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new BPMSegment(*this); } // note that this takes a BPM, not a BPS (compatibility) BPMSegment( int iStartRow = ROW_INVALID, float fBPM = 0.0f ) : @@ -276,6 +419,23 @@ struct BPMSegment : public TimingSegment void SetBPM( float fBPM ) { m_fBPS = fBPM / 60.0f; } RString ToString( int dec ) const; + + bool operator==( const BPMSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE_FLOAT( m_fBPS ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } + private: /** @brief The number of beats per second within this BPMSegment. */ float m_fBPS; @@ -291,6 +451,12 @@ private: struct TimeSignatureSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_TIME_SIG; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new TimeSignatureSegment(*this); } TimeSignatureSegment( int iStartRow = ROW_INVALID, int iNum = 4, int iDenom = 4 ) : @@ -308,6 +474,8 @@ struct TimeSignatureSegment : public TimingSegment int GetDen() const { return m_iDenominator; } void SetDen( int den ) { m_iDenominator = den; } + void Set( int num, int den ) { m_iNumerator = num; m_iDenominator = den; } + RString ToString( int dec ) const; /** @@ -325,6 +493,23 @@ struct TimeSignatureSegment : public TimingSegment return BeatToNoteRow(1) * 4 * m_iNumerator / m_iDenominator; } + bool operator==( const TimeSignatureSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE( m_iNumerator ); + COMPARE( m_iDenominator ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } + private: int m_iNumerator, m_iDenominator; }; @@ -342,6 +527,12 @@ private: struct SpeedSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_SPEED; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new SpeedSegment(*this); } /** @brief The type of unit used for segment scaling. */ enum BaseUnit { UNIT_BEATS, UNIT_SECONDS }; @@ -369,6 +560,25 @@ struct SpeedSegment : public TimingSegment void Scale( int start, int length, int newLength ); RString ToString( int dec ) const; + + bool operator==( const SpeedSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE_FLOAT( m_fRatio ); + COMPARE_FLOAT( m_fDelay ); + COMPARE( m_Unit ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } + private: /** @brief The percentage by which the Player's BPM is multiplied. */ float m_fRatio; @@ -393,6 +603,12 @@ private: struct ScrollSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_SCROLL; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; } + + bool IsNotable() const { return true; } // indefinite segments are always true + void DebugPrint() const; + + TimingSegment* Copy() const { return new ScrollSegment(*this); } ScrollSegment( int iStartRow = ROW_INVALID, float fRatio = 1.0f ) : TimingSegment(iStartRow), m_fRatio(fRatio) { } @@ -405,6 +621,23 @@ struct ScrollSegment : public TimingSegment void SetRatio( float fRatio ) { m_fRatio = fRatio; } RString ToString( int dec ) const; + + bool operator==( const ScrollSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE_FLOAT( m_fRatio ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } + private: /** @brief The percentage by which the chart's scroll rate is multiplied. */ float m_fRatio; @@ -416,6 +649,12 @@ private: struct StopSegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_STOP; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Row; } + + bool IsNotable() const { return m_fSeconds > 0; } + void DebugPrint() const; + + TimingSegment* Copy() const { return new StopSegment(*this); } StopSegment( int iStartRow = ROW_INVALID, float fSeconds = 0.0f ) : TimingSegment(iStartRow), m_fSeconds(fSeconds) { } @@ -428,6 +667,22 @@ struct StopSegment : public TimingSegment void SetPause( float fSeconds ) { m_fSeconds = fSeconds; } RString ToString( int dec ) const; + + bool operator==( const StopSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE_FLOAT( m_fSeconds ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } private: /** @brief The number of seconds to pause at the segment's row. */ float m_fSeconds; @@ -439,8 +694,14 @@ private: struct DelaySegment : public TimingSegment { TimingSegmentType GetType() const { return SEGMENT_DELAY; } + SegmentEffectType GetEffectType() const { return SegmentEffectType_Row; } - DelaySegment( int iStartRow, float fSeconds ) : + bool IsNotable() const { return m_fSeconds > 0; } + void DebugPrint() const; + + TimingSegment* Copy() const { return new DelaySegment(*this); } + + DelaySegment( int iStartRow = ROW_INVALID, float fSeconds = 0 ) : TimingSegment(iStartRow), m_fSeconds(fSeconds) { } DelaySegment( const DelaySegment &other ) : @@ -451,11 +712,29 @@ struct DelaySegment : public TimingSegment void SetPause( float fSeconds ) { m_fSeconds = fSeconds; } RString ToString( int dec ) const; + + bool operator==( const DelaySegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + COMPARE_FLOAT( m_fSeconds ); + return true; + } + + bool operator==( const TimingSegment &other ) const + { + LOG->Trace( __PRETTY_FUNCTION__ ); + if( GetType() != other.GetType() ) + return false; + + return operator==( static_cast(other) ); + } private: /** @brief The number of seconds to pause at the segment's row. */ float m_fSeconds; }; +#undef COMPARE +#undef COMPARE_FLOAT #endif