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 Program/StepMania*.lib
crashinfo.txt crashinfo.txt
Utils/temp* Utils/temp*
*.lnk
syntax: regexp 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. Implement .ssc at your own risk.
________________________________________________________________________________ ________________________________________________________________________________
[v0.66] - Wolfman2000
* Implement #WARPS tag to replace negative bpm/stop gimmicks.
[v0.55] - Wolfman2000 [v0.55] - Wolfman2000
* Add #COMBOS tag to the Song and Steps (does nothing here). * Add #COMBOS tag to the Song and Steps (does nothing here).
+7
View File
@@ -13,6 +13,13 @@ _____________________________________________________________________________
sm-ssc v1.2.4 | 2011032? 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 20110320
-------- --------
* [PlayerOptions] Added SetNoteSkin(string) Lua binding. [AJ] * [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 time signature (bottom)=Edit time signature (single beat note value)
Edit tickcount=Edit tickcount Edit tickcount=Edit tickcount
Edit combo=Edit combo Edit combo=Edit combo
Edit warp=Edit warp
Editor options=Options Editor options=Options
EditorShowBGChangesPlay=Show Backgrounds EditorShowBGChangesPlay=Show Backgrounds
Effect=Effect 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 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 Tickcount value.=Enter a new Tickcount value.
Enter a new Combo value.=Enter a new Combo 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 transliteration.=Enter a new artist transliteration.
Enter a new artist.=Enter a new artist. Enter a new artist.=Enter a new artist.
Enter a new genre.=Enter a new genre. Enter a new genre.=Enter a new genre.
+5 -2
View File
@@ -963,6 +963,7 @@ RoutineNoteSkinP2=RoutineSkinP2()
BPMColor=color("1,0,0,1") BPMColor=color("1,0,0,1")
StopColor=color("0.8,0.8,0,1") StopColor=color("0.8,0.8,0,1")
DelayColor=color("0,0.8,0.8,1") DelayColor=color("0,0.8,0.8,1")
WarpColor=color("1,0,0.5,1")
TimeSignatureColor=color("1,0.55,0,1") TimeSignatureColor=color("1,0.55,0,1")
TickcountColor=color("0,1,0,1") TickcountColor=color("0,1,0,1")
ComboColor=color("0.55,1,0,1") ComboColor=color("0.55,1,0,1")
@@ -970,13 +971,15 @@ ComboColor=color("0.55,1,0,1")
BPMIsLeftSide=true BPMIsLeftSide=true
StopIsLeftSide=true StopIsLeftSide=true
DelayIsLeftSide=true DelayIsLeftSide=true
WarpIsLeftSide=false
TimeSignatureIsLeftSide=true TimeSignatureIsLeftSide=true
TickcountIsLeftSide=false TickcountIsLeftSide=false
ComboIsLeftSide=false ComboIsLeftSide=false
# #
BPMOffsetX=60 BPMOffsetX=60
StopOffsetX=10 StopOffsetX=50
DelayOffsetX=10 DelayOffsetX=120
WarpOffsetX=60
TimeSignatureOffsetX=30 TimeSignatureOffsetX=30
TickcountOffsetX=10 TickcountOffsetX=10
ComboOffsetX=30 ComboOffsetX=30
+3 -11
View File
@@ -897,7 +897,7 @@ void GameState::ResetMusicStatistics()
m_bFreeze = false; m_bFreeze = false;
m_bDelay = false; m_bDelay = false;
m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj 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_fMusicSecondsVisible = 0;
m_fSongBeatVisible = 0; m_fSongBeatVisible = 0;
Actor::SetBGMTime( 0, 0, 0, 0 ); Actor::SetBGMTime( 0, 0, 0, 0 );
@@ -943,7 +943,7 @@ void GameState::ResetStageStatistics()
} }
static Preference<float> g_fVisualDelaySeconds( "VisualDelaySeconds", 0.0f ); static Preference<float> g_fVisualDelaySeconds( "VisualDelaySeconds", 0.0f );
// todo: modify for warps -aj
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer &timestamp ) void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer &timestamp )
{ {
if( !timestamp.IsZero() ) 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) ); 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" // "Crash reason : -243478.890625 -48695.773438"
ASSERT_M( m_fSongBeat > -2000, ssprintf("Song beat %f at %f seconds", m_fSongBeat, fPositionSeconds) ); 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_fMusicSeconds = fPositionSeconds;
m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds ); m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds );
+3 -2
View File
@@ -208,9 +208,10 @@ public:
bool m_bFreeze; bool m_bFreeze;
/** @brief A flag to determine if we're in the middle of a delay (Pump style stop). */ /** @brief A flag to determine if we're in the middle of a delay (Pump style stop). */
bool m_bDelay; bool m_bDelay;
// used for warping: /** @brief The row used to start a warp. */
int m_iWarpBeginRow; 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 RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
BroadcastOnChange<bool> m_bGameplayLeadIn; 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 ) FOREACH_CONST( Command, cmds.v, c )
{ {
const Command& cmd = (*c); const Command& cmd = (*c);
RString sName = cmd.GetName(); RString local_sName = cmd.GetName();
s << "\tself:" << sName << "("; s << "\tself:" << local_sName << "(";
for( unsigned i=1; i<cmd.m_vsArgs.size(); i++ ) 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 if( sArg[0] == '#' ) // HTML color
{ {
RageColor c; // in case FromString fails RageColor col; // in case FromString fails
c.FromString( sArg ); col.FromString( sArg );
// c is still valid if FromString fails // col is still valid if FromString fails
s << c.r << "," << c.g << "," << c.b << "," << c.a; s << col.r << "," << col.g << "," << col.b << "," << col.a;
} }
else 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> BPM_COLOR ( "NoteField", "BPMColor" );
static ThemeMetric<RageColor> STOP_COLOR ( "NoteField", "StopColor" ); static ThemeMetric<RageColor> STOP_COLOR ( "NoteField", "StopColor" );
static ThemeMetric<RageColor> DELAY_COLOR ( "NoteField", "DelayColor" ); 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> TIME_SIGNATURE_COLOR ( "NoteField", "TimeSignatureColor" );
static ThemeMetric<RageColor> TICKCOUNT_COLOR ( "NoteField", "TickcountColor" ); static ThemeMetric<RageColor> TICKCOUNT_COLOR ( "NoteField", "TickcountColor" );
static ThemeMetric<RageColor> COMBO_COLOR ( "NoteField", "ComboColor" ); static ThemeMetric<RageColor> COMBO_COLOR ( "NoteField", "ComboColor" );
static ThemeMetric<bool> BPM_IS_LEFT_SIDE ( "NoteField", "BPMIsLeftSide" ); static ThemeMetric<bool> BPM_IS_LEFT_SIDE ( "NoteField", "BPMIsLeftSide" );
static ThemeMetric<bool> STOP_IS_LEFT_SIDE ( "NoteField", "StopIsLeftSide" ); static ThemeMetric<bool> STOP_IS_LEFT_SIDE ( "NoteField", "StopIsLeftSide" );
static ThemeMetric<bool> DELAY_IS_LEFT_SIDE ( "NoteField", "DelayIsLeftSide" ); 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> TIME_SIGNATURE_IS_LEFT_SIDE ( "NoteField", "TimeSignatureIsLeftSide" );
static ThemeMetric<bool> TICKCOUNT_IS_LEFT_SIDE ( "NoteField", "TickcountIsLeftSide" ); static ThemeMetric<bool> TICKCOUNT_IS_LEFT_SIDE ( "NoteField", "TickcountIsLeftSide" );
static ThemeMetric<bool> COMBO_IS_LEFT_SIDE ( "NoteField", "ComboIsLeftSide" ); static ThemeMetric<bool> COMBO_IS_LEFT_SIDE ( "NoteField", "ComboIsLeftSide" );
static ThemeMetric<float> BPM_OFFSETX ( "NoteField", "BPMOffsetX" ); static ThemeMetric<float> BPM_OFFSETX ( "NoteField", "BPMOffsetX" );
static ThemeMetric<float> STOP_OFFSETX ( "NoteField", "StopOffsetX" ); static ThemeMetric<float> STOP_OFFSETX ( "NoteField", "StopOffsetX" );
static ThemeMetric<float> DELAY_OFFSETX ( "NoteField", "DelayOffsetX" ); 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> TIME_SIGNATURE_OFFSETX ( "NoteField", "TimeSignatureOffsetX" );
static ThemeMetric<float> TICKCOUNT_OFFSETX ( "NoteField", "TickcountOffsetX" ); static ThemeMetric<float> TICKCOUNT_OFFSETX ( "NoteField", "TickcountOffsetX" );
static ThemeMetric<float> COMBO_OFFSETX ( "NoteField", "ComboOffsetX" ); 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(); 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 ) void NoteField::DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator )
{ {
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat ); const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
@@ -771,6 +791,19 @@ void NoteField::DrawPrimitives()
} }
} }
// 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 // Time Signature text
const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments; const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments;
for( unsigned i=0; i<vTimeSignatureSegments.size(); i++ ) for( unsigned i=0; i<vTimeSignatureSegments.size(); i++ )
@@ -810,8 +843,6 @@ void NoteField::DrawPrimitives()
} }
} }
// todo: add warp text -aj
// Course mods text // Course mods text
const Course *pCourse = GAMESTATE->m_pCurCourse; const Course *pCourse = GAMESTATE->m_pCurCourse;
if( pCourse ) if( pCourse )
+1
View File
@@ -57,6 +57,7 @@ protected:
void DrawAreaHighlight( int iStartBeat, int iEndBeat ); void DrawAreaHighlight( int iStartBeat, int iEndBeat );
void DrawBPMText( const float fBeat, const float fBPM ); void DrawBPMText( const float fBeat, const float fBPM );
void DrawFreezeText( const float fBeat, const float fBPM, const float bDelay ); 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 DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator );
void DrawTickcountText( const float fBeat, int iTicks ); void DrawTickcountText( const float fBeat, int iTicks );
void DrawComboText( const float fBeat, int iCombo ); void DrawComboText( const float fBeat, int iCombo );
+115 -162
View File
@@ -12,7 +12,13 @@
#include "Steps.h" #include "Steps.h"
#include "PrefsManager.h" #include "PrefsManager.h"
/** @brief The maximum file size for edits. */
const int MAX_EDIT_STEPS_SIZE_BYTES = 60*1024; // 60KB 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( void SMLoader::LoadFromSMTokens(
RString sStepsType, RString sStepsType,
@@ -104,9 +110,6 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
out.m_WarpSegments.clear(); out.m_WarpSegments.clear();
out.m_vTimeSignatureSegments.clear(); out.m_vTimeSignatureSegments.clear();
vector<WarpSegment> arrayWarpsFromNegativeBPMs;
//vector<WarpSegment> arrayWarpsFromNegativeStops;
for( unsigned i=0; i<msd.GetNumValues(); i++ ) for( unsigned i=0; i<msd.GetNumValues(); i++ )
{ {
const MsdFile::value_t &sParams = msd.GetValue(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] ); 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" ) else if( sValueName=="STOPS" || sValueName=="FREEZES" )
{ {
vector<RString> arrayFreezeExpressions; vector<RString> arrayFreezeExpressions;
split( sParams[1], ",", 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++ ) for( unsigned f=0; f<arrayFreezeExpressions.size(); f++ )
{ {
vector<RString> arrayFreezeValues; vector<RString> arrayFreezeValues;
@@ -136,29 +211,49 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] ); const float fFreezeBeat = StringToFloat( arrayFreezeValues[0] );
const float fFreezeSeconds = StringToFloat( arrayFreezeValues[1] ); 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 ); BPMSegment oldBPM = out.GetBPMSegmentAtRow(BeatToNoteRow(negBeat));
out.AddStopSegment( new_seg ); 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 negBeat = fFreezeBeat;
if( PREFSMAN->m_bQuirksMode ) negPause = -fFreezeSeconds;
}
else if( fFreezeSeconds > 0.0f )
{ {
// LOG->Trace( "Adding a negative freeze segment: beat: %f, seconds = %f", new_seg.m_fStartBeat, new_seg.m_fStopSeconds ); StopSegment ss( BeatToNoteRow(fFreezeBeat), fFreezeSeconds );
out.AddStopSegment( new_seg ); out.AddStopSegment( ss );
}
else
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid stop at beat %f, length %f.", fFreezeBeat, fFreezeSeconds );
} }
}
// 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" ) else if( sValueName=="DELAYS" )
{ {
vector<RString> arrayDelayExpressions; vector<RString> arrayDelayExpressions;
@@ -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" ) else if( sValueName=="TIMESIGNATURES" )
{ {
vector<RString> vs1; vector<RString> vs1;
@@ -366,61 +372,8 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
} }
} }
} }
// Ensure all of the warps are handled right.
// warps (replacement for Negative BPM and Negative Stops) sort(out.m_WarpSegments.begin(), out.m_WarpSegments.end());
/*
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());
} }
} }
+29
View File
@@ -486,6 +486,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" ) else if( sValueName=="TIMESIGNATURES" )
{ {
vector<RString> vs1; vector<RString> vs1;
+3
View File
@@ -21,7 +21,10 @@ enum SSCLoadingStates
NUM_SSCLoadingStates /**< The number of states used. */ 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; 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. * @brief The SSCLoader handles all of the parsing needed for .ssc files.
+16 -3
View File
@@ -144,18 +144,18 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
} }
f.PutLine( ";" ); f.PutLine( ";" );
/*
f.Write( "#WARPS:" ); f.Write( "#WARPS:" );
for( unsigned i=0; i<out.m_Timing.m_WarpSegments.size(); i++ ) for( unsigned i=0; i<out.m_Timing.m_WarpSegments.size(); i++ )
{ {
const WarpSegment &ws = out.m_Timing.m_WarpSegments[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 ) if( i != out.m_Timing.m_WarpSegments.size()-1 )
f.Write( "," ); f.Write( "," );
} }
f.PutLine( ";" ); f.PutLine( ";" );
*/
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() ); ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
f.Write( "#TIMESIGNATURES:" ); 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( "#BPMS:;" );
lines.push_back( "#STOPS:;" ); lines.push_back( "#STOPS:;" );
lines.push_back( "#DELAYS:;" ); lines.push_back( "#DELAYS:;" );
lines.push_back( "#WARPS:;" );
lines.push_back( "#TIMESIGNATURES:;" ); lines.push_back( "#TIMESIGNATURES:;" );
lines.push_back( "#TICKCOUNTS:;" ); lines.push_back( "#TICKCOUNTS:;" );
lines.push_back( "#ATTACKS:;" ); lines.push_back( "#ATTACKS:;" );
@@ -337,6 +338,18 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa
} }
lines.push_back( ssprintf( "#DELAYS:%s;", join("\n,", asDelayValues).c_str() ) ); 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() ); ASSERT( !in.m_Timing.m_vTimeSignatureSegments.empty() );
vector<RString> asTimeSigValues; vector<RString> asTimeSigValues;
FOREACH_CONST( TimeSignatureSegment, in.m_Timing.m_vTimeSignatureSegments, iter ) FOREACH_CONST( TimeSignatureSegment, in.m_Timing.m_vTimeSignatureSegments, iter )
+17 -16
View File
@@ -1520,6 +1520,8 @@ int Player::GetClosestNoteDirectional( int col, int iStartRow, int iEndRow, bool
// Is this the row we want? // Is this the row we want?
do { do {
const TapNote &tn = begin->second; const TapNote &tn = begin->second;
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( begin->first ) )
break;
if( tn.type == TapNote::empty ) if( tn.type == TapNote::empty )
break; break;
if( !bAllowGraded && tn.result.tns != TNS_None ) if( !bAllowGraded && tn.result.tns != TNS_None )
@@ -1569,6 +1571,11 @@ int Player::GetClosestNonEmptyRowDirectional( int iStartRow, int iEndRow, bool b
++iter; ++iter;
continue; continue;
} }
if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
{
++iter;
continue;
}
return iter.Row(); 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 * Either option would fundamentally change the grading of two quick notes
* "jack hammers." Hmm. * "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; int iRowOfOverlappingNoteOrRow = row;
if( row == -1 ) if( row == -1 )
{ {
@@ -2040,7 +2047,9 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b
{ {
case TapNote::mine: case TapNote::mine:
// Stepped too close to 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; score = TNS_HitMine;
break; break;
@@ -2522,8 +2531,8 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
{ {
//LOG->Trace( "Steps::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat ); //LOG->Trace( "Steps::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
int iMissIfOlderThanThisRow; int iMissIfOlderThanThisRow;
{
const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds; const float fEarliestTime = GAMESTATE->m_fMusicSeconds - fMissIfOlderThanSeconds;
{
bool bFreeze, bDelay; bool bFreeze, bDelay;
float fMissIfOlderThanThisBeat; float fMissIfOlderThanThisBeat;
float fThrowAway; float fThrowAway;
@@ -2551,8 +2560,8 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
if( !NeedsTapJudging(tn) ) if( !NeedsTapJudging(tn) )
continue; continue;
// warp hackery // Ignore all notes that are skipped via WARPS.
if( iter.Row() >= GAMESTATE->m_iWarpBeginRow && iter.Row() <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) ) if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iter.Row() ) )
continue; continue;
if( tn.type == TapNote::mine ) if( tn.type == TapNote::mine )
@@ -2568,11 +2577,6 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
} }
else 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; tn.result.tns = TNS_Miss;
} }
} }
@@ -2592,8 +2596,7 @@ void Player::UpdateJudgedRows()
int iRow = iter.Row(); int iRow = iter.Row();
// if row is within a warp section, ignore it. -aj // if row is within a warp section, ignore it. -aj
if( iRow >= GAMESTATE->m_iWarpBeginRow && if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow(iRow) )
iRow <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
continue; continue;
if( iLastSeenRow != iRow ) if( iLastSeenRow != iRow )
@@ -2928,8 +2931,7 @@ void Player::HandleTapRowScore( unsigned row )
#endif #endif
// more warp hackery. -aj // more warp hackery. -aj
if( row >= (unsigned)GAMESTATE->m_iWarpBeginRow && if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( row ) )
row <= (unsigned)(GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
return; return;
if( GAMESTATE->m_bDemonstrationOrJukebox ) if( GAMESTATE->m_bDemonstrationOrJukebox )
@@ -3033,8 +3035,7 @@ void Player::HandleHoldCheckpoint( int iRow, int iNumHoldsHeldThisRow, int iNumH
#endif #endif
// more warp hackery. -aj // more warp hackery. -aj
if( iRow >= GAMESTATE->m_iWarpBeginRow && if( GAMESTATE->m_pCurSong->m_Timing.IsWarpAtRow( iRow ) )
iRow <= (GAMESTATE->m_iWarpBeginRow + BeatToNoteRow(GAMESTATE->m_fWarpLength)) )
return; return;
// don't accumulate combo if AutoPlay is on. // don't accumulate combo if AutoPlay is on.
+22 -2
View File
@@ -80,6 +80,7 @@ AutoScreenMessage( SM_BackFromTimeSignatureNumeratorChange );
AutoScreenMessage( SM_BackFromTimeSignatureDenominatorChange ); AutoScreenMessage( SM_BackFromTimeSignatureDenominatorChange );
AutoScreenMessage( SM_BackFromTickcountChange ); AutoScreenMessage( SM_BackFromTickcountChange );
AutoScreenMessage( SM_BackFromComboChange ); AutoScreenMessage( SM_BackFromComboChange );
AutoScreenMessage( SM_BackFromWarpChange );
AutoScreenMessage( SM_DoSaveAndExit ); AutoScreenMessage( SM_DoSaveAndExit );
AutoScreenMessage( SM_DoExit ); AutoScreenMessage( SM_DoExit );
AutoScreenMessage( SM_SaveSuccessful ); 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_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::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::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 }; 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 ); 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 ) else if( SM == SM_BackFromBGChange )
{ {
HandleBGChangeChoice( (BGChangeChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); 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[time_signature_denominator].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d", pTime.GetComboAtBeat( 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 ); 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 // don't move the step from where it is, just move everything later
NoteDataUtil::InsertRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) ); 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; break;
case undo: 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_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_TICKCOUNT_VALUE ( "ScreenEdit", "Enter a new Tickcount value." );
static LocalizedString ENTER_COMBO_VALUE ( "ScreenEdit", "Enter a new Combo 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 ) void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector<int> &iAnswers )
{ {
switch( c ) switch( c )
@@ -3673,6 +3684,15 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice
4 4
); );
break; 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, time_signature_denominator,
tickcount, tickcount,
combo, combo,
warp,
NUM_TIMING_DATA_INFORMATION_CHOICES NUM_TIMING_DATA_INFORMATION_CHOICES
}; };
+1 -1
View File
@@ -41,7 +41,7 @@
* @brief The internal version of the cache for StepMania. * @brief The internal version of the cache for StepMania.
* *
* Increment this value to invalidate the current cache. */ * 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? */ /** @brief How long does a song sample last by default? */
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
+2 -2
View File
@@ -17,7 +17,7 @@ struct lua_State;
struct BackgroundChange; struct BackgroundChange;
/** @brief The version of the .ssc file format. */ /** @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? */ /** @brief How many edits for this song can each profile have? */
const int MAX_EDITS_PER_SONG_PER_PROFILE = 5; const int MAX_EDITS_PER_SONG_PER_PROFILE = 5;
@@ -307,7 +307,7 @@ public:
void AddBPMSegment( const BPMSegment &seg ) { m_Timing.AddBPMSegment( seg ); } void AddBPMSegment( const BPMSegment &seg ) { m_Timing.AddBPMSegment( seg ); }
void AddStopSegment( const StopSegment &seg ) { m_Timing.AddStopSegment( 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 AddBackgroundChange( BackgroundLayer blLayer, BackgroundChange seg );
void AddForegroundChange( BackgroundChange seg ); void AddForegroundChange( BackgroundChange seg );
void AddLyricSegment( LyricSegment seg ); void AddLyricSegment( LyricSegment seg );
+313 -199
View File
@@ -150,12 +150,30 @@ void TimingData::SetTimeSignatureDenominatorAtRow( int iRow, int iDenominator )
SetTimeSignatureAtRow( iRow, GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iRow ) ).m_iNumerator, iDenominator ); SetTimeSignatureAtRow( iRow, GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iRow ) ).m_iNumerator, iDenominator );
} }
/* void TimingData::SetWarpAtRow( int iRow, float fNew )
void TimingData::SetWarpAtRow( int iRowAt, float fLengthBeats )
{ {
// 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. */ /* Change an existing Tickcount segment, merge identical segments together or insert a new one. */
void TimingData::SetTickcountAtRow( int iRow, int iTicks ) void TimingData::SetTickcountAtRow( int iRow, int iTicks )
@@ -229,13 +247,13 @@ int TimingData::GetComboAtRow( int iNoteRow ) const
return m_ComboSegments[GetComboSegmentIndexAtRow( iNoteRow )].m_iCombo; 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++ ) 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; return 0;
@@ -296,7 +314,7 @@ int TimingData::GetBPMSegmentIndexAtRow( int iNoteRow ) const
for( i=0; i<m_BPMSegments.size()-1; i++ ) for( i=0; i<m_BPMSegments.size()-1; i++ )
if( m_BPMSegments[i+1].m_iStartRow > iNoteRow ) if( m_BPMSegments[i+1].m_iStartRow > iNoteRow )
break; break;
return (int)i; return static_cast<int>(i);
} }
int TimingData::GetStopSegmentIndexAtRow( int iNoteRow, bool bDelay ) const 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 ) if( s.m_iStartRow > iNoteRow && s.m_bDelay == bDelay )
break; 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 TimingData::GetTimeSignatureSegmentIndexAtRow( int iRow ) const
{ {
int i; unsigned i;
for (i=0; i < (int)(m_vTimeSignatureSegments.size()) - 1; i++ ) for (i=0; i < m_vTimeSignatureSegments.size() - 1; i++ )
if( m_vTimeSignatureSegments[i+1].m_iStartRow > iRow ) if( m_vTimeSignatureSegments[i+1].m_iStartRow > iRow )
break; 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 ) TimeSignatureSegment& TimingData::GetTimeSignatureSegmentAtRow( int iRow )
@@ -339,28 +413,6 @@ int TimingData::GetTimeSignatureDenominatorAtRow( int iRow )
return GetTimeSignatureSegmentAtRow( iRow ).m_iDenominator; 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 ) ComboSegment& TimingData::GetComboSegmentAtRow( int iRow )
{ {
unsigned i; unsigned i;
@@ -380,6 +432,16 @@ StopSegment& TimingData::GetStopSegmentAtRow( int iNoteRow, bool bDelay )
return m_StopSegments[i]; 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 TimingData::GetTickcountSegmentIndexAtRow( int iRow ) const
{ {
int i; int i;
@@ -411,150 +473,118 @@ void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatO
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut, iWarpBeginOut, fWarpLengthOut ); 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 ); FOUND_WARP,
const float fTime = fElapsedTime; FOUND_WARP_DESTINATION,
fElapsedTime += m_fBeat0OffsetInSeconds; 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; int iEventRow = INT_MAX;
const float fStartBeatThisSegment = NoteRowToBeat( iStartRowThisSegment ); int iEventType = NOT_FOUND;
const bool bIsFirstBPMSegment = i==0; if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1; {
const int iStartRowNextSegment = bIsLastBPMSegment ? MAX_NOTE_ROW : m_BPMSegments[i+1].m_iStartRow; iEventRow = BeatToNoteRow(fWarpDestination);
const float fStartBeatNextSegment = NoteRowToBeat( iStartRowNextSegment ); iEventType = FOUND_WARP_DESTINATION;
const float fBPS = m_BPMSegments[i].m_fBPS; }
if( itBPMS != m_BPMSegments.end() && itBPMS->m_iStartRow < iEventRow )
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze {
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 )
{ {
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; break;
if( bIsDelay && fFreezeStartSecond > fElapsedTime ) }
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
float fNextEventTime = fLastTime + fTimeToNextEvent;
if ( fElapsedTime < fNextEventTime )
{
break; break;
}
// the freeze segment is <= current time fLastTime = fNextEventTime;
fElapsedTime -= m_StopSegments[j].m_fStopSeconds; switch( iEventType )
{
if( (fFreezeStartSecond >= fElapsedTime && !bIsDelay) || case FOUND_WARP_DESTINATION:
(fFreezeStartSecond > fElapsedTime && bIsDelay) ) bIsWarping = false;
break;
case FOUND_BPM_CHANGE:
fBPS = itBPMS->m_fBPS;
itBPMS ++;
break;
case FOUND_STOP: // TODO: update for Delays.
{
fTimeToNextEvent = itSS->m_fStopSeconds;
fNextEventTime = fLastTime + fTimeToNextEvent;
const bool bIsDelay = itSS->m_bDelay;
if ( fElapsedTime < fNextEventTime )
{ {
// The time lies within the stop.
fBeatOut = NoteRowToBeat(m_StopSegments[j].m_iStartRow);
fBPSOut = fBPS;
bFreezeOut = !bIsDelay; bFreezeOut = !bIsDelay;
bDelayOut = bIsDelay; bDelayOut = bIsDelay;
//iWarpBeginOut = -1; fBeatOut = NoteRowToBeat( itSS->m_iStartRow );
//fWarpLengthOut = -1;
return;
}
}
// 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 )
{
// 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;
}
*/
}
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; fBPSOut = fBPS;
bFreezeOut = false;
bDelayOut = false;
//iWarpBeginOut;
//fWarpLengthOut;
return; return;
} }
fLastTime = fNextEventTime;
// this BPMSegment is NOT the current segment. itSS ++;
fElapsedTime -= fSecondsInThisSegment; }
// xxx: negative testing [aj] break;
/* case FOUND_WARP:
//if(fBPS < 0.0f) bIsWarping = true;
if( (fStartBeatNextSegment >= 445.490f && fStartBeatNextSegment <= 453.72f) || fBPS < 0.0f ) if( itWS->m_fEndBeat > fWarpDestination )
{ {
//LOG->Trace( ssprintf("beat %f is %f BPS (%f BPM)",fBeatOut,fBPSOut,fBPSOut*60.0f) ); fWarpDestination = itWS->m_fEndBeat;
//LOG->Trace( ssprintf("start beat %f + elapsed time %f",fStartBeatThisSegment,fElapsedTime) );
//LOG->Trace( ssprintf("elapsed time is now %f",fElapsedTime) );
} }
*/ iWarpBeginOut = iEventRow;
fWarpDestinationOut = fWarpDestination;
itWS ++;
break;
} }
// If we get here, something has gone wrong. Is everything sorted? iLastRow = iEventRow;
vector<BPMSegment> vBPMS = m_BPMSegments; }
vector<StopSegment> vSS = m_StopSegments;
vector<WarpSegment> vWS = m_WarpSegments; fBeatOut = NoteRowToBeat( iLastRow ) + (fElapsedTime - fLastTime) * fBPS;
vector<TimeSignatureSegment> vTSS = m_vTimeSignatureSegments; fBPSOut = fBPS;
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) );
} }
float TimingData::GetElapsedTimeFromBeat( float fBeat ) const float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
{ {
return TimingData::GetElapsedTimeFromBeatNoOffset( fBeat ) - PREFSMAN->m_fGlobalOffsetSeconds; return TimingData::GetElapsedTimeFromBeatNoOffset( fBeat ) - PREFSMAN->m_fGlobalOffsetSeconds;
@@ -562,53 +592,86 @@ float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
{ {
float fElapsedTime = 0;
fElapsedTime -= m_fBeat0OffsetInSeconds;
int iRow = BeatToNoteRow(fBeat); vector<BPMSegment>::const_iterator itBPMS = m_BPMSegments.begin();
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze 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. (>=) int iEventRow = INT_MAX;
* A Pump delay acts differently: the pause is before the beat. (>) int iEventType = NOT_FOUND;
*/ if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
if( ( m_StopSegments[j].m_iStartRow >= iRow && !m_StopSegments[j].m_bDelay ) || {
( m_StopSegments[j].m_iStartRow > iRow && m_StopSegments[j].m_bDelay ) ) 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; break;
fElapsedTime += m_StopSegments[j].m_fStopSeconds; case FOUND_BPM_CHANGE:
} fBPS = itBPMS->m_fBPS;
itBPMS ++;
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment 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 )
{ {
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1; fWarpDestination = itWS->m_fEndBeat;
const float fBPS = m_BPMSegments[i].m_fBPS;
if( bIsLastBPMSegment )
{
fElapsedTime += NoteRowToBeat( iRow ) / fBPS;
} }
else itWS ++;
{ break;
const int iStartIndexThisSegment = m_BPMSegments[i].m_iStartRow; }
const int iStartIndexNextSegment = m_BPMSegments[i+1].m_iStartRow; iLastRow = iEventRow;
const int iRowsInThisSegment = min( iStartIndexNextSegment - iStartIndexThisSegment, iRow );
fElapsedTime += NoteRowToBeat( iRowsInThisSegment ) / fBPS;
iRow -= iRowsInThisSegment;
} }
if( iRow <= 0 ) // won't reach here, unless BeatToNoteRow(fBeat == INT_MAX) (impossible)
return fElapsedTime;
}
/*
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;
} }
void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool bAdjustBPM ) void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool bAdjustBPM )
@@ -639,6 +702,25 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
m_StopSegments[i].m_iStartRow = lrintf((iSegStartRow - iStartIndex) * fScale) + iStartIndex; 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 // adjust BPM changes to preserve timing
if( bAdjustBPM ) if( bAdjustBPM )
{ {
@@ -683,6 +765,16 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd )
stop.m_iStartRow += 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++ ) for( unsigned i = 0; i < m_vTimeSignatureSegments.size(); i++ )
{ {
TimeSignatureSegment &time = m_vTimeSignatureSegments[i]; TimeSignatureSegment &time = m_vTimeSignatureSegments[i];
@@ -764,6 +856,26 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
stop.m_iStartRow -= 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++ ) for( unsigned i = 0; i < m_vTimeSignatureSegments.size(); i++ )
{ {
TimeSignatureSegment &time = m_vTimeSignatureSegments[i]; TimeSignatureSegment &time = m_vTimeSignatureSegments[i];
@@ -891,6 +1003,7 @@ class LunaTimingData: public Luna<TimingData>
public: public:
static int HasStops( T* p, lua_State *L ) { lua_pushboolean(L, p->HasStops()); return 1; } 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 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 ) static int GetStops( T* p, lua_State *L )
{ {
vector<RString> vStops; vector<RString> vStops;
@@ -965,6 +1078,7 @@ public:
{ {
ADD_METHOD( HasStops ); ADD_METHOD( HasStops );
ADD_METHOD( HasBPMChanges ); ADD_METHOD( HasBPMChanges );
ADD_METHOD( HasWarps );
ADD_METHOD( GetStops ); ADD_METHOD( GetStops );
ADD_METHOD( GetDelays ); ADD_METHOD( GetDelays );
ADD_METHOD( GetBPMs ); ADD_METHOD( GetBPMs );
+84 -16
View File
@@ -318,25 +318,41 @@ struct TimeSignatureSegment
struct WarpSegment 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. * 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 s the starting row of this segment.
* @param r the row to warp to. * @param r the row to warp to.
*/ */
WarpSegment( int s, int r ): m_iStartRow(max(0, s)), WarpSegment( int s, int r ): m_iStartRow(max(0, (s < r ? s : r))),
m_fWarpBeats(max(0, NoteRowToBeat(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. * @brief Creates a Warp Segment with the specified starting row and beat to warp to.
* @param s the starting row of this segment. * @param s the starting row of this segment.
* @param b the beat to warp to. * @param b the beat to warp to.
*/ */
WarpSegment( int s, float b ): m_iStartRow(max(0, s)), 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. * @brief The row in which the WarpSegment activates.
*/ */
@@ -344,7 +360,7 @@ struct WarpSegment
/** /**
* @brief The beat to warp to. * @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. * @brief Compares two WarpSegments to see if they are equal to each other.
* @param other the other WarpSegment to compare to. * @param other the other WarpSegment to compare to.
@@ -353,7 +369,7 @@ struct WarpSegment
bool operator==( const WarpSegment &other ) const bool operator==( const WarpSegment &other ) const
{ {
COMPARE( m_iStartRow ); COMPARE( m_iStartRow );
COMPARE( m_fWarpBeats ); COMPARE( m_fEndBeat );
return true; return true;
} }
/** /**
@@ -370,7 +386,7 @@ struct WarpSegment
bool operator<( const WarpSegment &other ) const bool operator<( const WarpSegment &other ) const
{ {
return m_iStartRow < other.m_iStartRow || 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. * @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. * @param seg the new TimeSignatureSegment.
*/ */
void AddTimeSignatureSegment( const TimeSignatureSegment &seg ); void AddTimeSignatureSegment( const TimeSignatureSegment &seg );
/** /**
* @brief Determine the row to warp to. * @brief Determine the beat to warp to.
* @param iWarpBeginRow The row you start on. * @param iRow The row you start on.
* @return the row you warp to. * @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. * @brief Add the WarpSegment to the TimingData.
* @param seg the new WarpSegment. * @param seg the new WarpSegment.
*/ */
void AddWarpSegment( const WarpSegment &seg ); void AddWarpSegment( const WarpSegment &seg );
/** /**
* @brief Retrieve the Tickcount at the given row. * @brief Retrieve the Tickcount at the given row.
* @param iNoteRow the row in question. * @param iNoteRow the row in question.
@@ -1011,7 +1079,7 @@ public:
} }
float GetElapsedTimeFromBeat( float fBeat ) const; 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 GetBeatFromElapsedTimeNoOffset( float fElapsedTime ) const // shortcut for places that care only about the beat
{ {
float fBeat, fThrowAway, fThrowAway2; float fBeat, fThrowAway, fThrowAway2;