[splittiming]
- load split timing from BMS files - fix a crash because autogen'd steps don't inherit the timing from parent - be more verbose in UpdateSongPosition
This commit is contained in:
@@ -99,7 +99,7 @@ void AutoKeysounds::LoadAutoplaySoundsInto( RageSoundReader_Chain *pChain )
|
|||||||
if( tn[pn].iKeysoundIndex >= 0 )
|
if( tn[pn].iKeysoundIndex >= 0 )
|
||||||
{
|
{
|
||||||
RString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[tn[pn].iKeysoundIndex];
|
RString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[tn[pn].iKeysoundIndex];
|
||||||
float fSeconds = pSong->m_SongTiming.GetElapsedTimeFromBeatNoOffset( NoteRowToBeat(iRow) ) + SOUNDMAN->GetPlayLatency();
|
float fSeconds = GAMESTATE->m_pCurSteps[pn]->m_Timing.GetElapsedTimeFromBeatNoOffset( NoteRowToBeat(iRow) ) + SOUNDMAN->GetPlayLatency();
|
||||||
|
|
||||||
float fPan = 0;
|
float fPan = 0;
|
||||||
if( !bSoundIsGlobal )
|
if( !bSoundIsGlobal )
|
||||||
|
|||||||
+5
-4
@@ -940,14 +940,15 @@ void GameState::ResetStageStatistics()
|
|||||||
m_iStageSeed = rand();
|
m_iStageSeed = rand();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp )
|
void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp, bool bUpdatePlayers )
|
||||||
{
|
{
|
||||||
|
|
||||||
m_Position.UpdateSongPosition( fPositionSeconds, timing, timestamp );
|
m_Position.UpdateSongPosition( fPositionSeconds, timing, timestamp );
|
||||||
|
|
||||||
FOREACH_EnabledPlayer( pn )
|
if( bUpdatePlayers )
|
||||||
if( m_pCurSteps[pn] )
|
FOREACH_EnabledPlayer( pn )
|
||||||
m_pPlayerState[pn]->m_Position.UpdateSongPosition( fPositionSeconds, m_pCurSteps[pn]->m_Timing, timestamp );
|
if( m_pCurSteps[pn] )
|
||||||
|
m_pPlayerState[pn]->m_Position.UpdateSongPosition( fPositionSeconds, m_pCurSteps[pn]->m_Timing, timestamp );
|
||||||
|
|
||||||
Actor::SetBGMTime( GAMESTATE->m_Position.m_fMusicSecondsVisible, GAMESTATE->m_Position.m_fSongBeatVisible, fPositionSeconds, GAMESTATE->m_Position.m_fSongBeatNoOffset );
|
Actor::SetBGMTime( GAMESTATE->m_Position.m_fMusicSecondsVisible, GAMESTATE->m_Position.m_fSongBeatVisible, fPositionSeconds, GAMESTATE->m_Position.m_fSongBeatNoOffset );
|
||||||
// LOG->Trace( "m_fMusicSeconds = %f, m_fSongBeat = %f, m_fCurBPS = %f, m_bFreeze = %f", m_fMusicSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze );
|
// LOG->Trace( "m_fMusicSeconds = %f, m_fSongBeat = %f, m_fCurBPS = %f, m_bFreeze = %f", m_fMusicSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze );
|
||||||
|
|||||||
+1
-1
@@ -205,7 +205,7 @@ public:
|
|||||||
static const float MUSIC_SECONDS_INVALID;
|
static const float MUSIC_SECONDS_INVALID;
|
||||||
|
|
||||||
void ResetMusicStatistics(); // Call this when it's time to play a new song. Clears the values above.
|
void ResetMusicStatistics(); // Call this when it's time to play a new song. Clears the values above.
|
||||||
void UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp = RageZeroTimer );
|
void UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp = RageZeroTimer, bool bUpdatePlayers = false );
|
||||||
float GetSongPercent( float beat ) const;
|
float GetSongPercent( float beat ) const;
|
||||||
|
|
||||||
bool AllAreInDangerOrWorse() const;
|
bool AllAreInDangerOrWorse() const;
|
||||||
|
|||||||
+231
-177
@@ -435,9 +435,121 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t
|
|||||||
static const int BEATS_PER_MEASURE = 4;
|
static const int BEATS_PER_MEASURE = 4;
|
||||||
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
|
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
|
||||||
|
|
||||||
static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameToData, Steps &out,
|
static bool SearchForKeysound( const RString &sPath, RString nDataOriginal, map<RString, int> &mapFilenameToKeysoundIndex, Song &out, int &outKeysoundIndex )
|
||||||
const MeasureToTimeSig_t &sigAdjustments, const map<RString,int> &idToKeySoundIndex )
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Search for memoized file names:
|
||||||
|
{
|
||||||
|
RString nDataToSearchFor = nDataOriginal;
|
||||||
|
nDataToSearchFor.MakeLower();
|
||||||
|
map<RString, int>::iterator it = mapFilenameToKeysoundIndex.find(nDataToSearchFor);
|
||||||
|
if (it != mapFilenameToKeysoundIndex.end()) {
|
||||||
|
outKeysoundIndex = it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: garbled song names seem to crash the app.
|
||||||
|
// this might not be the best place to put this code.
|
||||||
|
if( !utf8_is_valid(nDataOriginal) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Due to bugs in some programs, many BMS files have a "WAV" extension
|
||||||
|
* on files in the BMS for files that actually have some other extension.
|
||||||
|
* Do a search. Don't do a wildcard search; if sData is "song.wav",
|
||||||
|
* we might also have "song.png", which we shouldn't match. */
|
||||||
|
RString nData = nDataOriginal;
|
||||||
|
if( !IsAFile(out.GetSongDir()+nData) )
|
||||||
|
{
|
||||||
|
const char *exts[] = { "oga", "ogg", "wav", "mp3", NULL }; // XXX: stop duplicating these everywhere
|
||||||
|
for( unsigned i = 0; exts[i] != NULL; ++i )
|
||||||
|
{
|
||||||
|
RString fn = SetExtension( nData, exts[i] );
|
||||||
|
if( IsAFile(out.GetSongDir()+fn) )
|
||||||
|
{
|
||||||
|
nData = fn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !IsAFile(out.GetSongDir()+nData) )
|
||||||
|
{
|
||||||
|
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's again search for memoized file names (we got the normalized one!):
|
||||||
|
{
|
||||||
|
RString nDataToSearchFor = nData;
|
||||||
|
nDataToSearchFor.MakeLower();
|
||||||
|
map<RString, int>::iterator it = mapFilenameToKeysoundIndex.find(nDataToSearchFor);
|
||||||
|
if (it != mapFilenameToKeysoundIndex.end()) {
|
||||||
|
outKeysoundIndex = it->second;
|
||||||
|
|
||||||
|
{
|
||||||
|
RString nDataToAdd = nDataOriginal;
|
||||||
|
nDataToAdd.MakeLower();
|
||||||
|
mapFilenameToKeysoundIndex[nDataToAdd] = outKeysoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now this is a new sample.
|
||||||
|
out.m_vsKeysoundFile.push_back( nData );
|
||||||
|
outKeysoundIndex = out.m_vsKeysoundFile.size() - 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
RString nDataToAdd = nDataOriginal;
|
||||||
|
nDataToAdd.MakeLower();
|
||||||
|
mapFilenameToKeysoundIndex[nDataToAdd] = outKeysoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
RString nDataToAdd = nData;
|
||||||
|
nDataToAdd.MakeLower();
|
||||||
|
mapFilenameToKeysoundIndex[nDataToAdd] = outKeysoundIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SearchForKeysound( const RString &sPath, RString sNoteId, const NameToData_t &mapNameToData, map<RString, int> &mapIdToKeysoundIndex, map<RString, int> &mapFilenameToKeysoundIndex, Song &out, int &outKeysoundIndex )
|
||||||
|
{
|
||||||
|
|
||||||
|
sNoteId.MakeLower();
|
||||||
|
{
|
||||||
|
map<RString, int>::iterator it = mapIdToKeysoundIndex.find(sNoteId);
|
||||||
|
if (it != mapIdToKeysoundIndex.end())
|
||||||
|
{
|
||||||
|
outKeysoundIndex = it->second;
|
||||||
|
return outKeysoundIndex >= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RString sTagToLookFor = ssprintf( "#wav%s", sNoteId.c_str() );
|
||||||
|
RString nDataOriginal;
|
||||||
|
if( !GetTagFromMap( mapNameToData, sTagToLookFor, nDataOriginal ) )
|
||||||
|
{
|
||||||
|
LOG->UserLog( "Song file", sPath.c_str(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool retval = SearchForKeysound(sPath, nDataOriginal, mapFilenameToKeysoundIndex, out, outKeysoundIndex);
|
||||||
|
mapIdToKeysoundIndex[sNoteId] = outKeysoundIndex;
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameToData, Steps &out, Song &outSong, map<RString, int> &mapFilenameToKeysoundIndex )
|
||||||
|
{
|
||||||
|
|
||||||
|
map<RString, int> mapIdToKeysoundIndex;
|
||||||
|
MeasureToTimeSig_t sigAdjustments;
|
||||||
|
|
||||||
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
|
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
|
||||||
|
|
||||||
out.m_StepsType = StepsType_Invalid;
|
out.m_StepsType = StepsType_Invalid;
|
||||||
@@ -453,11 +565,121 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
|||||||
NoteData ndNotes;
|
NoteData ndNotes;
|
||||||
ndNotes.SetNumTracks( NUM_BMS_TRACKS );
|
ndNotes.SetNumTracks( NUM_BMS_TRACKS );
|
||||||
|
|
||||||
|
// Read BPM
|
||||||
|
if( GetTagFromMap(mapNameToData, "#bpm", sData) )
|
||||||
|
{
|
||||||
|
const float fBPM = StringToFloat( sData );
|
||||||
|
|
||||||
|
if( fBPM > 0.0f )
|
||||||
|
{
|
||||||
|
BPMSegment newSeg( 0, fBPM );
|
||||||
|
out.m_Timing.AddBPMSegment( newSeg );
|
||||||
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG->UserLog( "Song file", sPath.c_str(), "has an invalid BPM change at beat %f, BPM %f.",
|
||||||
|
NoteRowToBeat(0), fBPM );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Read time signatures. Note that these can differ across files in the same
|
/* Read time signatures. Note that these can differ across files in the same
|
||||||
* song. */
|
* song. */
|
||||||
MeasureToTimeSig_t mapMeasureToTimeSig;
|
MeasureToTimeSig_t mapMeasureToTimeSig;
|
||||||
ReadTimeSigs( mapNameToData, mapMeasureToTimeSig );
|
ReadTimeSigs( mapNameToData, mapMeasureToTimeSig );
|
||||||
|
|
||||||
|
for( NameToData_t::const_iterator it = mapNameToData.lower_bound("#00000"); it != mapNameToData.end(); ++it )
|
||||||
|
{
|
||||||
|
const RString &sName = it->first;
|
||||||
|
if( sName.size() != 6 || sName[0] != '#' || !IsAnInt( sName.substr(1,5) ) )
|
||||||
|
continue;
|
||||||
|
// this is step or offset data. Looks like "#00705"
|
||||||
|
int iMeasureNo = atoi( sName.substr(1, 3).c_str() );
|
||||||
|
int iBMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
||||||
|
int iStepIndex = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||||
|
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustments );
|
||||||
|
int iRowsPerMeasure = BeatToNoteRow( fBeatsPerMeasure );
|
||||||
|
|
||||||
|
RString nData = it->second;
|
||||||
|
int totalPairs = nData.size() / 2;
|
||||||
|
for( int i = 0; i < totalPairs; ++i )
|
||||||
|
{
|
||||||
|
RString sPair = nData.substr( i*2, 2 );
|
||||||
|
|
||||||
|
int iRow = iStepIndex + (i * iRowsPerMeasure) / totalPairs;
|
||||||
|
float fBeat = NoteRowToBeat( iRow );
|
||||||
|
int iVal = 0;
|
||||||
|
sscanf( sPair, "%x", &iVal );
|
||||||
|
|
||||||
|
if (sPair == "00")
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( iBMSTrackNo )
|
||||||
|
{
|
||||||
|
case BMS_TRACK_BPM:
|
||||||
|
if( iVal > 0 )
|
||||||
|
{
|
||||||
|
out.m_Timing.SetBPMAtBeat( fBeat, (float) iVal );
|
||||||
|
LOG->Trace( "Inserting new BPM change at beat %f, BPM %i", fBeat, iVal );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG->UserLog( "Song file", sPath.c_str(), "has an invalid BPM change at beat %f, BPM %d.",
|
||||||
|
fBeat, iVal );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BMS_TRACK_BPM_REF:
|
||||||
|
{
|
||||||
|
RString sTagToLookFor = ssprintf( "#bpm%s", sPair.c_str() );
|
||||||
|
RString sBPM;
|
||||||
|
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBPM ) )
|
||||||
|
{
|
||||||
|
float fBPM = StringToFloat( sBPM );
|
||||||
|
out.m_Timing.SetBPMAtBeat( fBeat, fBPM );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG->UserLog( "Song file", sPath.c_str(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BMS_TRACK_STOP:
|
||||||
|
{
|
||||||
|
if( iVal == 0 )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
RString sTagToLookFor = ssprintf( "#stop%02x", iVal );
|
||||||
|
RString sBeats;
|
||||||
|
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBeats ) )
|
||||||
|
{
|
||||||
|
// find the BPM at the time of this freeze
|
||||||
|
float fBPS = out.m_Timing.GetBPMAtBeat(fBeat) / 60.0f;
|
||||||
|
float fBeats = StringToFloat( sBeats ) / 48.0f;
|
||||||
|
float fFreezeSecs = fBeats / fBPS;
|
||||||
|
|
||||||
|
StopSegment newSeg( BeatToNoteRow(fBeat), fFreezeSecs );
|
||||||
|
out.m_Timing.AddStopSegment( newSeg );
|
||||||
|
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg.m_fStopSeconds );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG->UserLog( "Song file", sPath.c_str(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we're done reading BPMs, factor out weird time signatures.
|
||||||
|
SetTimeSigAdjustments( mapMeasureToTimeSig, outSong, sigAdjustments );
|
||||||
|
|
||||||
int iHoldStarts[NUM_BMS_TRACKS];
|
int iHoldStarts[NUM_BMS_TRACKS];
|
||||||
TapNote iHoldHeads[NUM_BMS_TRACKS];
|
TapNote iHoldHeads[NUM_BMS_TRACKS];
|
||||||
|
|
||||||
@@ -487,10 +709,9 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
|||||||
RString sNoteId = sNoteData.substr( i, 2 );
|
RString sNoteId = sNoteData.substr( i, 2 );
|
||||||
if( sNoteId != "00" )
|
if( sNoteId != "00" )
|
||||||
{
|
{
|
||||||
vTapNotes.push_back( TAP_ORIGINAL_TAP );
|
TapNote tn = TAP_ORIGINAL_TAP;
|
||||||
map<RString,int>::const_iterator rInt = idToKeySoundIndex.find( sNoteId );
|
SearchForKeysound( sPath, sNoteId, mapNameToData, mapIdToKeysoundIndex, mapFilenameToKeysoundIndex, outSong, tn.iKeysoundIndex );
|
||||||
if( rInt != idToKeySoundIndex.end() )
|
vTapNotes.push_back( tn );
|
||||||
vTapNotes.back().iKeysoundIndex = rInt->second;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -781,7 +1002,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, MeasureToTimeSig_t &sigAdjustmentsOut, map<RString,int> &idToKeySoundIndexOut )
|
static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out )
|
||||||
{
|
{
|
||||||
RString sData;
|
RString sData;
|
||||||
if( GetTagFromMap(mapNameToData, "#title", sData) )
|
if( GetTagFromMap(mapNameToData, "#title", sData) )
|
||||||
@@ -792,172 +1013,6 @@ static void ReadGlobalTags( const NameToData_t &mapNameToData, Song &out, Measur
|
|||||||
GetTagFromMap( mapNameToData, "#backbmp", out.m_sBackgroundFile );
|
GetTagFromMap( mapNameToData, "#backbmp", out.m_sBackgroundFile );
|
||||||
GetTagFromMap( mapNameToData, "#wav", out.m_sMusicFile );
|
GetTagFromMap( mapNameToData, "#wav", out.m_sMusicFile );
|
||||||
|
|
||||||
if( GetTagFromMap(mapNameToData, "#bpm", sData) )
|
|
||||||
{
|
|
||||||
const float fBPM = StringToFloat( sData );
|
|
||||||
|
|
||||||
if( PREFSMAN->m_bQuirksMode )
|
|
||||||
{
|
|
||||||
BPMSegment newSeg( 0, fBPM );
|
|
||||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
|
||||||
if( fBPM > 0.0f )
|
|
||||||
LOG->Trace( "Inserting new positive BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
|
||||||
else
|
|
||||||
LOG->Trace( "Inserting new negative BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( fBPM > 0.0f )
|
|
||||||
{
|
|
||||||
BPMSegment newSeg( 0, fBPM );
|
|
||||||
out.m_SongTiming.AddBPMSegment( newSeg );
|
|
||||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %f", NoteRowToBeat(0), fBPM );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG->UserLog( "Song file", out.GetSongDir(), "has an invalid BPM change at beat %f, BPM %f.",
|
|
||||||
NoteRowToBeat(0), fBPM );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NameToData_t::const_iterator it;
|
|
||||||
for( it = mapNameToData.lower_bound("#wav"); it != mapNameToData.end(); ++it )
|
|
||||||
{
|
|
||||||
const RString &sName = it->first;
|
|
||||||
|
|
||||||
if( sName.size() != 6 || sName.Left(4) != "#wav" )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// this is keysound file name. Looks like "#WAV1A"
|
|
||||||
RString nData = it->second;
|
|
||||||
RString sWavID = sName.Right(2);
|
|
||||||
|
|
||||||
// FIXME: garbled song names seem to crash the app.
|
|
||||||
// this might not be the best place to put this code.
|
|
||||||
if( !utf8_is_valid(nData) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Due to bugs in some programs, many BMS files have a "WAV" extension
|
|
||||||
* on files in the BMS for files that actually have some other extension.
|
|
||||||
* Do a search. Don't do a wildcard search; if sData is "song.wav",
|
|
||||||
* we might also have "song.png", which we shouldn't match. */
|
|
||||||
if( !IsAFile(out.GetSongDir()+nData) )
|
|
||||||
{
|
|
||||||
const char *exts[] = { "oga", "ogg", "wav", "mp3", NULL }; // XXX: stop duplicating these everywhere
|
|
||||||
for( unsigned i = 0; exts[i] != NULL; ++i )
|
|
||||||
{
|
|
||||||
RString fn = SetExtension( nData, exts[i] );
|
|
||||||
if( IsAFile(out.GetSongDir()+fn) )
|
|
||||||
{
|
|
||||||
nData = fn;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( !IsAFile(out.GetSongDir()+nData) )
|
|
||||||
LOG->UserLog( "Song file", out.GetSongDir(), "references key \"%s\" that can't be found", nData.c_str() );
|
|
||||||
|
|
||||||
sWavID.MakeUpper(); // HACK: undo the MakeLower()
|
|
||||||
out.m_vsKeysoundFile.push_back( nData );
|
|
||||||
idToKeySoundIndexOut[ sWavID ] = out.m_vsKeysoundFile.size()-1;
|
|
||||||
LOG->Trace( "Inserting keysound index %u '%s'", unsigned(out.m_vsKeysoundFile.size()-1), sWavID.c_str() );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time signature tags affect all other global timing tags, so read them first.
|
|
||||||
MeasureToTimeSig_t mapMeasureToTimeSig;
|
|
||||||
ReadTimeSigs( mapNameToData, mapMeasureToTimeSig );
|
|
||||||
|
|
||||||
for( it = mapNameToData.lower_bound("#00000"); it != mapNameToData.end(); ++it )
|
|
||||||
{
|
|
||||||
const RString &sName = it->first;
|
|
||||||
if( sName.size() != 6 || sName[0] != '#' || !IsAnInt( sName.substr(1,5) ) )
|
|
||||||
continue;
|
|
||||||
// this is step or offset data. Looks like "#00705"
|
|
||||||
int iMeasureNo = atoi( sName.substr(1, 3).c_str() );
|
|
||||||
int iBMSTrackNo = atoi( sName.substr(4, 2).c_str() );
|
|
||||||
int iStepIndex = GetMeasureStartRow( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut );
|
|
||||||
float fBeatsPerMeasure = GetBeatsPerMeasure( mapMeasureToTimeSig, iMeasureNo, sigAdjustmentsOut );
|
|
||||||
int iRowsPerMeasure = BeatToNoteRow( fBeatsPerMeasure );
|
|
||||||
|
|
||||||
RString nData = it->second;
|
|
||||||
int totalPairs = nData.size() / 2;
|
|
||||||
for( int i = 0; i < totalPairs; ++i )
|
|
||||||
{
|
|
||||||
RString sPair = nData.substr( i*2, 2 );
|
|
||||||
|
|
||||||
int iRow = iStepIndex + (i * iRowsPerMeasure) / totalPairs;
|
|
||||||
float fBeat = NoteRowToBeat( iRow );
|
|
||||||
int iVal = 0;
|
|
||||||
sscanf( sPair, "%x", &iVal );
|
|
||||||
|
|
||||||
if (sPair == "00")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( iBMSTrackNo )
|
|
||||||
{
|
|
||||||
case BMS_TRACK_BPM:
|
|
||||||
if( iVal > 0 )
|
|
||||||
{
|
|
||||||
out.m_SongTiming.SetBPMAtBeat( fBeat, (float) iVal );
|
|
||||||
LOG->Trace( "Inserting new BPM change at beat %f, BPM %i", fBeat, iVal );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG->UserLog( "Song file", out.GetSongDir(), "has an invalid BPM change at beat %f, BPM %d.",
|
|
||||||
fBeat, iVal );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BMS_TRACK_BPM_REF:
|
|
||||||
{
|
|
||||||
RString sTagToLookFor = ssprintf( "#bpm%s", sPair.c_str() );
|
|
||||||
RString sBPM;
|
|
||||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBPM ) )
|
|
||||||
{
|
|
||||||
float fBPM = StringToFloat( sBPM );
|
|
||||||
out.m_SongTiming.SetBPMAtBeat( fBeat, fBPM );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG->UserLog( "Song file", out.GetSongDir(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BMS_TRACK_STOP:
|
|
||||||
{
|
|
||||||
if( iVal == 0 )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
RString sTagToLookFor = ssprintf( "#stop%02x", iVal );
|
|
||||||
RString sBeats;
|
|
||||||
if( GetTagFromMap( mapNameToData, sTagToLookFor, sBeats ) )
|
|
||||||
{
|
|
||||||
// find the BPM at the time of this freeze
|
|
||||||
float fBPS = out.m_SongTiming.GetBPMAtBeat(fBeat) / 60.0f;
|
|
||||||
float fBeats = StringToFloat( sBeats ) / 48.0f;
|
|
||||||
float fFreezeSecs = fBeats / fBPS;
|
|
||||||
|
|
||||||
StopSegment newSeg( BeatToNoteRow(fBeat), fFreezeSecs );
|
|
||||||
out.m_SongTiming.AddStopSegment( newSeg );
|
|
||||||
LOG->Trace( "Inserting new Freeze at beat %f, secs %f", fBeat, newSeg.m_fStopSeconds );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG->UserLog( "Song file", out.GetSongDir(), "has tag \"%s\" which cannot be found.", sTagToLookFor.c_str() );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we're done reading BPMs, factor out weird time signatures.
|
|
||||||
SetTimeSigAdjustments( mapMeasureToTimeSig, out, sigAdjustmentsOut );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SlideDuplicateDifficulties( Song &p )
|
static void SlideDuplicateDifficulties( Song &p )
|
||||||
@@ -1100,9 +1155,7 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
|||||||
if( apSteps[i]->GetDifficulty() == Difficulty_Medium )
|
if( apSteps[i]->GetDifficulty() == Difficulty_Medium )
|
||||||
iMainDataIndex = i;
|
iMainDataIndex = i;
|
||||||
|
|
||||||
MeasureToTimeSig_t sigAdjustments;
|
ReadGlobalTags( aBMSData[iMainDataIndex], out );
|
||||||
map<RString,int> idToKeysoundIndex;
|
|
||||||
ReadGlobalTags( aBMSData[iMainDataIndex], out, sigAdjustments, idToKeysoundIndex );
|
|
||||||
|
|
||||||
// The brackets before the difficulty are in common substring, so remove them if it's found.
|
// The brackets before the difficulty are in common substring, so remove them if it's found.
|
||||||
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
if( commonSubstring.size() > 2 && commonSubstring[commonSubstring.size() - 2] == ' ' )
|
||||||
@@ -1124,10 +1177,11 @@ bool BMSLoader::LoadFromDir( const RString &sDir, Song &out )
|
|||||||
|
|
||||||
// Now that we've parsed the keysound data, load the Steps from the rest
|
// Now that we've parsed the keysound data, load the Steps from the rest
|
||||||
// of the .bms files.
|
// of the .bms files.
|
||||||
|
map<RString, int> mapFilenameToKeysoundIndex;
|
||||||
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ )
|
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ )
|
||||||
{
|
{
|
||||||
Steps* pNewNotes = apSteps[i];
|
Steps* pNewNotes = apSteps[i];
|
||||||
const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i], aBMSData[i], *pNewNotes, sigAdjustments, idToKeysoundIndex );
|
const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i], aBMSData[i], *pNewNotes, out, mapFilenameToKeysoundIndex );
|
||||||
if( ok )
|
if( ok )
|
||||||
out.AddSteps( pNewNotes );
|
out.AddSteps( pNewNotes );
|
||||||
else
|
else
|
||||||
|
|||||||
+2
-2
@@ -915,7 +915,7 @@ void ScreenEdit::Update( float fDeltaTime )
|
|||||||
{
|
{
|
||||||
RageTimer tm;
|
RageTimer tm;
|
||||||
const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm );
|
const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm );
|
||||||
GAMESTATE->UpdateSongPosition( fSeconds, GAMESTATE->m_pCurSong->m_SongTiming, tm );
|
GAMESTATE->UpdateSongPosition( fSeconds, GAMESTATE->m_pCurSong->m_SongTiming, tm, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_EditState == STATE_RECORDING )
|
if( m_EditState == STATE_RECORDING )
|
||||||
@@ -2388,7 +2388,7 @@ void ScreenEdit::TransitionEditState( EditState em )
|
|||||||
|
|
||||||
/* Give a 1 second lead-in. If we're loading Player, this must be done first. */
|
/* Give a 1 second lead-in. If we're loading Player, this must be done first. */
|
||||||
float fSeconds = m_pSteps->m_Timing.GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - 1;
|
float fSeconds = m_pSteps->m_Timing.GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - 1;
|
||||||
GAMESTATE->UpdateSongPosition( fSeconds, m_pSteps->m_Timing );
|
GAMESTATE->UpdateSongPosition( fSeconds, m_pSteps->m_Timing, RageZeroTimer, true );
|
||||||
|
|
||||||
GAMESTATE->m_bGameplayLeadIn.Set( false );
|
GAMESTATE->m_bGameplayLeadIn.Set( false );
|
||||||
|
|
||||||
|
|||||||
@@ -1423,7 +1423,7 @@ void ScreenGameplay::UpdateSongPosition( float fDeltaTime )
|
|||||||
RageTimer tm;
|
RageTimer tm;
|
||||||
const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm );
|
const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm );
|
||||||
const float fAdjust = SOUND->GetFrameTimingAdjustment( fDeltaTime );
|
const float fAdjust = SOUND->GetFrameTimingAdjustment( fDeltaTime );
|
||||||
GAMESTATE->UpdateSongPosition( fSeconds+fAdjust, GAMESTATE->m_pCurSong->m_SongTiming, tm+fAdjust );
|
GAMESTATE->UpdateSongPosition( fSeconds+fAdjust, GAMESTATE->m_pCurSong->m_SongTiming, tm+fAdjust, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenGameplay::BeginScreen()
|
void ScreenGameplay::BeginScreen()
|
||||||
|
|||||||
@@ -339,6 +339,7 @@ void Steps::AutogenFrom( const Steps *parent_, StepsType ntTo )
|
|||||||
{
|
{
|
||||||
parent = parent_;
|
parent = parent_;
|
||||||
m_StepsType = ntTo;
|
m_StepsType = ntTo;
|
||||||
|
m_Timing = parent->m_Timing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds ) // pSource does not have to be of the same StepsType
|
void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds ) // pSource does not have to be of the same StepsType
|
||||||
@@ -348,6 +349,7 @@ void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds
|
|||||||
pSource->GetNoteData( noteData );
|
pSource->GetNoteData( noteData );
|
||||||
noteData.SetNumTracks( GAMEMAN->GetStepsTypeInfo(ntTo).iNumTracks );
|
noteData.SetNumTracks( GAMEMAN->GetStepsTypeInfo(ntTo).iNumTracks );
|
||||||
parent = NULL;
|
parent = NULL;
|
||||||
|
m_Timing = pSource->m_Timing;
|
||||||
this->SetNoteData( noteData );
|
this->SetNoteData( noteData );
|
||||||
this->SetDescription( pSource->GetDescription() );
|
this->SetDescription( pSource->GetDescription() );
|
||||||
this->SetDifficulty( pSource->GetDifficulty() );
|
this->SetDifficulty( pSource->GetDifficulty() );
|
||||||
|
|||||||
Reference in New Issue
Block a user