diff --git a/Docs/Changelog_SSCformat.txt b/Docs/Changelog_SSCformat.txt index 12cb56d7b8..20088bb92b 100644 --- a/Docs/Changelog_SSCformat.txt +++ b/Docs/Changelog_SSCformat.txt @@ -9,6 +9,11 @@ change to JSON, but it is unsure if this will be done. Implement .ssc at your own risk. ________________________________________________________________________________ +[v0.73] - Wolfman2000 +* Use #FIRSTSECOND, #LASTSECOND, and #LASTSECONDHINT instead of the BEAT + equivalents. +* Move #LASTSECONDHINT to after the Timing tags. + [v0.72] - Wolfman2000 * Have #OFFSET come before #ATTACKS in the Step data. diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 3becdfa38c..51ae1cbde6 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -8,6 +8,12 @@ ________________________________________________________________________________ StepMania 5.0 Preview 2 | 201106?? -------------------------------------------------------------------------------- +2011/06/30 +---------- +* [NotesAll] Use #FIRSTSECOND, #LASTSECOND, and #LASTSECONDHINT instead of the + former BEAT equivalents. Split Timing has made this a necessity thanks to + certain files that abuse Stops/Delays more than usual. [Wolfman2000] + 2011/06/28 ---------- * [ScreenEdit] Add a function to copy/paste the TimingData of a player's diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 1785b00b94..ed192118a5 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1244,9 +1244,11 @@ + + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 2098898a41..40d229921d 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -3084,6 +3084,9 @@ Returns the first beat of the song. + + Returns the first second of the song. + Returns the genre of the song. @@ -3093,6 +3096,9 @@ Returns the last beat of the song. + + Returns the last second of the song. + Gets the path to the lyrics. diff --git a/Docs/SimfileFormats/ssc_msd5.txt b/Docs/SimfileFormats/ssc_msd5.txt index 119f61b297..9c00273a64 100644 --- a/Docs/SimfileFormats/ssc_msd5.txt +++ b/Docs/SimfileFormats/ssc_msd5.txt @@ -21,11 +21,11 @@ #DELAYS:; #LABELS:; #TIMESIGNATURES:; -#LASTBEATHINT:; #SAMPLESTART:; #SAMPLELENGTH:; #DISPLAYBPM:[xxx][xxx:xxx]|[*]; #SELECTABLE:; +#LASTBEATHINT:; #BGCHANGES:; #FGCHANGES:; #KEYSOUNDS:; diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 1c796abc6b..e9f39cb930 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -916,7 +916,7 @@ Insert Entry=Insert Entry Jumps=Jumps Key Joy Mappings=Config Key/Joy Mappings Language=Language -Last beat hint=Last beat hint +Last second hint=Last second hint Layer=Layer LifeDifficulty=Life Difficulty LifeType=Life Type @@ -1256,7 +1256,7 @@ Enter a new chart style.=Enter a new chart style (e.g. "Pad", "Keyboard"). Enter a new meter.=Enter the difficulty rating for this chart. Enter the author who made this step pattern.=Enter the author who made this step pattern. Enter the offset for the song.=Enter the beat 0 offset for the song. -Enter a new last beat hint.=Enter a new last beat hint. +Enter a new last second hint.=Enter a new last second hint. Enter a new main title transliteration.=Enter a new main title transliteration. Enter a new main title.=Enter a new main title. Enter a new sub title transliteration.=Enter a new sub title transliteration. diff --git a/src/Background.cpp b/src/Background.cpp index 19e21abf7a..b86a86027e 100644 --- a/src/Background.cpp +++ b/src/Background.cpp @@ -577,13 +577,16 @@ void BackgroundImpl::LoadFromSong( const Song* pSong ) else // pSong doesn't have an animation plan { Layer &layer = m_Layer[0]; + const TimingData &t = pSong->m_SongTiming; + float firstBeat = t.GetBeatFromElapsedTime(pSong->firstSecond); + float lastBeat = t.GetBeatFromElapsedTime(pSong->lastSecond); - LoadFromRandom( pSong->m_fFirstBeat, pSong->m_fLastBeat, BackgroundChange() ); + LoadFromRandom( firstBeat, lastBeat, BackgroundChange() ); // end showing the static song background BackgroundChange change; change.m_def = m_StaticBackgroundDef; - change.m_fStartBeat = pSong->m_fLastBeat; + change.m_fStartBeat = lastBeat; layer.m_aBGChanges.push_back( change ); } @@ -642,7 +645,7 @@ void BackgroundImpl::LoadFromSong( const Song* pSong ) continue; float fStartBeat = change.m_fStartBeat; - float fEndBeat = pSong->m_fLastBeat; + float fEndBeat = pSong->m_SongTiming.GetBeatFromElapsedTime(pSong->lastSecond); if( i+1 < mainlayer.m_aBGChanges.size() ) fEndBeat = mainlayer.m_aBGChanges[i+1].m_fStartBeat; diff --git a/src/DancingCharacters.cpp b/src/DancingCharacters.cpp index 08088290eb..d9382c42d1 100644 --- a/src/DancingCharacters.cpp +++ b/src/DancingCharacters.cpp @@ -166,7 +166,8 @@ void DancingCharacters::LoadNextSong() m_fThisCameraEndBeat = 0; ASSERT( GAMESTATE->m_pCurSong ); - m_fThisCameraEndBeat = GAMESTATE->m_pCurSong->m_fFirstBeat; + Song &s = *GAMESTATE->m_pCurSong; + m_fThisCameraEndBeat = s.m_SongTiming.GetBeatFromElapsedTime(s.firstSecond); FOREACH_PlayerNumber( p ) if( GAMESTATE->IsPlayerEnabled(p) ) @@ -212,9 +213,10 @@ void DancingCharacters::Update( float fDelta ) bWasGameplayStarting = bGameplayStarting; static float fLastBeat = GAMESTATE->m_Position.m_fSongBeat; + Song &s = *GAMESTATE->m_pCurSong; + float firstBeat = s.m_SongTiming.GetBeatFromElapsedTime(s.firstSecond); float fThisBeat = GAMESTATE->m_Position.m_fSongBeat; - if( fLastBeat < GAMESTATE->m_pCurSong->m_fFirstBeat && - fThisBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat ) + if( fLastBeat < firstBeat && fThisBeat >= firstBeat ) { FOREACH_PlayerNumber( p ) m_pCharacter[p]->PlayAnimation( "dance" ); diff --git a/src/GameState.cpp b/src/GameState.cpp index fe0d7099de..1ba34d53f9 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -991,7 +991,8 @@ update player position code goes here float GameState::GetSongPercent( float beat ) const { // 0 = first step; 1 = last step - return (beat - m_pCurSong->m_fFirstBeat) / m_pCurSong->m_fLastBeat; + float curTime = this->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat(beat); + return (curTime - m_pCurSong->firstSecond) / m_pCurSong->lastSecond; } int GameState::GetNumStagesLeft( PlayerNumber pn ) const diff --git a/src/Inventory.cpp b/src/Inventory.cpp index d3f563c30a..71a30a9ba9 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -119,10 +119,10 @@ void Inventory::Update( float fDelta ) } } - + Song &song = *GAMESTATE->m_pCurSong; // use items if this player is CPU-controlled if( m_pPlayerState->m_PlayerController != PC_HUMAN && - GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fLastBeat ) + GAMESTATE->m_Position.m_fSongBeat < song.m_SongTiming.GetBeatFromElapsedTime(song.lastSecond) ) { // every 1 seconds, try to use an item int iLastSecond = (int)(RageTimer::GetTimeSinceStartFast() - fDelta); diff --git a/src/LyricDisplay.cpp b/src/LyricDisplay.cpp index 7284d2dc17..a6bdfea918 100644 --- a/src/LyricDisplay.cpp +++ b/src/LyricDisplay.cpp @@ -59,8 +59,8 @@ void LyricDisplay::Update( float fDeltaTime ) if( m_iCurLyricNumber+1 < GAMESTATE->m_pCurSong->m_LyricSegments.size() ) fEndTime = pSong->m_LyricSegments[m_iCurLyricNumber+1].m_fStartTime; else - fEndTime = pSong->m_SongTiming.GetElapsedTimeFromBeat( pSong->m_fLastBeat ); - + fEndTime = pSong->lastSecond; + const float fDistance = fEndTime - pSong->m_LyricSegments[m_iCurLyricNumber].m_fStartTime; const float fTweenBufferTime = IN_LENGTH.GetValue() + OUT_LENGTH.GetValue(); diff --git a/src/MeterDisplay.cpp b/src/MeterDisplay.cpp index 641b50d73c..5bc9da2e6a 100644 --- a/src/MeterDisplay.cpp +++ b/src/MeterDisplay.cpp @@ -73,8 +73,8 @@ void SongMeterDisplay::Update( float fDeltaTime ) { if( GAMESTATE->m_pCurSong ) { - float fSongStartSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fFirstBeat ); - float fSongEndSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat ); + float fSongStartSeconds = GAMESTATE->m_pCurSong->firstSecond; + float fSongEndSeconds = GAMESTATE->m_pCurSong->lastSecond; float fPercentPositionSong = SCALE( GAMESTATE->m_Position.m_fMusicSeconds, fSongStartSeconds, fSongEndSeconds, 0.0f, 1.0f ); CLAMP( fPercentPositionSong, 0, 1 ); diff --git a/src/NotesLoaderJson.cpp b/src/NotesLoaderJson.cpp index 9d422d5177..dab70f29ee 100644 --- a/src/NotesLoaderJson.cpp +++ b/src/NotesLoaderJson.cpp @@ -150,8 +150,6 @@ static void Deserialize( Song &out, const Json::Value &root ) else if( sSelectable.EqualsNoCase("NO") ) out.m_SelectionDisplay = out.SHOW_NEVER; - out.m_fFirstBeat = (float)root["FirstBeat"].asDouble(); - out.m_fLastBeat = (float)root["LastBeat"].asDouble(); out.m_sSongFileName = root["SongFileName"].asString(); out.m_bHasMusic = root["HasMusic"].asBool(); out.m_bHasBanner = root["HasBanner"].asBool(); diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index fc5a59273e..82da1ffbbc 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -812,37 +812,19 @@ bool SMLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCache } else if( sValueName=="LASTBEATHINT" ) - out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] ); + { + // unable to identify at this point: ignore + } else if( sValueName=="MUSICBYTES" ) ; /* ignore */ - /* We calculate these. Some SMs in circulation have bogus values for - * these, so make sure we always calculate it ourself. */ - else if( sValueName=="FIRSTBEAT" ) + // cache tags from older SM files: ignore. + else if(sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" || + sValueName=="SONGFILENAME" || sValueName=="HASMUSIC" || + sValueName=="HASBANNER") { - if( bFromCache ) - out.m_fFirstBeat = StringToFloat( sParams[1] ); - } - else if( sValueName=="LASTBEAT" ) - { - if( bFromCache ) - out.m_fLastBeat = StringToFloat( sParams[1] ); - } - else if( sValueName=="SONGFILENAME" ) - { - if( bFromCache ) - out.m_sSongFileName = sParams[1]; - } - else if( sValueName=="HASMUSIC" ) - { - if( bFromCache ) - out.m_bHasMusic = StringToInt( sParams[1] ) != 0; - } - else if( sValueName=="HASBANNER" ) - { - if( bFromCache ) - out.m_bHasBanner = StringToInt( sParams[1] ) != 0; + ; } else if( sValueName=="SAMPLESTART" ) @@ -1108,9 +1090,10 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache ) if( bFromCache ) break; + float lastBeat = song.m_SongTiming.GetBeatFromElapsedTime(song.lastSecond); /* If BGChanges already exist after the last beat, don't add the * background in the middle. */ - if( !bg.empty() && bg.back().m_fStartBeat-0.0001f >= song.m_fLastBeat ) + if( !bg.empty() && bg.back().m_fStartBeat-0.0001f >= lastBeat ) break; // If the last BGA is already the song BGA, don't add a duplicate. @@ -1120,7 +1103,8 @@ void SMLoader::TidyUpData( Song &song, bool bFromCache ) if( !IsAFile( song.GetBackgroundPath() ) ) break; - bg.push_back( BackgroundChange(song.m_fLastBeat,song.m_sBackgroundFile) ); + + bg.push_back( BackgroundChange(lastBeat,song.m_sBackgroundFile) ); } while(0); } song.TidyUpData( bFromCache ); diff --git a/src/NotesLoaderSMA.cpp b/src/NotesLoaderSMA.cpp index 648d54dcca..05db8d42b0 100644 --- a/src/NotesLoaderSMA.cpp +++ b/src/NotesLoaderSMA.cpp @@ -225,30 +225,16 @@ bool SMALoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach } else if( sValueName=="LASTBEATHINT" ) - out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] ); - + { + // can't identify at this position: ignore. + } else if( sValueName=="MUSICBYTES" ) ; /* ignore */ - /* We calculate these. Some SMs in circulation have bogus values for - * these, so make sure we always calculate it ourself. */ - else if( sValueName=="FIRSTBEAT" ) - { - ; - } - else if( sValueName=="LASTBEAT" ) - { - ; - } - else if( sValueName=="SONGFILENAME" ) - { - ; - } - else if( sValueName=="HASMUSIC" ) - { - ; - } - else if( sValueName=="HASBANNER" ) + // Cache tags: ignore. + else if (sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" || + sValueName=="SONGFILENAME" || sValueName=="HASMUSIC" || + sValueName=="HASBANNER" ) { ; } diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index fe02b9181b..d2d92daf51 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -265,7 +265,12 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach else if( sValueName=="LASTBEATHINT" ) { - out.m_fSpecifiedLastBeat = StringToFloat( sParams[1] ); + // unable to parse due to tag position. Ignore. + } + + else if (sValueName == "LASTSECONDHINT") + { + out.specifiedLastSecond = StringToFloat(sParams[1]); } else if( sValueName=="MUSICBYTES" ) @@ -395,16 +400,20 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach /* The following are cache tags. Never fill their values * directly: only from the cached version. */ - else if( sValueName=="FIRSTBEAT" ) + else if( sValueName=="FIRSTBEAT" || sValueName=="LASTBEAT" ) + { + // no longer used. + } + else if (sValueName=="FIRSTSECOND") { if( bFromCache ) - out.m_fFirstBeat = StringToFloat( sParams[1] ); + out.firstSecond = StringToFloat( sParams[1] ); } else if( sValueName=="LASTBEAT" ) { if( bFromCache ) - out.m_fLastBeat = StringToFloat( sParams[1] ); + out.lastSecond = StringToFloat( sParams[1] ); } else if( sValueName=="SONGFILENAME" ) diff --git a/src/NotesWriterJson.cpp b/src/NotesWriterJson.cpp index e5ad6338d9..f0df061323 100644 --- a/src/NotesWriterJson.cpp +++ b/src/NotesWriterJson.cpp @@ -147,8 +147,8 @@ bool NotesWriterJson::WriteSong( const RString &sFile, const Song &out, bool bWr else root["Selectable"] = "YES"; - root["FirstBeat"] = out.m_fFirstBeat; - root["LastBeat"] = out.m_fLastBeat; + root["FirstBeat"] = out.m_SongTiming.GetBeatFromElapsedTime(out.firstSecond); + root["LastBeat"] = out.m_SongTiming.GetBeatFromElapsedTime(out.lastSecond); root["SongFileName"] = out.m_sSongFileName; root["HasMusic"] = out.m_bHasMusic; root["HasBanner"] = out.m_bHasBanner; diff --git a/src/NotesWriterSM.cpp b/src/NotesWriterSM.cpp index 6b9f89c1fe..06934bb06e 100644 --- a/src/NotesWriterSM.cpp +++ b/src/NotesWriterSM.cpp @@ -66,8 +66,8 @@ static void WriteGlobalTags( RageFile &f, Song &out ) f.PutLine( ssprintf( "#OFFSET:%.3f;", out.m_SongTiming.m_fBeat0OffsetInSeconds ) ); f.PutLine( ssprintf( "#SAMPLESTART:%.3f;", out.m_fMusicSampleStartSeconds ) ); f.PutLine( ssprintf( "#SAMPLELENGTH:%.3f;", out.m_fMusicSampleLengthSeconds ) ); - if( out.m_fSpecifiedLastBeat > 0 ) - f.PutLine( ssprintf("#LASTBEATHINT:%.3f;", out.m_fSpecifiedLastBeat) ); + if( out.specifiedLastSecond > 0 ) + f.PutLine( ssprintf("#LASTBEATHINT:%.3f;", out.m_SongTiming.GetBeatFromElapsedTime(out.specifiedLastSecond)) ); f.Write( "#SELECTABLE:" ); switch(out.m_SelectionDisplay) diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index 528f438c66..261cf73c05 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -203,8 +203,6 @@ static void WriteGlobalTags( RageFile &f, const Song &out ) f.PutLine( ssprintf( "#OFFSET:%.6f;", out.m_SongTiming.m_fBeat0OffsetInSeconds ) ); f.PutLine( ssprintf( "#SAMPLESTART:%.6f;", out.m_fMusicSampleStartSeconds ) ); f.PutLine( ssprintf( "#SAMPLELENGTH:%.6f;", out.m_fMusicSampleLengthSeconds ) ); - if( out.m_fSpecifiedLastBeat > 0 ) - f.PutLine( ssprintf("#LASTBEATHINT:%.6f;", out.m_fSpecifiedLastBeat) ); f.Write( "#SELECTABLE:" ); switch(out.m_SelectionDisplay) @@ -234,6 +232,9 @@ static void WriteGlobalTags( RageFile &f, const Song &out ) WriteTimingTags( f, out.m_SongTiming, true ); + if( out.specifiedLastSecond > 0 ) + f.PutLine( ssprintf("#LASTSECONDHINT:%.6f;", out.specifiedLastSecond) ); + FOREACH_BackgroundLayer( b ) { if( b==0 ) @@ -367,8 +368,8 @@ bool NotesWriterSSC::Write( RString sPath, const Song &out, const vector if( bSavingCache ) { f.PutLine( ssprintf( "// cache tags:" ) ); - f.PutLine( ssprintf( "#FIRSTBEAT:%.6f;", out.m_fFirstBeat ) ); - f.PutLine( ssprintf( "#LASTBEAT:%.6f;", out.m_fLastBeat ) ); + f.PutLine( ssprintf( "#FIRSTSECOND:%.6f;", out.firstSecond ) ); + f.PutLine( ssprintf( "#LASTSECOND:%.6f;", out.lastSecond ) ); f.PutLine( ssprintf( "#SONGFILENAME:%s;", out.m_sSongFileName.c_str() ) ); f.PutLine( ssprintf( "#HASMUSIC:%i;", out.m_bHasMusic ) ); f.PutLine( ssprintf( "#HASBANNER:%i;", out.m_bHasBanner ) ); diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 55660d2c05..c14cb5c3b9 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -618,7 +618,7 @@ static MenuDef g_SongInformation( MenuRowDef( ScreenEdit::main_title_transliteration, "Main title transliteration", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::sub_title_transliteration, "Sub title transliteration", true, EditMode_Practice, true, true, 0, NULL ), MenuRowDef( ScreenEdit::artist_transliteration, "Artist transliteration", true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::last_beat_hint, "Last beat hint", true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::last_second_hint, "Last second hint", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::preview_start, "Preview Start", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::preview_length, "Preview Length", true, EditMode_Full, true, true, 0, NULL ), MenuRowDef( ScreenEdit::display_bpm, "Display BPM", true, EditMode_Full, true, true, 0, "Actual", "Specified", "Random" ), @@ -2127,7 +2127,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) if (index >= 0) { - const RString sDuration = ssprintf( "%.5f", attacks[index].fSecsRemaining ); + const RString sDuration = ssprintf( "%.6f", attacks[index].fSecsRemaining ); g_InsertCourseAttack.rows[remove].bEnabled = true; if( g_InsertCourseAttack.rows[duration].choices.size() == 9 ) @@ -2158,7 +2158,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) if( iAttack >= 0 ) { - const RString sDuration = ssprintf( "%.5f", ce.attacks[iAttack].fSecsRemaining ); + const RString sDuration = ssprintf( "%.6f", ce.attacks[iAttack].fSecsRemaining ); g_InsertCourseAttack.rows[remove].bEnabled = true; if( g_InsertCourseAttack.rows[duration].choices.size() == 9 ) @@ -3426,13 +3426,19 @@ static void ChangeArtistTranslit( const RString &sNew ) static void ChangeBeat0Offset( const RString &sNew ) { - TimingData &timing = GAMESTATE->m_bIsUsingStepTiming ? GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : GAMESTATE->m_pCurSong->m_SongTiming; + TimingData &timing = (GAMESTATE->m_bIsUsingStepTiming ? + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : + GAMESTATE->m_pCurSong->m_SongTiming); timing.m_fBeat0OffsetInSeconds = StringToFloat( sNew ); } -static void ChangeLastBeatHint( const RString &sNew ) +static void ChangeLastSecondHint( const RString &sNew ) { - GAMESTATE->m_pCurSong->m_fSpecifiedLastBeat = StringToFloat( sNew ); + Song &s = *GAMESTATE->m_pCurSong; + TimingData &timing = (GAMESTATE->m_bIsUsingStepTiming ? + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing : + s.m_SongTiming); + s.specifiedLastSecond = StringToFloat(sNew); } static void ChangePreviewStart( const RString &sNew ) @@ -3507,23 +3513,23 @@ void ScreenEdit::DisplayTimingMenu() TimingData &pTime = GetAppropriateTiming(); bool bHasSpeedOnThisRow = pTime.GetSpeedSegmentAtBeat( fBeat ).GetBeat() == fBeat; - g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( ssprintf("%.5f", pTime.m_fBeat0OffsetInSeconds) ); - g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetBPMAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetStopAtBeat( fBeat ) ) ) ; - g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetDelayAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( ssprintf("%.6f", pTime.m_fBeat0OffsetInSeconds) ); + g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetBPMAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetStopAtBeat( fBeat ) ) ) ; + g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetDelayAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[time_signature].SetOneUnthemedChoice( ssprintf("%d / %d", pTime.GetTimeSignatureNumeratorAtBeat( fBeat ), pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[label].SetOneUnthemedChoice( pTime.GetLabelAtBeat( fBeat ).c_str() ); 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 ) ) ); - g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.5f", pTime.GetSpeedPercentAtBeat( fBeat ) ) : "---" ); - g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.5f", pTime.GetSpeedWaitAtBeat( fBeat ) ) : "---" ); + g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetWarpAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.6f", pTime.GetSpeedPercentAtBeat( fBeat ) ) : "---" ); + g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? ssprintf("%.6f", pTime.GetSpeedWaitAtBeat( fBeat ) ) : "---" ); RString starting = ( pTime.GetSpeedModeAtBeat( fBeat ) == 1 ? "Seconds" : "Beats" ); g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); - g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetScrollAtBeat( fBeat ) ) ); - g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetFakeAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetScrollAtBeat( fBeat ) ) ); + g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( ssprintf("%.6f", pTime.GetFakeAtBeat( fBeat ) ) ); g_TimingDataInformation.rows[tickcount].bEnabled = GAMESTATE->m_bIsUsingStepTiming; g_TimingDataInformation.rows[combo].bEnabled = GAMESTATE->m_bIsUsingStepTiming; @@ -3733,12 +3739,12 @@ void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAns g_SongInformation.rows[main_title_transliteration].SetOneUnthemedChoice( pSong->m_sMainTitleTranslit ); g_SongInformation.rows[sub_title_transliteration].SetOneUnthemedChoice( pSong->m_sSubTitleTranslit ); g_SongInformation.rows[artist_transliteration].SetOneUnthemedChoice( pSong->m_sArtistTranslit ); - g_SongInformation.rows[last_beat_hint].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedLastBeat) ); - g_SongInformation.rows[preview_start].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fMusicSampleStartSeconds) ); - g_SongInformation.rows[preview_length].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fMusicSampleLengthSeconds) ); + g_SongInformation.rows[last_second_hint].SetOneUnthemedChoice( ssprintf("%.6f", pSong->specifiedLastSecond) ); + g_SongInformation.rows[preview_start].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fMusicSampleStartSeconds) ); + g_SongInformation.rows[preview_length].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fMusicSampleLengthSeconds) ); g_SongInformation.rows[display_bpm].iDefaultChoice = pSong->m_DisplayBPMType; - g_SongInformation.rows[min_bpm].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedBPMMin) ); - g_SongInformation.rows[max_bpm].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedBPMMax) ); + g_SongInformation.rows[min_bpm].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fSpecifiedBPMMin) ); + g_SongInformation.rows[max_bpm].SetOneUnthemedChoice( ssprintf("%.6f", pSong->m_fSpecifiedBPMMax) ); EditMiniMenu( &g_SongInformation, SM_BackFromSongInformation ); } @@ -4206,7 +4212,7 @@ static LocalizedString ENTER_CREDIT ("ScreenEdit","Enter a new credit."); static LocalizedString ENTER_MAIN_TITLE_TRANSLIT ("ScreenEdit","Enter a new main title transliteration."); static LocalizedString ENTER_SUB_TITLE_TRANSLIT ("ScreenEdit","Enter a new sub title transliteration."); static LocalizedString ENTER_ARTIST_TRANSLIT ("ScreenEdit","Enter a new artist transliteration."); -static LocalizedString ENTER_LAST_BEAT_HINT ("ScreenEdit","Enter a new last beat hint."); +static LocalizedString ENTER_LAST_SECOND_HINT ("ScreenEdit","Enter a new last second hint."); static LocalizedString ENTER_PREVIEW_START ("ScreenEdit","Enter a new preview start."); static LocalizedString ENTER_PREVIEW_LENGTH ("ScreenEdit","Enter a new preview length."); static LocalizedString ENTER_MIN_BPM ("ScreenEdit","Enter a new min BPM."); @@ -4242,29 +4248,29 @@ void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vec case artist_transliteration: ScreenTextEntry::TextEntry( SM_None, ENTER_ARTIST_TRANSLIT, pSong->m_sArtistTranslit, 100, NULL, ChangeArtistTranslit, NULL ); break; - case last_beat_hint: - ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_BEAT_HINT, - ssprintf("%.5f", pSong->m_fSpecifiedLastBeat), 20, - ScreenTextEntry::FloatValidate, ChangeLastBeatHint, NULL ); + case last_second_hint: + ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_SECOND_HINT, + ssprintf("%.6f", pSong->specifiedLastSecond), 20, + ScreenTextEntry::FloatValidate, ChangeLastSecondHint, NULL ); break; case preview_start: ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_START, - ssprintf("%.5f", pSong->m_fMusicSampleStartSeconds), 20, + ssprintf("%.6f", pSong->m_fMusicSampleStartSeconds), 20, ScreenTextEntry::FloatValidate, ChangePreviewStart, NULL ); break; case preview_length: ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_LENGTH, - ssprintf("%.5f", pSong->m_fMusicSampleLengthSeconds), 20, + ssprintf("%.6f", pSong->m_fMusicSampleLengthSeconds), 20, ScreenTextEntry::FloatValidate, ChangePreviewLength, NULL ); break; case min_bpm: ScreenTextEntry::TextEntry( SM_None, ENTER_MIN_BPM, - ssprintf("%.5f", pSong->m_fSpecifiedBPMMin), 20, + ssprintf("%.6f", pSong->m_fSpecifiedBPMMin), 20, ScreenTextEntry::FloatValidate, ChangeMinBPM, NULL ); break; case max_bpm: ScreenTextEntry::TextEntry( SM_None, ENTER_MAX_BPM, - ssprintf("%.5f", pSong->m_fSpecifiedBPMMax), 20, + ssprintf("%.6f", pSong->m_fSpecifiedBPMMax), 20, ScreenTextEntry::FloatValidate, ChangeMaxBPM, NULL ); break; }; @@ -4293,7 +4299,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice DEFAULT_FAIL( c ); case beat_0_offset: ScreenTextEntry::TextEntry( SM_None, ENTER_BEAT_0_OFFSET, - ssprintf("%.5f", GetAppropriateTiming().m_fBeat0OffsetInSeconds), 20, + ssprintf("%.6f", GetAppropriateTiming().m_fBeat0OffsetInSeconds), 20, ScreenTextEntry::FloatValidate, ChangeBeat0Offset, NULL ); break; case bpm: @@ -4359,7 +4365,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromWarpChange, ENTER_WARP_VALUE, - ssprintf( "%.4f", GetAppropriateTiming().GetWarpAtBeat( GetBeat() ) ), + ssprintf( "%.6f", GetAppropriateTiming().GetWarpAtBeat( GetBeat() ) ), 10 ); break; @@ -4367,7 +4373,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromSpeedPercentChange, ENTER_SPEED_PERCENT_VALUE, - ssprintf( "%.5f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetRatio() ), + ssprintf( "%.6f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetRatio() ), 10 ); break; @@ -4375,7 +4381,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromScrollChange, ENTER_SCROLL_VALUE, - ssprintf( "%.5f", GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() ).GetRatio() ), + ssprintf( "%.6f", GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() ).GetRatio() ), 10 ); break; @@ -4383,7 +4389,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromSpeedWaitChange, ENTER_SPEED_WAIT_VALUE, - ssprintf( "%.5f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetLength() ), + ssprintf( "%.6f", GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() ).GetLength() ), 10 ); break; @@ -4403,7 +4409,7 @@ void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice ScreenTextEntry::TextEntry( SM_BackFromFakeChange, ENTER_FAKE_VALUE, - ssprintf("%.5f", GetAppropriateTiming().GetFakeAtBeat( GetBeat() ) ), + ssprintf("%.6f", GetAppropriateTiming().GetFakeAtBeat( GetBeat() ) ), 10 ); break; @@ -4701,14 +4707,15 @@ float ScreenEdit::GetMaximumBeatForNewNote() const case EditMode_CourseMods: case EditMode_Home: { - float fEndBeat = GAMESTATE->m_pCurSong->m_fLastBeat; + Song &s = *GAMESTATE->m_pCurSong; + TimingData &timing = s.m_SongTiming; + float fEndBeat = timing.GetBeatFromElapsedTime(s.lastSecond); /* Round up to the next measure end. Some songs end on weird beats * mid-measure, and it's odd to have movement capped to these weird * beats. */ - TimingData &songTiming = GAMESTATE->m_pCurSong->m_SongTiming; float playerBeat = GetAppropriatePosition().m_fSongBeat; - int beatsPerMeasure = songTiming.GetTimeSignatureSegmentAtBeat( playerBeat ).GetNum(); + int beatsPerMeasure = timing.GetTimeSignatureSegmentAtBeat( playerBeat ).GetNum(); fEndBeat += beatsPerMeasure; fEndBeat = ftruncf( fEndBeat, (float)beatsPerMeasure ); @@ -4723,7 +4730,7 @@ float ScreenEdit::GetMaximumBeatForMoving() const { float fEndBeat = GetMaximumBeatForNewNote(); - /* Jump to GetLastBeat even if it's past m_pCurSong->m_fLastBeat + /* Jump to GetLastBeat even if it's past the song's last beat * so that users can delete garbage steps past then end that they have * have inserted in a text editor. Once they delete all steps on * GetLastBeat() and move off of that beat, they won't be able to return. */ diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 255cf2b425..e75a4a854f 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -529,7 +529,7 @@ public: main_title_transliteration, sub_title_transliteration, artist_transliteration, - last_beat_hint, + last_second_hint, preview_start, preview_length, display_bpm, diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 475ca16250..5379530502 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -1329,8 +1329,7 @@ void ScreenGameplay::StartPlayingSong( float fMinTimeToNotes, float fMinTimeToMu p.StopMode = RageSoundParams::M_CONTINUE; { - const float fFirstBeat = GAMESTATE->m_pCurSong->m_fFirstBeat; - const float fFirstSecond = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( fFirstBeat ); + const float fFirstSecond = GAMESTATE->m_pCurSong->firstSecond; float fStartDelay = fMinTimeToNotes - fFirstSecond; fStartDelay = max( fStartDelay, fMinTimeToMusic ); p.m_StartSecond = -fStartDelay; @@ -1412,8 +1411,9 @@ void ScreenGameplay::PlayAnnouncer( RString type, float fSeconds ) /* Don't play before the first beat, or after we're finished. */ if( m_DancingState != STATE_DANCING ) return; - if( GAMESTATE->m_pCurSong == NULL || // this will be true on ScreenDemonstration sometimes - GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat ) + if(GAMESTATE->m_pCurSong == NULL || // this will be true on ScreenDemonstration sometimes + GAMESTATE->m_Position.m_fSongBeat < + GAMESTATE->m_pCurSong->m_SongTiming.GetBeatFromElapsedTime(GAMESTATE->m_pCurSong->firstSecond) ) return; if( m_fTimeSinceLastDancingComment < fSeconds ) @@ -1488,7 +1488,7 @@ bool ScreenGameplay::AllAreFailing() void ScreenGameplay::GetMusicEndTiming( float &fSecondsToStartFadingOutMusic, float &fSecondsToStartTransitioningOut ) { - float fLastStepSeconds = GAMESTATE->m_pCurSong->m_SongTiming.GetElapsedTimeFromBeat( GAMESTATE->m_pCurSong->m_fLastBeat ); + float fLastStepSeconds = GAMESTATE->m_pCurSong->lastSecond; fLastStepSeconds += Player::GetMaxStepDistanceSeconds(); float fTransitionLength; @@ -1697,7 +1697,11 @@ void ScreenGameplay::Update( float fDeltaTime ) // update fGameplaySeconds STATSMAN->m_CurStageStats.m_fGameplaySeconds += fUnscaledDeltaTime; float curBeat = GAMESTATE->m_Position.m_fSongBeat; - if( curBeat >= GAMESTATE->m_pCurSong->m_fFirstBeat && curBeat < GAMESTATE->m_pCurSong->m_fLastBeat ) + Song &s = *GAMESTATE->m_pCurSong; + TimingData &timing = s.m_SongTiming; + + + if( curBeat >= timing.GetBeatFromElapsedTime(s.firstSecond) && curBeat < timing.GetBeatFromElapsedTime(s.lastSecond) ) { STATSMAN->m_CurStageStats.m_fStepsSeconds += fUnscaledDeltaTime; @@ -1978,7 +1982,8 @@ void ScreenGameplay::UpdateLights() } // Before the first beat of the song, all cabinet lights solid on (except for menu buttons). - bool bOverrideCabinetBlink = (GAMESTATE->m_Position.m_fSongBeat < GAMESTATE->m_pCurSong->m_fFirstBeat); + Song &s = *GAMESTATE->m_pCurSong; + bool bOverrideCabinetBlink = (GAMESTATE->m_Position.m_fSongBeat < s.m_SongTiming.GetBeatFromElapsedTime(s.firstSecond)); FOREACH_CabinetLight( cl ) bBlinkCabinetLight[cl] |= bOverrideCabinetBlink; diff --git a/src/Song.cpp b/src/Song.cpp index 750605658e..5ac5a64554 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -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 = 185; +const int FILE_CACHE_VERSION = 186; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; @@ -511,7 +511,8 @@ void Song::TidyUpData( bool bFromCache ) if( m_fMusicSampleStartSeconds+m_fMusicSampleLengthSeconds > this->m_fMusicLengthSeconds ) { - int iBeat = lrintf( m_fLastBeat/2 ); + // Attempt to get a reasonable default. + int iBeat = lrintf(this->m_SongTiming.GetBeatFromElapsedTime(this->lastSecond)/2); iBeat -= iBeat%4; m_fMusicSampleStartSeconds = timing.GetElapsedTimeFromBeat( (float)iBeat ); } @@ -800,7 +801,7 @@ void Song::TranslateTitles() void Song::ReCalculateRadarValuesAndLastBeat( bool bFromCache ) { - if( bFromCache && m_fFirstBeat >= 0 && m_fLastBeat > 0 ) + if( bFromCache && this->firstSecond >= 0 && this->lastSecond > 0 ) { // this is loaded from cache, then we just have to calculate the radar values. for( unsigned i=0; ispecifiedLastSecond; // Make sure we're at least as long as the specified amount. for( unsigned i=0; iCalculateRadarValues( m_fMusicLengthSeconds ); - // calculate lastBeat + // calculate lastSecond // If it's autogen, then first/last beat will come from the parent. if( pSteps->IsAutogen() ) @@ -842,12 +843,14 @@ void Song::ReCalculateRadarValuesAndLastBeat( bool bFromCache ) if( tempNoteData.GetLastRow() == 0 ) continue; - fFirstBeat = min( fFirstBeat, m_SongTiming.GetBeatFromElapsedTime(pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat())) ); - fLastBeat = max( fLastBeat, m_SongTiming.GetBeatFromElapsedTime(pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat())) ); + localFirst = min(localFirst, + pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetFirstBeat())); + localLast = max(localLast, + pSteps->m_Timing.GetElapsedTimeFromBeat(tempNoteData.GetLastBeat())); } - m_fFirstBeat = fFirstBeat; - m_fLastBeat = fLastBeat; + this->firstSecond = localFirst; + this->lastSecond = localLast; } // Return whether the song is playable in the given style. @@ -1461,8 +1464,7 @@ bool Song::HasSignificantBpmChangesOrStops() const float Song::GetStepsSeconds() const { - const TimingData &timing = this->m_SongTiming; - return timing.GetElapsedTimeFromBeat( m_fLastBeat ) - timing.GetElapsedTimeFromBeat( m_fFirstBeat ); + return this->lastSecond - this->firstSecond; } bool Song::IsLong() const @@ -1579,8 +1581,26 @@ public: } static int GetStepsSeconds( T* p, lua_State *L ) { lua_pushnumber(L, p->GetStepsSeconds()); return 1; } static int NormallyDisplayed( T* p, lua_State *L ){ lua_pushboolean(L, p->NormallyDisplayed()); return 1; } - static int GetFirstBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->m_fFirstBeat); return 1; } - static int GetLastBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->m_fLastBeat); return 1; } + static int GetFirstSecond(T* p, lua_State *L) + { + lua_pushnumber(L, p->firstSecond); + return 1; + } + static int GetLastSecond(T* p, lua_State *L) + { + lua_pushnumber(L, p->lastSecond); + return 1; + } + static int GetFirstBeat( T* p, lua_State *L ) + { + lua_pushnumber(L, p->m_SongTiming.GetBeatFromElapsedTime(p->firstSecond)); + return 1; + } + static int GetLastBeat( T* p, lua_State *L ) + { + lua_pushnumber(L, p->m_SongTiming.GetBeatFromElapsedTime(p->lastSecond)); + return 1; + } static int HasAttacks( T* p, lua_State *L ) { lua_pushboolean(L, p->HasAttacks()); return 1; } static int GetDisplayBpms( T* p, lua_State *L ) { @@ -1657,7 +1677,9 @@ public: ADD_METHOD( GetStepsSeconds ); ADD_METHOD( NormallyDisplayed ); ADD_METHOD( GetFirstBeat ); + ADD_METHOD( GetFirstSecond ); ADD_METHOD( GetLastBeat ); + ADD_METHOD( GetLastSecond ); ADD_METHOD( HasAttacks ); ADD_METHOD( GetDisplayBpms ); ADD_METHOD( IsDisplayBpmSecret ); diff --git a/src/Song.h b/src/Song.h index 95dd3b1a6e..d661347db1 100644 --- a/src/Song.h +++ b/src/Song.h @@ -20,7 +20,7 @@ void FixupPath( RString &path, const RString &sSongPath ); RString GetSongAssetPath( RString sPath, const RString &sSongPath ); /** @brief The version of the .ssc file format. */ -const static float STEPFILE_VERSION_NUMBER = 0.72f; +const static float STEPFILE_VERSION_NUMBER = 0.73f; /** @brief How many edits for this song can each profile have? */ const int MAX_EDITS_PER_SONG_PER_PROFILE = 15;