Mission accomplished: [warps] are in default.

Let the fun times roll!
This commit is contained in:
Jason Felds
2011-03-27 03:12:43 -04:00
21 changed files with 684 additions and 441 deletions
+1
View File
@@ -72,6 +72,7 @@ Data/SpeedMods.txt
Program/StepMania*.lib
crashinfo.txt
Utils/temp*
*.lnk
syntax: regexp
.*\#.*\#$
+3
View File
@@ -9,6 +9,9 @@ change to JSON, but it is unsure if this will be done.
Implement .ssc at your own risk.
________________________________________________________________________________
[v0.66] - Wolfman2000
* Implement #WARPS tag to replace negative bpm/stop gimmicks.
[v0.55] - Wolfman2000
* Add #COMBOS tag to the Song and Steps (does nothing here).
+7
View File
@@ -13,6 +13,13 @@ _____________________________________________________________________________
sm-ssc v1.2.4 | 2011032?
--------------------------------------------------------------------------------
20110326
--------
* [NotesLoaderSM, NotesWriterSSC] Officially add the #WARPS tag. The negative
tricks of the past are now officially standardized. Please tell us about
files which have broken sync or are otherwise unplayable/incorrect.
[AJ, Thai, Wolfman2000]
20110320
--------
* [PlayerOptions] Added SetNoteSkin(string) Lua binding. [AJ]
+2
View File
@@ -843,6 +843,7 @@ Edit time signature (top)=Edit time signature (beats per measure)
Edit time signature (bottom)=Edit time signature (single beat note value)
Edit tickcount=Edit tickcount
Edit combo=Edit combo
Edit warp=Edit warp
Editor options=Options
EditorShowBGChangesPlay=Show Backgrounds
Effect=Effect
@@ -1209,6 +1210,7 @@ Enter a new Time Signature numerator value.=Enter the number of beats per measur
Enter a new Time Signature denominator value.=Enter the note value that represents one beat.
Enter a new Tickcount value.=Enter a new Tickcount value.
Enter a new Combo value.=Enter a new Combo value.
Enter a new Warp value.=Enter the beat you will warp to when you reach this point.
Enter a new artist transliteration.=Enter a new artist transliteration.
Enter a new artist.=Enter a new artist.
Enter a new genre.=Enter a new genre.
+5 -2
View File
@@ -963,6 +963,7 @@ RoutineNoteSkinP2=RoutineSkinP2()
BPMColor=color("1,0,0,1")
StopColor=color("0.8,0.8,0,1")
DelayColor=color("0,0.8,0.8,1")
WarpColor=color("1,0,0.5,1")
TimeSignatureColor=color("1,0.55,0,1")
TickcountColor=color("0,1,0,1")
ComboColor=color("0.55,1,0,1")
@@ -970,13 +971,15 @@ ComboColor=color("0.55,1,0,1")
BPMIsLeftSide=true
StopIsLeftSide=true
DelayIsLeftSide=true
WarpIsLeftSide=false
TimeSignatureIsLeftSide=true
TickcountIsLeftSide=false
ComboIsLeftSide=false
#
BPMOffsetX=60
StopOffsetX=10
DelayOffsetX=10
StopOffsetX=50
DelayOffsetX=120
WarpOffsetX=60
TimeSignatureOffsetX=30
TickcountOffsetX=10
ComboOffsetX=30
+3 -11
View File
@@ -897,7 +897,7 @@ void GameState::ResetMusicStatistics()
m_bFreeze = false;
m_bDelay = false;
m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj
m_fWarpLength = -1; // Set when a warp is encountered. also see above. -aj
m_fWarpDestination = -1; // Set when a warp is encountered. also see above. -aj
m_fMusicSecondsVisible = 0;
m_fSongBeatVisible = 0;
Actor::SetBGMTime( 0, 0, 0, 0 );
@@ -943,7 +943,7 @@ void GameState::ResetStageStatistics()
}
static Preference<float> g_fVisualDelaySeconds( "VisualDelaySeconds", 0.0f );
// todo: modify for warps -aj
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer &timestamp )
{
if( !timestamp.IsZero() )
@@ -957,18 +957,10 @@ void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &ti
LOG->Trace( ssprintf("[GameState::UpdateSongPosition] cur BPS = %f, fPositionSeconds = %f",m_fCurBPS,fPositionSeconds) );
*/
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_fWarpLength );
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_fWarpDestination );
// "Crash reason : -243478.890625 -48695.773438"
ASSERT_M( m_fSongBeat > -2000, ssprintf("Song beat %f at %f seconds", m_fSongBeat, fPositionSeconds) );
//if( m_iWarpBeginRow != -1 || m_iWarpEndRow == -1 )
if( m_iWarpBeginRow != -1 && m_fWarpLength > 0.0f )
{
// There is a warp in this section.
LOG->Trace("warp at %i lasts for %f, jumps to %i",m_iWarpBeginRow,m_fWarpLength,m_iWarpBeginRow+BeatToNoteRow(m_fWarpLength));
//fPositionSeconds += (m_fWarpLength * m_fCurBPS);
}
m_fMusicSeconds = fPositionSeconds;
m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds );
+3 -2
View File
@@ -208,9 +208,10 @@ public:
bool m_bFreeze;
/** @brief A flag to determine if we're in the middle of a delay (Pump style stop). */
bool m_bDelay;
// used for warping:
/** @brief The row used to start a warp. */
int m_iWarpBeginRow;
float m_fWarpLength;
/** @brief The beat to warp to afterwards. */
float m_fWarpDestination;
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
BroadcastOnChange<bool> m_bGameplayLeadIn;
+6 -6
View File
@@ -864,8 +864,8 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri
FOREACH_CONST( Command, cmds.v, c )
{
const Command& cmd = (*c);
RString sName = cmd.GetName();
s << "\tself:" << sName << "(";
RString local_sName = cmd.GetName();
s << "\tself:" << local_sName << "(";
for( unsigned i=1; i<cmd.m_vsArgs.size(); i++ )
{
@@ -877,10 +877,10 @@ void LuaHelpers::ParseCommandList( Lua *L, const RString &sCommands, const RStri
if( sArg[0] == '#' ) // HTML color
{
RageColor c; // in case FromString fails
c.FromString( sArg );
// c is still valid if FromString fails
s << c.r << "," << c.g << "," << c.b << "," << c.a;
RageColor col; // in case FromString fails
col.FromString( sArg );
// col is still valid if FromString fails
s << col.r << "," << col.g << "," << col.b << "," << col.a;
}
else
{
+33 -2
View File
@@ -422,18 +422,21 @@ void NoteField::DrawAreaHighlight( int iStartBeat, int iEndBeat )
static ThemeMetric<RageColor> BPM_COLOR ( "NoteField", "BPMColor" );
static ThemeMetric<RageColor> STOP_COLOR ( "NoteField", "StopColor" );
static ThemeMetric<RageColor> DELAY_COLOR ( "NoteField", "DelayColor" );
static ThemeMetric<RageColor> WARP_COLOR ( "NoteField", "WarpColor" );
static ThemeMetric<RageColor> TIME_SIGNATURE_COLOR ( "NoteField", "TimeSignatureColor" );
static ThemeMetric<RageColor> TICKCOUNT_COLOR ( "NoteField", "TickcountColor" );
static ThemeMetric<RageColor> COMBO_COLOR ( "NoteField", "ComboColor" );
static ThemeMetric<bool> BPM_IS_LEFT_SIDE ( "NoteField", "BPMIsLeftSide" );
static ThemeMetric<bool> STOP_IS_LEFT_SIDE ( "NoteField", "StopIsLeftSide" );
static ThemeMetric<bool> DELAY_IS_LEFT_SIDE ( "NoteField", "DelayIsLeftSide" );
static ThemeMetric<bool> WARP_IS_LEFT_SIDE ( "NoteField", "WarpIsLeftSide" );
static ThemeMetric<bool> TIME_SIGNATURE_IS_LEFT_SIDE ( "NoteField", "TimeSignatureIsLeftSide" );
static ThemeMetric<bool> TICKCOUNT_IS_LEFT_SIDE ( "NoteField", "TickcountIsLeftSide" );
static ThemeMetric<bool> COMBO_IS_LEFT_SIDE ( "NoteField", "ComboIsLeftSide" );
static ThemeMetric<float> BPM_OFFSETX ( "NoteField", "BPMOffsetX" );
static ThemeMetric<float> STOP_OFFSETX ( "NoteField", "StopOffsetX" );
static ThemeMetric<float> DELAY_OFFSETX ( "NoteField", "DelayOffsetX" );
static ThemeMetric<float> WARP_OFFSETX ( "NoteField", "WarpOffsetX" );
static ThemeMetric<float> TIME_SIGNATURE_OFFSETX ( "NoteField", "TimeSignatureOffsetX" );
static ThemeMetric<float> TICKCOUNT_OFFSETX ( "NoteField", "TickcountOffsetX" );
static ThemeMetric<float> COMBO_OFFSETX ( "NoteField", "ComboOffsetX" );
@@ -481,6 +484,23 @@ void NoteField::DrawFreezeText( const float fBeat, const float fSecs, const floa
m_textMeasureNumber.Draw();
}
void NoteField::DrawWarpText( const float fBeat, const float fNewBeat )
{
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
const float fYPos = ArrowEffects::GetYPos( m_pPlayerState, 0, fYOffset, m_fYReverseOffsetPixels );
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
const float xBase = GetWidth()/2.f;
const float xOffset = WARP_OFFSETX * fZoom;
m_textMeasureNumber.SetZoom( fZoom );
m_textMeasureNumber.SetHorizAlign( WARP_IS_LEFT_SIDE ? align_right : align_left );
m_textMeasureNumber.SetDiffuse( WARP_COLOR );
m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) );
m_textMeasureNumber.SetText( ssprintf("%.3f", fNewBeat) );
m_textMeasureNumber.SetXY( (WARP_IS_LEFT_SIDE ? -xBase - xOffset : xBase + xOffset), fYPos );
m_textMeasureNumber.Draw();
}
void NoteField::DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator )
{
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
@@ -770,6 +790,19 @@ void NoteField::DrawPrimitives()
DrawFreezeText( fBeat, aStopSegments[i].m_fStopSeconds, aStopSegments[i].m_bDelay );
}
}
// Warp text
const vector<WarpSegment> &aWarpSegments = GAMESTATE->m_pCurSong->m_Timing.m_WarpSegments;
for( unsigned i=0; i<aWarpSegments.size(); i++ )
{
if( aWarpSegments[i].m_iStartRow >= iFirstRowToDraw &&
aWarpSegments[i].m_iStartRow <= iLastRowToDraw)
{
float fBeat = NoteRowToBeat(aWarpSegments[i].m_iStartRow);
if( IS_ON_SCREEN(fBeat) )
DrawWarpText( fBeat, aWarpSegments[i].m_fEndBeat );
}
}
// Time Signature text
const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments;
@@ -810,8 +843,6 @@ void NoteField::DrawPrimitives()
}
}
// todo: add warp text -aj
// Course mods text
const Course *pCourse = GAMESTATE->m_pCurCourse;
if( pCourse )
+1
View File
@@ -57,6 +57,7 @@ protected:
void DrawAreaHighlight( int iStartBeat, int iEndBeat );
void DrawBPMText( const float fBeat, const float fBPM );
void DrawFreezeText( const float fBeat, const float fBPM, const float bDelay );
void DrawWarpText( const float fBeat, const float fNewBeat );
void DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator );
void DrawTickcountText( const float fBeat, int iTicks );
void DrawComboText( const float fBeat, int iCombo );
+121 -168
View File
@@ -12,7 +12,13 @@
#include "Steps.h"
#include "PrefsManager.h"
/** @brief The maximum file size for edits. */
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB
/**
* @brief The highest allowable speed before Warps come in.
*
* This was brought in from StepMania 4's recent betas. */
const float FAST_BPM_WARP = 9999999.f;
void SMLoader::LoadFromSMTokens(
RString sStepsType,
@@ -104,9 +110,6 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
out.m_WarpSegments.clear();
out.m_vTimeSignatureSegments.clear();
vector<WarpSegment> arrayWarpsFromNegativeBPMs;
//vector<WarpSegment> arrayWarpsFromNegativeStops;
for( unsigned i=0; i<msd.GetNumValues(); i++ )
{
const MsdFile::value_t &sParams = msd.GetValue(i);
@@ -117,11 +120,83 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
{
out.m_fBeat0OffsetInSeconds = StringToFloat( sParams[1] );
}
else if( sValueName=="BPMS" )
{
vector<RString> arrayBPMChangeExpressions;
split( sParams[1], ",", arrayBPMChangeExpressions );
// prepare storage variables for negative BPMs -> Warps.
float negBeat = -1;
float negBPM = 1;
float highspeedBeat = -1;
for( unsigned b=0; b<arrayBPMChangeExpressions.size(); b++ )
{
vector<RString> arrayBPMChangeValues;
split( arrayBPMChangeExpressions[b], "=", arrayBPMChangeValues );
// XXX: Hard to tell which file caused this.
if( arrayBPMChangeValues.size() != 2 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
sValueName.c_str(), arrayBPMChangeExpressions[b].c_str() );
continue;
}
const float fBeat = StringToFloat( arrayBPMChangeValues[0] );
const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] );
if( fNewBPM < 0.0f )
{
out.m_bHasNegativeBpms = true;
negBeat = fBeat;
negBPM = fNewBPM;
}
else if( fNewBPM > 0.0f )
{
// add in a warp.
if( negBPM < 0 )
{
float endBeat = fBeat + (fNewBPM / -negBPM) * (fBeat - negBeat);
WarpSegment new_seg(negBeat, endBeat);
out.AddWarpSegment( new_seg );
negBeat = -1;
negBPM = 1;
}
// too fast. make it a warp.
if( fNewBPM > FAST_BPM_WARP )
{
highspeedBeat = fBeat;
}
else
{
// add in a warp.
if( highspeedBeat > 0 )
{
WarpSegment new_seg(highspeedBeat, fBeat);
out.AddWarpSegment( new_seg );
highspeedBeat = -1;
}
{
BPMSegment new_seg;
new_seg.m_iStartRow = BeatToNoteRow(fBeat);
new_seg.SetBPM( fNewBPM );
out.AddBPMSegment( new_seg );
}
}
}
}
}
else if( sValueName=="STOPS" || sValueName=="FREEZES" )
{
vector<RString> arrayFreezeExpressions;
split( sParams[1], ",", arrayFreezeExpressions );
// Prepare variables for negative stop conversion.
float negBeat = -1;
float negPause = 0;
for( unsigned f=0; f<arrayFreezeExpressions.size(); f++ )
{
vector<RString> arrayFreezeValues;
@@ -136,29 +211,49 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] );
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] );
StopSegment new_seg( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
// XXX: Remove Negatives Bug?
new_seg.m_iStartRow = BeatToNoteRow(fFreezeBeat);
new_seg.m_fStopSeconds = fFreezeSeconds;
if(fFreezeSeconds > 0.0f)
// Process the prior stop.
if( negPause > 0 )
{
// LOG->Trace( "Adding a freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
out.AddStopSegment( new_seg );
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
float fSkipBeats = negPause / fSecondsPerBeat;
if( negBeat + fSkipBeats > fFreezeBeat )
fSkipBeats = fFreezeBeat - negBeat;
WarpSegment ws( negBeat, negBeat + fSkipBeats);
out.AddWarpSegment( ws );
negBeat = -1;
negPause = 0;
}
else
if( fFreezeSeconds < 0.0f )
{
// negative stops (hi JS!) -aj
if( PREFSMAN->m_bQuirksMode )
{
// LOG->Trace( "Adding a negative freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
out.AddStopSegment( new_seg );
}
else
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid stop at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
negBeat = fFreezeBeat;
negPause = -fFreezeSeconds;
}
else if( fFreezeSeconds > 0.0f )
{
StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
out.AddStopSegment( ss );
}
}
// Process the prior stop if there was one.
if( negPause > 0 )
{
BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
float fSecondsPerBeat = 60 / oldBPM.GetBPM();
float fSkipBeats = negPause / fSecondsPerBeat;
WarpSegment ws( negBeat, negBeat + fSkipBeats);
out.AddWarpSegment( ws );
}
}
else if( sValueName=="DELAYS" )
{
vector<RString> arrayDelayExpressions;
@@ -183,7 +278,7 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
// XXX: Remove Negatives Bug?
new_seg.m_iStartRow = BeatToNoteRow(fFreezeBeat);
new_seg.m_fStopSeconds = fFreezeSeconds;
// LOG->Trace( "Adding a delay segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds );
if(fFreezeSeconds > 0.0f)
@@ -193,95 +288,6 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
}
}
else if( sValueName=="BPMS" )
{
vector<RString> arrayBPMChangeExpressions;
split( sParams[1], ",", arrayBPMChangeExpressions );
for( unsigned b=0; b<arrayBPMChangeExpressions.size(); b++ )
{
vector<RString> arrayBPMChangeValues;
split( arrayBPMChangeExpressions[b], "=", arrayBPMChangeValues );
// XXX: Hard to tell which file caused this.
if( arrayBPMChangeValues.size() != 2 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
sValueName.c_str(), arrayBPMChangeExpressions[b].c_str() );
continue;
}
const float fBeat = StringToFloat( arrayBPMChangeValues[0] );
const float fNewBPM = StringToFloat( arrayBPMChangeValues[1] );
// XXX: Remove Negatives Bug?
BPMSegment new_seg;
new_seg.m_iStartRow = BeatToNoteRow(fBeat);
new_seg.SetBPM( fNewBPM );
// convert negative BPMs into Warp segments
if( fNewBPM < 0.0f )
{
vector<RString> arrayNextBPMChangeValues;
// get next bpm in sequence
if((b+1) < arrayBPMChangeExpressions.size())
{
split( arrayBPMChangeExpressions[b+1], "=", arrayNextBPMChangeValues );
const float fNextPositiveBeat = StringToFloat( arrayNextBPMChangeValues[0] );
const float fNextPositiveBPM = StringToFloat( arrayNextBPMChangeValues[1] );
// tJumpPos = (tPosBPS-abs(negBPS)) + (gPosBPMPosition - fNegPosition)
float fDeltaBeat = ((fNextPositiveBPM/60.0f)-abs(fNewBPM/60.0f)) + (fNextPositiveBeat-fBeat);
//float fWarpLengthBeats = fNextPositiveBeat + fDeltaBeat;
WarpSegment wsTemp(BeatToNoteRow(fBeat),fDeltaBeat);
arrayWarpsFromNegativeBPMs.push_back(wsTemp);
/*
LOG->Trace( ssprintf("==NotesLoSM negbpm==\nfnextposbeat = %f, fnextposbpm = %f,\nfdelta = %f, fwarpto = %f",
fNextPositiveBeat,
fNextPositiveBPM,
fDeltaBeat,
fWarpToBeat
) );
*/
/*
LOG->Trace( ssprintf("==Negative/Subtractive BPM in NotesLoader==\nNegBPM has noterow = %i, BPM = %f\nNextBPM @ noterow %i\nDelta value = %i noterows\nThis warp will have us end up at noterow %i",
BeatToNoteRow(fBeat), fNewBPM,
BeatToNoteRow(fNextPositiveBeat),
BeatToNoteRow(fDeltaBeat),
BeatToNoteRow(fWarpToBeat))
);
*/
//float fDeltaBeat = ((fNextPositiveBPM/60.0f)-abs(fNewBPM/60.0f)) + (fNextPositiveBeat-fBeat);
/*
LOG->Trace( ssprintf("==NotesLoader Delta as NoteRows==\nfDeltaBeat = %f (beat)\nfDeltaBeat = (NextBPMSeg %f - abs(fBPS %f)) + (nextStartRow %i - thisRow %i)",
fDeltaBeat,(fNextPositiveBPM/60.0f),abs(fNewBPM/60.0f),BeatToNoteRow(fNextPositiveBeat),BeatToNoteRow(fBeat))
);
*/
out.AddBPMSegment( new_seg );
continue;
}
else
{
// last BPM is a negative one? ugh. -aj (MAX_NOTE_ROW exists btw)
out.AddBPMSegment( new_seg );
}
}
if(fNewBPM > 0.0f)
out.AddBPMSegment( new_seg );
else
{
out.m_bHasNegativeBpms = true;
// only add Negative BPMs in quirks mode -aj
if( PREFSMAN->m_bQuirksMode )
out.AddBPMSegment( new_seg );
else
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid BPM change at beat %f, BPM %f.", fBeat, fNewBPM );
}
}
}
else if( sValueName=="TIMESIGNATURES" )
{
vector<RString> vs1;
@@ -326,12 +332,12 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
out.AddTimeSignatureSegment( seg );
}
}
else if( sValueName=="TICKCOUNTS" )
{
vector<RString> arrayTickcountExpressions;
split( sParams[1], ",", arrayTickcountExpressions );
for( unsigned f=0; f<arrayTickcountExpressions.size(); f++ )
{
vector<RString> arrayTickcountValues;
@@ -343,7 +349,7 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
sValueName.c_str(), arrayTickcountExpressions[f].c_str() );
continue;
}
const float fTickcountBeat = StringToFloat( arrayTickcountValues[0] );
int iTicks = atoi( arrayTickcountValues[1] );
// you're lazy, let SM do the work for you... -DaisuMaster
@@ -366,61 +372,8 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
}
}
}
// warps (replacement for Negative BPM and Negative Stops)
/*
else if( sValueName=="WARPS" )
{
vector<RString> arrayWarpExpressions;
split( sParams[1], ",", arrayWarpExpressions );
for( unsigned f=0; f<arrayWarpExpressions.size(); f++ )
{
vector<RString> arrayWarpValues;
split( arrayWarpExpressions[f], "=", arrayWarpValues );
if( arrayWarpValues.size() != 2 )
{
// XXX: Hard to tell which file caused this.
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
sValueName.c_str(), arrayWarpExpressions[f].c_str() );
continue;
}
const float fWarpStart = StringToFloat( arrayWarpValues[0] );
const float fWarpBeats = StringToFloat( arrayWarpValues[1] );
if( fWarpStart > 0.0f && fWarpBeats > 0.0f )
{
WarpSegment new_seg( BeatToNoteRow(fWarpStart), fWarpBeats );
out.AddWarpSegment( new_seg );
}
else
{
// Currently disallow negative warps, to prevent the same
// kind of problem that happened when Negative/Subtractive
// BPMs arrived on the StepMania scene. -aj
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid warp at beat %f lasting %f beats.", fWarpStart, fWarpBeats );
}
}
}
*/
// Note: Even though it is possible to have Negative BPMs and Stops in
// a song along with Warps, we should not support files that contain
// both styles of warp tricks (Negatives vs. #WARPS).
// If Warps have been populated from Negative BPMs, then go through that
// instead of using the data in the Warps tag. This should be above,
// but it breaks compiling so...
if(arrayWarpsFromNegativeBPMs.size() > 0)
{
// zomg we already have some warps...
for( unsigned j=0; j<arrayWarpsFromNegativeBPMs.size(); j++ )
{
out.AddWarpSegment( arrayWarpsFromNegativeBPMs[j] );
}
}
// warp sorting will need to take place.
//sort(out.m_WarpSegments.begin(), out.m_WarpSegments.end());
// Ensure all of the warps are handled right.
sort(out.m_WarpSegments.begin(), out.m_WarpSegments.end());
}
}
+29
View File
@@ -485,6 +485,35 @@ bool SSCLoader::LoadFromSSCFile( const RString &sPath, Song &out, bool bFromCach
}
}
}
else if( sValueName=="WARPS" )
{
vector<RString> arrayWarpExpressions;
split( sParams[1], ",", arrayWarpExpressions );
for( unsigned b=0; b<arrayWarpExpressions.size(); b++ )
{
vector<RString> arrayWarpValues;
split( arrayWarpExpressions[b], "=", arrayWarpValues );
// XXX: Hard to tell which file caused this.
if( arrayWarpValues.size() != 2 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid #%s value \"%s\" (must have exactly one '='), ignored.",
sValueName.c_str(), arrayWarpExpressions[b].c_str() );
continue;
}
const float fBeat = StringToFloat( arrayWarpValues[0] );
const float fNewBeat = StringToFloat( arrayWarpValues[1] );
if(fNewBeat > fBeat)
out.m_Timing.AddWarpSegment( WarpSegment(fBeat, fNewBeat) );
else
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid Warp at beat %f, BPM %f.", fBeat, fNewBeat );
}
}
}
else if( sValueName=="TIMESIGNATURES" )
{
+3
View File
@@ -21,7 +21,10 @@ enum SSCLoadingStates
NUM_SSCLoadingStates /**< The number of states used. */
};
/** @brief The version where fakes started to be used as a radar category. */
const float VERSION_RADAR_FAKE = 0.53f;
/** @brief The version where WarpSegments started to be utilized. */
const float VERSION_WARP_SEGMENT = 0.56f;
/**
* @brief The SSCLoader handles all of the parsing needed for .ssc files.
+17 -4
View File
@@ -144,18 +144,18 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
}
f.PutLine( ";" );
/*
f.Write( "#WARPS:" );
for( unsigned i=0; i<out.m_Timing.m_WarpSegments.size(); i++ )
{
const WarpSegment &ws = out.m_Timing.m_WarpSegments[i];
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(ws.m_iStartRow), ws.m_fWarpBeats ) );
f.PutLine( ssprintf( "%.6f=%.6f", NoteRowToBeat(ws.m_iStartRow), ws.m_fEndBeat ) );
if( i != out.m_Timing.m_WarpSegments.size()-1 )
f.Write( "," );
}
f.PutLine( ";" );
*/
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
f.Write( "#TIMESIGNATURES:" );
@@ -299,6 +299,7 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
lines.push_back( "#BPMS:;" );
lines.push_back( "#STOPS:;" );
lines.push_back( "#DELAYS:;" );
lines.push_back( "#WARPS:;" );
lines.push_back( "#TIMESIGNATURES:;" );
lines.push_back( "#TICKCOUNTS:;" );
lines.push_back( "#ATTACKS:;" );
@@ -336,7 +337,19 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
}
}
lines.push_back( ssprintf( "#DELAYS:%s;", join("\n,", asDelayValues).c_str() ) );
vector<RString> asWarpValues;
for( unsigned i=0; i<in.m_Timing.m_WarpSegments.size(); i++ )
{
const WarpSegment &ws = in.m_Timing.m_WarpSegments[i];
if( ws.m_bDelay )
{
asWarpValues.push_back( ssprintf( "%.6f=%.6f", NoteRowToBeat(fs.m_iStartRow), fs.m_fWarpBeats ) );
}
}
lines.push_back( ssprintf( "#WARPS:%s;", join("\n,", asWarpValues).c_str() ) );
ASSERT( !in.m_Timing.m_vTimeSignatureSegments.empty() );
vector<RString> asTimeSigValues;
FOREACH_CONST( TimeSignatureSegment, in.m_Timing.m_vTimeSignatureSegments, iter )
+18 -17
View File
@@ -1520,6 +1520,8 @@ int Player::GetClosestNoteDirectional( int col, int iStartRow, int iEndRow, bool
// Is this the row we want?
do {
const TapNote &tn = begin->second;
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( begin->first ) )
break;
if( tn.type == TapNote::empty )
break;
if( !bAllowGraded && tn.result.tns != TNS_None )
@@ -1569,6 +1571,11 @@ int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool b
++iter;
continue;
}
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
{
++iter;
continue;
}
return iter.Row();
}
}
@@ -1966,7 +1973,7 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
* Either option would fundamentally change the grading of two quick notes
* "jack hammers." Hmm.
*/
const int iStepSearchRows = BeatToNoteRow( StepSearchDistance * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate );
const int iStepSearchRows = BeatToNoteRow( GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTime( GAMESTATE->m_fMusicSeconds + StepSearchDistance ) ) - iSongRow;
int iRowOfOverlappingNoteOrRow = row;
if( row == -1 )
{
@@ -2040,7 +2047,9 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
{
case TapNote::mine:
// Stepped too close to mine?
if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) && fSecondsFromExact <= GetWindowSeconds(TW_Mine) )
if( !bRelease && ( REQUIRE_STEP_ON_MINES == !bHeld ) &&
fSecondsFromExact <= GetWindowSeconds(TW_Mine) &&
!GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iSongRow) )
score = TNS_HitMine;
break;
@@ -2522,8 +2531,8 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
{
//LOG->Trace( "Steps::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
int iMissIfOlderThanThisRow;
const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds;
{
const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds;
bool bFreeze, bDelay;
float fMissIfOlderThanThisBeat;
float fThrowAway;
@@ -2550,9 +2559,9 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
if( !NeedsTapJudging(tn) )
continue;
// warp hackery
if( iter.Row() >= GAMESTATE->m_iWarpBeginRow && iter.Row() <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
// Ignore all notes that are skipped via WARPS.
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
continue;
if( tn.type == TapNote::mine )
@@ -2568,11 +2577,6 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
}
else
{
// warp hackery: don't score notes within the warp region.
// (Only useful when QuirksMode is enabled.) -aj
if( iter.Row() >= GAMESTATE->m_iWarpBeginRow && iter.Row() <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
continue;
tn.result.tns = TNS_Miss;
}
}
@@ -2592,8 +2596,7 @@ void Player::UpdateJudgedRows()
int iRow = iter.Row();
// if row is within a warp section, ignore it. -aj
if( iRow >= GAMESTATE->m_iWarpBeginRow &&
iRow <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iRow) )
continue;
if( iLastSeenRow != iRow )
@@ -2928,8 +2931,7 @@ void Player::HandleTapRowScore( unsigned row )
#endif
// more warp hackery. -aj
if( row >= (unsigned)GAMESTATE->m_iWarpBeginRow &&
row <= (unsigned)(GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( row ) )
return;
if( GAMESTATE->m_bDemonstrationOrJukebox )
@@ -3033,8 +3035,7 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
#endif
// more warp hackery. -aj
if( iRow >= GAMESTATE->m_iWarpBeginRow &&
iRow <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iRow ) )
return;
// don't accumulate combo if AutoPlay is on.
+22 -2
View File
@@ -80,6 +80,7 @@ AutoScreenMessage( SM_BackFromTimeSignatureNumeratorChange );
AutoScreenMessage( SM_BackFromTimeSignatureDenominatorChange );
AutoScreenMessage( SM_BackFromTickcountChange );
AutoScreenMessage( SM_BackFromComboChange );
AutoScreenMessage( SM_BackFromWarpChange );
AutoScreenMessage( SM_DoSaveAndExit );
AutoScreenMessage( SM_DoExit );
AutoScreenMessage( SM_SaveSuccessful );
@@ -549,7 +550,8 @@ static MenuDef g_TimingDataInformation(
MenuRowDef( ScreenEdit::time_signature_numerator, "Edit time signature (top)", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::time_signature_denominator, "Edit time signature (bottom)", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::tickcount, "Edit tickcount", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::combo, "Edit combo", true, EditMode_Full, true, true, 0, NULL )
MenuRowDef( ScreenEdit::combo, "Edit combo", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::warp, "Edit warp", true, EditMode_Full, true, true, 0, NULL )
);
enum { song_bganimation, song_movie, song_bitmap, global_bganimation, global_movie, global_movie_song_group, global_movie_song_group_and_genre, dynamic_random, baked_random, none };
@@ -2639,6 +2641,13 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM )
}
SetDirty( true );
}
else if ( SM == SM_BackFromWarpChange )
{
float fWarp = StringToFloat( ScreenTextEntry::s_sLastAnswer );
if( fWarp > 0 )
m_pSong->m_Timing.SetWarpAtBeat( GAMESTATE->m_fSongBeat, fWarp );
SetDirty( true );
}
else if( SM == SM_BackFromBGChange )
{
HandleBGChangeChoice( (BGChangeChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers );
@@ -3176,6 +3185,7 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector<int> &iAns
g_TimingDataInformation.rows[time_signature_denominator].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d", pTime.GetComboAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetWarpAtBeat( fBeat ) ) );
EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation );
}
@@ -3493,7 +3503,7 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAns
// don't move the step from where it is, just move everything later
NoteDataUtil::InsertRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) );
m_pSong->m_Timing.InsertRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopSeconds) );
m_pSong->m_Timing.InsertRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) );
}
break;
case undo:
@@ -3609,6 +3619,7 @@ static LocalizedString ENTER_TIME_SIGNATURE_NUMERATOR_VALUE ( "ScreenEdit", "Ent
static LocalizedString ENTER_TIME_SIGNATURE_DENOMINATOR_VALUE ( "ScreenEdit", "Enter a new Time Signature denominator value." );
static LocalizedString ENTER_TICKCOUNT_VALUE ( "ScreenEdit", "Enter a new Tickcount value." );
static LocalizedString ENTER_COMBO_VALUE ( "ScreenEdit", "Enter a new Combo value." );
static LocalizedString ENTER_WARP_VALUE ( "ScreenEdit", "Enter a new Warp value." );
void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector<int> &iAnswers )
{
switch( c )
@@ -3673,6 +3684,15 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice
4
);
break;
case warp:
// todo: Call a screen with class ScreenTextEntry instead. -aj
ScreenTextEntry::TextEntry(
SM_BackFromWarpChange,
ENTER_WARP_VALUE,
ssprintf( "%.4f", m_pSong->m_Timing.GetWarpAtRow( BeatToNoteRow(GAMESTATE->m_fSongBeat) ) ),
10
);
break;
}
}
+1
View File
@@ -456,6 +456,7 @@ public:
time_signature_denominator,
tickcount,
combo,
warp,
NUM_TIMING_DATA_INFORMATION_CHOICES
};
+1 -1
View File
@@ -41,7 +41,7 @@
* @brief The internal version of the cache for StepMania.
*
* Increment this value to invalidate the current cache. */
const int FILE_CACHE_VERSION = 166;
const int FILE_CACHE_VERSION = 167;
/** @brief How long does a song sample last by default? */
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
+2 -2
View File
@@ -17,7 +17,7 @@ struct lua_State;
struct BackgroundChange;
/** @brief The version of the .ssc file format. */
const static float STEPFILE_VERSION_NUMBER = 0.55f;
const static float STEPFILE_VERSION_NUMBER = 0.56f;
/** @brief How many edits for this song can each profile have? */
const int MAX_EDITS_PER_SONG_PER_PROFILE = 5;
@@ -307,7 +307,7 @@ public:
void AddBPMSegment( const BPMSegment &seg ) { m_Timing.AddBPMSegment( seg ); }
void AddStopSegment( const StopSegment &seg ) { m_Timing.AddStopSegment( seg ); }
//void AddWarpSegment( const WarpSegment &seg ) { m_Timing.AddWarpSegment( seg ); }
void AddWarpSegment( const WarpSegment &seg ) { m_Timing.AddWarpSegment( seg ); }
void AddBackgroundChange( BackgroundLayer blLayer, BackgroundChange seg );
void AddForegroundChange( BackgroundChange seg );
void AddLyricSegment( LyricSegment seg );
+322 -208
View File
@@ -150,12 +150,30 @@ void TimingData::SetTimeSignatureDenominatorAtRow( int iRow, int iDenominator )
SetTimeSignatureAtRow( iRow, GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iRow ) ).m_iNumerator, iDenominator );
}
/*
void TimingData::SetWarpAtRow( int iRowAt, float fLengthBeats )
void TimingData::SetWarpAtRow( int iRow, float fNew )
{
// todo: code this -aj
unsigned i;
for( i=0; i<m_WarpSegments.size(); i++ )
if( m_WarpSegments[i].m_iStartRow == iRow )
break;
bool valid = iRow > 0 && NoteRowToBeat(iRow) < fNew;
if( i == m_WarpSegments.size() )
{
if( valid )
{
AddWarpSegment( WarpSegment(iRow, fNew) );
}
}
else
{
if( valid )
{
m_WarpSegments[i].m_fEndBeat = fNew;
}
else
m_WarpSegments.erase( m_WarpSegments.begin()+i, m_WarpSegments.begin()+i+1 );
}
}
*/
/* Change an existing Tickcount segment, merge identical segments together or insert a new one. */
void TimingData::SetTickcountAtRow( int iRow, int iTicks )
@@ -229,13 +247,13 @@ int TimingData::GetComboAtRow( int iNoteRow ) const
return m_ComboSegments[GetComboSegmentIndexAtRow( iNoteRow )].m_iCombo;
}
int TimingData::GetWarpToRow( int iWarpBeginRow ) const
float TimingData::GetWarpAtRow( int iWarpRow ) const
{
for( unsigned i=0; i<m_WarpSegments.size(); i++ )
{
if( m_WarpSegments[i].m_iStartRow == iWarpBeginRow )
if( m_WarpSegments[i].m_iStartRow == iWarpRow )
{
return iWarpBeginRow + BeatToNoteRow(m_WarpSegments[i].m_fWarpBeats);
return m_WarpSegments[i].m_fEndBeat;
}
}
return 0;
@@ -296,7 +314,7 @@ int TimingData::GetBPMSegmentIndexAtRow( int iNoteRow ) const
for( i=0; i<m_BPMSegments.size()-1; i++ )
if( m_BPMSegments[i+1].m_iStartRow > iNoteRow )
break;
return (int)i;
return static_cast<int>(i);
}
int TimingData::GetStopSegmentIndexAtRow( int iNoteRow, bool bDelay ) const
@@ -308,16 +326,72 @@ int TimingData::GetStopSegmentIndexAtRow( int iNoteRow, bool bDelay ) const
if( s.m_iStartRow > iNoteRow && s.m_bDelay == bDelay )
break;
}
return (int)i;
return static_cast<int>(i);
}
int TimingData::GetWarpSegmentIndexAtRow( int iNoteRow ) const
{
unsigned i;
for( i=0; i<m_WarpSegments.size()-1; i++ )
{
const WarpSegment& s = m_WarpSegments[i+1];
if( s.m_iStartRow > iNoteRow )
break;
}
return static_cast<int>(i);
}
bool TimingData::IsWarpAtRow( int iNoteRow ) const
{
if( m_WarpSegments.empty() )
return false;
int i = GetWarpSegmentIndexAtRow( iNoteRow );
const WarpSegment& s = m_WarpSegments[i];
if( s.m_iStartRow <= iNoteRow && iNoteRow < BeatToNoteRow(s.m_fEndBeat) )
{
if( m_StopSegments.empty() )
{
return true;
}
if( GetStopAtRow(iNoteRow) != 0.0f )
{
return false;
}
return true;
}
return false;
}
int TimingData::GetTimeSignatureSegmentIndexAtRow( int iRow ) const
{
int i;
for (i=0; i < (int)(m_vTimeSignatureSegments.size()) - 1; i++ )
unsigned i;
for (i=0; i < m_vTimeSignatureSegments.size() - 1; i++ )
if( m_vTimeSignatureSegments[i+1].m_iStartRow > iRow )
break;
return i;
return static_cast<int>(i);
}
int TimingData::GetComboSegmentIndexAtRow( int iRow ) const
{
unsigned i;
for( i=0; i<m_ComboSegments.size()-1; i++ )
{
const ComboSegment& s = m_ComboSegments[i+1];
if( s.m_iStartRow > iRow )
break;
}
return static_cast<int>(i);
}
BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow )
{
static BPMSegment empty;
if( m_BPMSegments.empty() )
return empty;
int i = GetBPMSegmentIndexAtRow( iNoteRow );
return m_BPMSegments[i];
}
TimeSignatureSegment& TimingData::GetTimeSignatureSegmentAtRow( int iRow )
@@ -339,28 +413,6 @@ int TimingData::GetTimeSignatureDenominatorAtRow( int iRow )
return GetTimeSignatureSegmentAtRow( iRow ).m_iDenominator;
}
int TimingData::GetComboSegmentIndexAtRow( int iRow ) const
{
unsigned i;
for( i=0; i<m_ComboSegments.size()-1; i++ )
{
const ComboSegment& s = m_ComboSegments[i+1];
if( s.m_iStartRow > iRow )
break;
}
return (int)i;
}
BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow )
{
static BPMSegment empty;
if( m_BPMSegments.empty() )
return empty;
int i = GetBPMSegmentIndexAtRow( iNoteRow );
return m_BPMSegments[i];
}
ComboSegment& TimingData::GetComboSegmentAtRow( int iRow )
{
unsigned i;
@@ -380,6 +432,16 @@ StopSegment& TimingData::GetStopSegmentAtRow( int iNoteRow, bool bDelay )
return m_StopSegments[i];
}
WarpSegment& TimingData::GetWarpSegmentAtRow( int iRow )
{
static WarpSegment empty;
if( m_WarpSegments.empty() )
return empty;
int i = GetWarpSegmentIndexAtRow( iRow );
return m_WarpSegments[i];
}
int TimingData::GetTickcountSegmentIndexAtRow( int iRow ) const
{
int i;
@@ -411,150 +473,118 @@ void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatO
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut, iWarpBeginOut, fWarpLengthOut );
}
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const
enum
{
// LOG->Trace( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime );
const float fTime = fElapsedTime;
fElapsedTime += m_fBeat0OffsetInSeconds;
FOUND_WARP,
FOUND_WARP_DESTINATION,
FOUND_BPM_CHANGE,
FOUND_STOP,
FOUND_MARKER,
NOT_FOUND
};
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const
{
vector<BPMSegment>::const_iterator itBPMS = m_BPMSegments.begin();
vector<WarpSegment>::const_iterator itWS = m_WarpSegments.begin();
vector<StopSegment>::const_iterator itSS = m_StopSegments.begin();
bFreezeOut = false;
bDelayOut = false;
iWarpBeginOut = -1;
int iLastRow = 0;
float fLastTime = -m_fBeat0OffsetInSeconds;
float fBPS = GetBPMAtRow(0) / 60.0;
float bIsWarping = false;
float fWarpDestination = 0.0;
for( ;; )
{
const int iStartRowThisSegment = m_BPMSegments[i].m_iStartRow;
const float fStartBeatThisSegment = NoteRowToBeat( iStartRowThisSegment );
const bool bIsFirstBPMSegment = i==0;
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1;
const int iStartRowNextSegment = bIsLastBPMSegment ? MAX_NOTE_ROW : m_BPMSegments[i+1].m_iStartRow;
const float fStartBeatNextSegment = NoteRowToBeat( iStartRowNextSegment );
const float fBPS = m_BPMSegments[i].m_fBPS;
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze
int iEventRow = INT_MAX;
int iEventType = NOT_FOUND;
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
{
const bool bIsDelay = m_StopSegments[j].m_bDelay;
if( !bIsFirstBPMSegment && iStartRowThisSegment >= m_StopSegments[j].m_iStartRow )
continue;
if( !bIsLastBPMSegment && m_StopSegments[j].m_iStartRow > iStartRowNextSegment )
continue;
// this freeze lies within this BPMSegment
const int iRowsBeatsSinceStartOfSegment = m_StopSegments[j].m_iStartRow - iStartRowThisSegment;
const float fBeatsSinceStartOfSegment = NoteRowToBeat(iRowsBeatsSinceStartOfSegment);
const float fFreezeStartSecond = fBeatsSinceStartOfSegment / fBPS;
// modified for delays
if( !bIsDelay && fFreezeStartSecond >= fElapsedTime )
break;
if( bIsDelay && fFreezeStartSecond > fElapsedTime )
break;
// the freeze segment is <= current time
fElapsedTime -= m_StopSegments[j].m_fStopSeconds;
if( (fFreezeStartSecond >= fElapsedTime && !bIsDelay) ||
(fFreezeStartSecond > fElapsedTime && bIsDelay) )
iEventRow = BeatToNoteRow(fWarpDestination);
iEventType = FOUND_WARP_DESTINATION;
}
if( itBPMS != m_BPMSegments.end() && itBPMS->m_iStartRow < iEventRow )
{
iEventRow = itBPMS->m_iStartRow;
iEventType = FOUND_BPM_CHANGE;
}
if( itSS != m_StopSegments.end() && itSS->m_iStartRow < iEventRow )
{
iEventRow = itSS->m_iStartRow;
iEventType = FOUND_STOP;
}
if( itWS != m_WarpSegments.end() && itWS->m_iStartRow < iEventRow )
{
iEventRow = itWS->m_iStartRow;
iEventType = FOUND_WARP;
}
if( iEventType == NOT_FOUND )
{
break;
}
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
float fNextEventTime = fLastTime + fTimeToNextEvent;
if ( fElapsedTime < fNextEventTime )
{
break;
}
fLastTime = fNextEventTime;
switch( iEventType )
{
case FOUND_WARP_DESTINATION:
bIsWarping = false;
break;
case FOUND_BPM_CHANGE:
fBPS = itBPMS->m_fBPS;
itBPMS ++;
break;
case FOUND_STOP: // TODO: update for Delays.
{
// The time lies within the stop.
fBeatOut = NoteRowToBeat(m_StopSegments[j].m_iStartRow);
fBPSOut = fBPS;
bFreezeOut = !bIsDelay;
bDelayOut = bIsDelay;
//iWarpBeginOut = -1;
//fWarpLengthOut = -1;
return;
fTimeToNextEvent = itSS->m_fStopSeconds;
fNextEventTime = fLastTime + fTimeToNextEvent;
const bool bIsDelay = itSS->m_bDelay;
if ( fElapsedTime < fNextEventTime )
{
bFreezeOut = !bIsDelay;
bDelayOut = bIsDelay;
fBeatOut = NoteRowToBeat( itSS->m_iStartRow );
fBPSOut = fBPS;
return;
}
fLastTime = fNextEventTime;
itSS ++;
}
}
// by this point we should have the warps in their own place.
for( unsigned j=0; j<m_WarpSegments.size(); j++ ) // foreach warp
{
if( !bIsFirstBPMSegment && iStartRowThisSegment >= m_WarpSegments[j].m_iStartRow )
continue;
if( !bIsLastBPMSegment && m_WarpSegments[j].m_iStartRow > iStartRowNextSegment )
continue;
/*
const int iRowsBeatsSinceStartOfSegment = m_WarpSegments[j].m_iStartRow - iStartRowThisSegment;
const float fBeatsSinceStartOfSegment = NoteRowToBeat(iRowsBeatsSinceStartOfSegment);
const float fWarpStartSecond = fBeatsSinceStartOfSegment / fBPS;
*/
// the freeze segment is <= current time
//fElapsedTime -= m_WarpSegments[j].m_fWarpBeats;
// this warp lies within this BPMSegment.
/*
if( fWarpStartSecond >= fElapsedTime )
break;
case FOUND_WARP:
bIsWarping = true;
if( itWS->m_fEndBeat > fWarpDestination )
{
// this WarpSegment IS the current segment.
// don't know how to properly handle beatout -aj
//fBeatOut = NoteRowToBeat(m_WarpSegments[j].m_iStartRow);
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
fBPSOut = m_BPMSegments[i+1].m_fBPS;
bFreezeOut = false;
bDelayOut = false;
iWarpBeginOut = m_WarpSegments[j].m_iStartRow;
fWarpLengthOut = m_WarpSegments[j].m_fWarpBeats;
return;
fWarpDestination = itWS->m_fEndBeat;
}
*/
iWarpBeginOut = iEventRow;
fWarpDestinationOut = fWarpDestination;
itWS ++;
break;
}
const float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment;
const float fSecondsInThisSegment = fBeatsInThisSegment / fBPS;
//if(fBPS < 0.0f)
/*
if(fStartBeatThisSegment == 445.500f || fStartBeatThisSegment == 449.500)
{
LOG->Trace( ssprintf("segment (beat %f) beats: %f / seconds: %f / BPS: %f",fStartBeatThisSegment,fBeatsInThisSegment,fSecondsInThisSegment,fBPS) );
}
*/
if( bIsLastBPMSegment || fElapsedTime <= fSecondsInThisSegment )
{
// this BPMSegment IS the current segment.
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
fBPSOut = fBPS;
bFreezeOut = false;
bDelayOut = false;
//iWarpBeginOut;
//fWarpLengthOut;
return;
}
// this BPMSegment is NOT the current segment.
fElapsedTime -= fSecondsInThisSegment;
// xxx: negative testing [aj]
/*
//if(fBPS < 0.0f)
if( (fStartBeatNextSegment >= 445.490f && fStartBeatNextSegment <= 453.72f) || fBPS < 0.0f )
{
//LOG->Trace( ssprintf("beat %f is %f BPS (%f BPM)",fBeatOut,fBPSOut,fBPSOut*60.0f) );
//LOG->Trace( ssprintf("start beat %f + elapsed time %f",fStartBeatThisSegment,fElapsedTime) );
//LOG->Trace( ssprintf("elapsed time is now %f",fElapsedTime) );
}
*/
iLastRow = iEventRow;
}
// If we get here, something has gone wrong. Is everything sorted?
vector<BPMSegment> vBPMS = m_BPMSegments;
vector<StopSegment> vSS = m_StopSegments;
vector<WarpSegment> vWS = m_WarpSegments;
vector<TimeSignatureSegment> vTSS = m_vTimeSignatureSegments;
vector<TickcountSegment> vTS = m_TickcountSegments;
vector<ComboSegment> vCS = m_ComboSegments;
sort( vBPMS.begin(), vBPMS.end() );
sort( vSS.begin(), vSS.end() );
sort( vWS.begin(), vWS.end() );
sort( vTSS.begin(), vTSS.end() );
sort( vTS.begin(), vTS.end() );
sort( vCS.begin(), vCS.end() );
ASSERT_M( vBPMS == m_BPMSegments, "The BPM segments were not sorted!" );
ASSERT_M( vSS == m_StopSegments, "The Stop segments were not sorted!" );
ASSERT_M( vWS == m_WarpSegments, "The Warp segments were not sorted!" );
ASSERT_M( vTSS == m_vTimeSignatureSegments, "The Time Signature segments were not sorted!" );
ASSERT_M( vTS == m_TickcountSegments, "The Tickcount segments were not sorted!" );
ASSERT_M( vCS == m_ComboSegments, "The Combo segments were not sorted!" );
FAIL_M( ssprintf("Failed to find the appropriate segment for elapsed time %f.", fTime) );
fBeatOut = NoteRowToBeat( iLastRow ) + (fElapsedTime - fLastTime) * fBPS;
fBPSOut = fBPS;
}
float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
{
return TimingData::GetElapsedTimeFromBeatNoOffset( fBeat ) - PREFSMAN->m_fGlobalOffsetSeconds;
@@ -562,53 +592,86 @@ float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
{
float fElapsedTime = 0;
fElapsedTime -= m_fBeat0OffsetInSeconds;
int iRow = BeatToNoteRow(fBeat);
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze
vector<BPMSegment>::const_iterator itBPMS = m_BPMSegments.begin();
vector<WarpSegment>::const_iterator itWS = m_WarpSegments.begin();
vector<StopSegment>::const_iterator itSS = m_StopSegments.begin();
int iLastRow = 0;
float fLastTime = -m_fBeat0OffsetInSeconds;
float fBPS = GetBPMAtRow(0) / 60.0;
float bIsWarping = false;
float fWarpDestination = 0.0;
for( ;; )
{
/* A traditional stop has the beat happening before the stop. (>=)
* A Pump delay acts differently: the pause is before the beat. (>)
*/
if( ( m_StopSegments[j].m_iStartRow >= iRow && !m_StopSegments[j].m_bDelay ) ||
( m_StopSegments[j].m_iStartRow > iRow && m_StopSegments[j].m_bDelay ) )
int iEventRow = INT_MAX;
int iEventType = NOT_FOUND;
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
{
iEventRow = BeatToNoteRow(fWarpDestination);
iEventType = FOUND_WARP_DESTINATION;
}
if( itBPMS != m_BPMSegments.end() && itBPMS->m_iStartRow < iEventRow )
{
iEventRow = itBPMS->m_iStartRow;
iEventType = FOUND_BPM_CHANGE;
}
if( itSS != m_StopSegments.end() && itSS->m_bDelay && itSS->m_iStartRow < iEventRow ) // delays (come before marker)
{
iEventRow = itSS->m_iStartRow;
iEventType = FOUND_STOP;
}
if( BeatToNoteRow(fBeat) < iEventRow )
{
iEventRow = BeatToNoteRow(fBeat);
iEventType = FOUND_MARKER;
}
if( itSS != m_StopSegments.end() && !itSS->m_bDelay && itSS->m_iStartRow < iEventRow ) // stops (come after marker)
{
iEventRow = itSS->m_iStartRow;
iEventType = FOUND_STOP;
}
if( itWS != m_WarpSegments.end() && itWS->m_iStartRow < iEventRow )
{
iEventRow = itWS->m_iStartRow;
iEventType = FOUND_WARP;
}
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
float fNextEventTime = fLastTime + fTimeToNextEvent;
fLastTime = fNextEventTime;
switch( iEventType )
{
case FOUND_WARP_DESTINATION:
bIsWarping = false;
break;
case FOUND_BPM_CHANGE:
fBPS = itBPMS->m_fBPS;
itBPMS ++;
break;
case FOUND_STOP:
fTimeToNextEvent = itSS->m_fStopSeconds;
fNextEventTime = fLastTime + fTimeToNextEvent;
fLastTime = fNextEventTime;
itSS ++;
break;
case FOUND_MARKER:
return fLastTime;
case FOUND_WARP:
bIsWarping = true;
if( itWS->m_fEndBeat > fWarpDestination )
{
fWarpDestination = itWS->m_fEndBeat;
}
itWS ++;
break;
fElapsedTime += m_StopSegments[j].m_fStopSeconds;
}
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment
{
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1;
const float fBPS = m_BPMSegments[i].m_fBPS;
if( bIsLastBPMSegment )
{
fElapsedTime += NoteRowToBeat( iRow ) / fBPS;
}
else
{
const int iStartIndexThisSegment = m_BPMSegments[i].m_iStartRow;
const int iStartIndexNextSegment = m_BPMSegments[i+1].m_iStartRow;
const int iRowsInThisSegment = min( iStartIndexNextSegment - iStartIndexThisSegment, iRow );
fElapsedTime += NoteRowToBeat( iRowsInThisSegment ) / fBPS;
iRow -= iRowsInThisSegment;
}
if( iRow <= 0 )
return fElapsedTime;
iLastRow = iEventRow;
}
/*
for( unsigned i=0; i<m_WarpSegments.size(); i++ ) // foreach WarpSegment
{
// todo: is this correct? -aj
const int iWarpToRow = m_WarpSegments[i].m_iEndRow;
fElapsedTime += NoteRowToBeat( iWarpToRow );
}
*/
return fElapsedTime;
// won't reach here, unless BeatToNoteRow(fBeat == INT_MAX) (impossible)
}
void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool bAdjustBPM )
@@ -627,7 +690,7 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
else
m_BPMSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex;
}
for( unsigned i = 0; i < m_StopSegments.size(); i++ )
{
const int iSegStartRow = m_StopSegments[i].m_iStartRow;
@@ -639,6 +702,25 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
m_StopSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex;
}
for( unsigned i = 0; i < m_WarpSegments.size(); i++ )
{
const int iSegStartRow = m_WarpSegments[i].m_iStartRow;
const int iSegEndRow = BeatToNoteRow( m_WarpSegments[i].m_fEndBeat );
if( iSegEndRow >= iStartIndex )
{
if( iSegEndRow > iEndIndex )
m_WarpSegments[i].m_fEndBeat += NoteRowToBeat(lrintf((iEndIndex - iStartIndex) * (fScale - 1)));
else
m_WarpSegments[i].m_fEndBeat = NoteRowToBeat(lrintf((iSegEndRow - iStartIndex) * fScale) + iStartIndex);
}
if( iSegStartRow < iStartIndex )
continue;
else if( iSegStartRow > iEndIndex )
m_WarpSegments[i].m_iStartRow += lrintf((iEndIndex - iStartIndex) * (fScale - 1));
else
m_WarpSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex;
}
// adjust BPM changes to preserve timing
if( bAdjustBPM )
{
@@ -683,6 +765,16 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd )
stop.m_iStartRow += iRowsToAdd;
}
for( unsigned i = 0; i < m_WarpSegments.size(); i++ )
{
WarpSegment &warp = m_WarpSegments[i];
if( BeatToNoteRow(warp.m_fEndBeat) >= iStartRow )
warp.m_fEndBeat += NoteRowToBeat(iRowsToAdd);
if( warp.m_iStartRow < iStartRow )
continue;
warp.m_iStartRow += iRowsToAdd;
}
for( unsigned i = 0; i < m_vTimeSignatureSegments.size(); i++ )
{
TimeSignatureSegment &time = m_vTimeSignatureSegments[i];
@@ -764,6 +856,26 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
stop.m_iStartRow -= iRowsToDelete;
}
for( unsigned i = 0; i < m_WarpSegments.size(); i++ )
{
WarpSegment &warp = m_WarpSegments[i];
if( BeatToNoteRow(warp.m_fEndBeat) >= iStartRow )
warp.m_fEndBeat = max( NoteRowToBeat(iStartRow), warp.m_fEndBeat - NoteRowToBeat(iRowsToDelete) );
if( warp.m_iStartRow < iStartRow )
continue;
if( warp.m_iStartRow < iStartRow+iRowsToDelete )
{
m_WarpSegments.erase( m_WarpSegments.begin()+i, m_WarpSegments.begin()+i+1 );
--i;
continue;
}
warp.m_iStartRow -= iRowsToDelete;
}
for( unsigned i = 0; i < m_vTimeSignatureSegments.size(); i++ )
{
TimeSignatureSegment &time = m_vTimeSignatureSegments[i];
@@ -891,6 +1003,7 @@ class LunaTimingData: public Luna<TimingData>
public:
static int HasStops( T* p, lua_State *L ) { lua_pushboolean(L, p->HasStops()); return 1; }
static int HasBPMChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasBpmChanges()); return 1; }
static int HasWarps( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; }
static int GetStops( T* p, lua_State *L )
{
vector<RString> vStops;
@@ -965,6 +1078,7 @@ public:
{
ADD_METHOD( HasStops );
ADD_METHOD( HasBPMChanges );
ADD_METHOD( HasWarps );
ADD_METHOD( GetStops );
ADD_METHOD( GetDelays );
ADD_METHOD( GetBPMs );
+84 -16
View File
@@ -318,25 +318,41 @@ struct TimeSignatureSegment
struct WarpSegment
{
/**
* @brief Creates a simple Warp Segment with default values.
* @brief Create a simple Warp Segment with default values.
*
* It is best to override the values as soon as possible.
*/
WarpSegment() : m_iStartRow(-1), m_fWarpBeats(-1) { }
WarpSegment() : m_iStartRow(-1), m_fEndBeat(-1) { }
/**
* @brief Creates a Warp Segment with the specified starting row and row to warp to.
* @brief Create a Warp Segment with the specified starting row and row to warp to.
* @param s the starting row of this segment.
* @param r the row to warp to.
*/
WarpSegment( int s, int r ): m_iStartRow(max(0, s)),
m_fWarpBeats(max(0, NoteRowToBeat(r))) {}
WarpSegment( int s, int r ): m_iStartRow(max(0, (s < r ? s : r))),
m_fEndBeat(max(0, NoteRowToBeat((r > s ? r : s)))) {}
/**
* @brief Creates a Warp Segment with the specified starting row and beat to warp to.
* @param s the starting row of this segment.
* @param b the beat to warp to.
*/
WarpSegment( int s, float b ): m_iStartRow(max(0, s)),
m_fWarpBeats(max(0, b)) {}
m_fEndBeat(max(0, b)) {}
/**
* @brief Create a Warp Segment with the specified starting beat and row to warp to.
* @param s the starting beat in this segment.
* @param r the row to warp to.
*/
WarpSegment( float s, int r ):
m_iStartRow(max(0, BeatToNoteRow(s))),
m_fEndBeat(max(0, NoteRowToBeat(r))) {}
/**
* @brief Creates a Warp Segment with the specified starting beat and beat to warp to.
* @param s the starting beat of this segment.
* @param b the beat to warp to.
*/
WarpSegment( float s, float b ):
m_iStartRow(max(0, BeatToNoteRow((s < b ? s : b)))),
m_fEndBeat(max(0, (b > s ? b : s))) {}
/**
* @brief The row in which the WarpSegment activates.
*/
@@ -344,7 +360,7 @@ struct WarpSegment
/**
* @brief The beat to warp to.
*/
float m_fWarpBeats;
float m_fEndBeat;
/**
* @brief Compares two WarpSegments to see if they are equal to each other.
* @param other the other WarpSegment to compare to.
@@ -353,7 +369,7 @@ struct WarpSegment
bool operator==( const WarpSegment &other ) const
{
COMPARE( m_iStartRow );
COMPARE( m_fWarpBeats );
COMPARE( m_fEndBeat );
return true;
}
/**
@@ -370,7 +386,7 @@ struct WarpSegment
bool operator<( const WarpSegment &other ) const
{
return m_iStartRow < other.m_iStartRow ||
( m_iStartRow == other.m_iStartRow && m_fWarpBeats < other.m_fWarpBeats );
( m_iStartRow == other.m_iStartRow && m_fEndBeat < other.m_fEndBeat );
}
/**
* @brief Compares two WarpSegments to see if one is less than or equal to the other.
@@ -875,19 +891,71 @@ public:
* @param seg the new TimeSignatureSegment.
*/
void AddTimeSignatureSegment( const TimeSignatureSegment &seg );
/**
* @brief Determine the row to warp to.
* @param iWarpBeginRow The row you start on.
* @return the row you warp to.
* @brief Determine the beat to warp to.
* @param iRow The row you start on.
* @return the beat you warp to.
*/
int GetWarpToRow( int iWarpBeginRow ) const;
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 ); }
/**
* @brief Retrieve the WarpSegment at the specified row.
* @param iRow the row to focus on.
* @return the WarpSegment in question.
*/
WarpSegment& GetWarpSegmentAtRow( int iRow );
/**
* @brief Retrieve the WarpSegment at the specified beat.
* @param fBeat the beat to focus on.
* @return the WarpSegment in question.
*/
WarpSegment& GetWarpSegmentAtBeat( float fBeat ) { return GetWarpSegmentAtRow( BeatToNoteRow( fBeat ) ); }
/**
* @brief Retrieve the index of the WarpSegment at the specified row.
* @param iRow the row to focus on.
* @return the index in question.
*/
int GetWarpSegmentIndexAtRow( int iRow ) const;
/**
* @brief Retrieve the index of the WarpSegment at the specified beat.
* @param fBeat the beat to focus on.
* @return the index in question.
*/
int GetWarpSegmentIndexAtBeat( float fBeat ) const { return GetWarpSegmentIndexAtRow( BeatToNoteRow( fBeat ) ); }
/**
* @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 Add the WarpSegment to the TimingData.
* @param seg the new WarpSegment.
*/
void AddWarpSegment( const WarpSegment &seg );
/**
* @brief Retrieve the Tickcount at the given row.
* @param iNoteRow the row in question.
@@ -1011,7 +1079,7 @@ public:
}
float GetElapsedTimeFromBeat( float fBeat ) const;
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &iWarpLengthOut ) const;
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const;
float GetBeatFromElapsedTimeNoOffset( float fElapsedTime ) const // shortcut for places that care only about the beat
{
float fBeat, fThrowAway, fThrowAway2;