Parse and maintain keysound data, but don't play the keysounds yet.

This commit is contained in:
Chris Danford
2004-09-29 07:19:44 +00:00
parent 0f6ed81faf
commit b20eaeb60b
9 changed files with 193 additions and 58 deletions
+7 -1
View File
@@ -275,7 +275,13 @@ void NoteData::SetTapAttackNote( int track, int row, Attack attack )
{ {
m_AttackMap[i] = attack; m_AttackMap[i] = attack;
TapNote tn; TapNote tn;
tn.Set( TapNote::attack, TapNote::original, i ); tn.Set(
TapNote::attack,
TapNote::original,
true,
i,
false,
0 );
SetTapNote( track, row, tn ); SetTapNote( track, row, tn );
return; return;
} }
+52 -12
View File
@@ -92,25 +92,32 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData,
// if( m_iNumTracks != sMeasureLine.GetLength() ) // if( m_iNumTracks != sMeasureLine.GetLength() )
// RageException::Throw( "Actual number of note columns (%d) is different from the StepsType (%d).", m_iNumTracks, sMeasureLine.GetLength() ); // RageException::Throw( "Actual number of note columns (%d) is different from the StepsType (%d).", m_iNumTracks, sMeasureLine.GetLength() );
for( int c=0; c<min(sMeasureLine.GetLength(),out.GetNumTracks()); c++ ) const char *p = sMeasureLine;
int iTrack = 0;
while( *p )
{ {
TapNote t; TapNote tn;
char ch = sMeasureLine[c]; char ch = *p;
switch( ch ) switch( ch )
{ {
case '0': t = TAP_EMPTY; break; case '0': tn = TAP_EMPTY; break;
case '1': t = TAP_ORIGINAL_TAP; break; case '1': tn = TAP_ORIGINAL_TAP; break;
case '2': t = TAP_ORIGINAL_HOLD_HEAD; break; case '2': tn = TAP_ORIGINAL_HOLD_HEAD; break;
case '3': t = TAP_ORIGINAL_HOLD_TAIL; break; case '3': tn = TAP_ORIGINAL_HOLD_TAIL; break;
// case 'm': // case 'm':
// Don't be loose with the definition. Use only 'M' since // Don't be loose with the definition. Use only 'M' since
// that's what we've been writing to disk. -Chris // that's what we've been writing to disk. -Chris
case 'M': case 'M': tn = TAP_ORIGINAL_MINE; break;
t = TAP_ORIGINAL_MINE; break;
default: default:
if( ch >= 'a' && ch <= 'z' ) if( ch >= 'a' && ch <= 'z' )
{ {
t.Set( TapNote::attack, TapNote::original, ch - 'a' ); tn.Set(
TapNote::attack,
TapNote::original,
true,
ch - 'a',
false,
0 );
} }
else else
{ {
@@ -119,11 +126,39 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData,
* due to invalid data. We should probably check for this when * due to invalid data. We should probably check for this when
* we load SM data for the first time ... */ * we load SM data for the first time ... */
// ASSERT(0); // ASSERT(0);
t = TAP_EMPTY; tn = TAP_EMPTY;
} }
break; break;
} }
out.SetTapNote(c, iIndex, t);
p++;
// look for optional keysound index (e.g. "[123]")
if( *p == '[' )
{
p++;
unsigned uKeysoundIndex = 0;
if( 1 == sscanf( p, "%u]", &uKeysoundIndex ) ) // not fatal if this fails due to malformed data
{
tn.bKeysound = true;
tn.keysoundIndex = uKeysoundIndex;
}
// skip past the ']'
while( *p )
{
if( *p == '[]' )
{
p++;
break;
}
p++;
}
}
out.SetTapNote( iTrack, iIndex, tn );
iTrack++;
} }
} }
} }
@@ -221,6 +256,11 @@ void NoteDataUtil::GetSMNoteDataString( const NoteData &in_, CString &notes_out,
break; break;
} }
sRet.append(1, c); sRet.append(1, c);
if( tn.bKeysound )
{
sRet.append( ssprintf("[%u]",tn.keysoundIndex) );
}
} }
sRet.append(1, '\n'); sRet.append(1, '\n');
+24 -6
View File
@@ -29,24 +29,42 @@ struct TapNote
// Equivalent to all 4s aside from the first one. // Equivalent to all 4s aside from the first one.
}; };
unsigned source : 2; // only valid if type!=empty unsigned source : 2; // only valid if type!=empty
unsigned attackIndex : 3; // attack index
// bit field sizes should add to 8 bits.
void Set( Type type_, Source source_, unsigned attackIndex_ ) bool bAttack : 1; // true if this note causes an attack when hit
bool bKeysound : 1; // true if this note plays a keysound when hit
// CAREFUL: small fields grouped together for alignment.
uint8_t attackIndex; // index into NoteData's vector of attacks
// Only valid if bAttack.
uint16_t keysoundIndex; // index into Song's vector of keysound files.
// Only valid if bKeysound.
// Some songs have > 256 keysounds.
void Set(
Type type_,
Source source_,
bool bAttack_,
unsigned attackIndex_,
bool bKeysound_,
unsigned keysoundIndex_ )
{ {
type = type_; type = type_;
source = source_; source = source_;
bAttack = bAttack_;
attackIndex = attackIndex_; attackIndex = attackIndex_;
bKeysound = bKeysound_;
keysoundIndex = keysoundIndex_;
} }
bool operator==( const TapNote &other ) bool operator==( const TapNote &other )
{ {
#define COMPARE(x) if(x!=other.x) return false; #define COMPARE(x) if(x!=other.x) return false;
COMPARE(type); COMPARE(type);
COMPARE(source); COMPARE(source);
// COMPARE(bAttack); COMPARE(bAttack);
// COMPARE(bKeysound); COMPARE(bKeysound);
COMPARE(attackIndex); COMPARE(attackIndex);
// COMPARE(keysoundIndex); COMPARE(keysoundIndex);
#undef COMPARE #undef COMPARE
return true; return true;
} }
+68 -31
View File
@@ -71,16 +71,16 @@ enum BmsTrack
BMS_TRACK_INVALID, BMS_TRACK_INVALID,
}; };
static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, TapNote &tapNoteOut ) static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, bool &bIsHoldOut )
{ {
if( iRawTrack > 40 ) if( iRawTrack > 40 )
{ {
tapNoteOut = TAP_ORIGINAL_HOLD_HEAD; bIsHoldOut = true;
iRawTrack -= 40; iRawTrack -= 40;
} }
else else
{ {
tapNoteOut = TAP_ORIGINAL_TAP; bIsHoldOut = false;
} }
switch( iRawTrack ) switch( iRawTrack )
@@ -178,7 +178,7 @@ static StepsType DetermineStepsType( int iPlayer, const NoteData &nd )
} }
} }
bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out ) bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out, const map<CString,unsigned> &mapWavIdToKeysoundIndex )
{ {
LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() ); LOG->Trace( "Steps::LoadFromBMSFile( '%s' )", sPath.c_str() );
@@ -262,7 +262,7 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out )
{ {
out.SetMeter(atoi(value_data)); out.SetMeter(atoi(value_data));
} }
else if( value_name.size() >= 6 && value_name[0] == '#' else if( value_name.size() == 6 && value_name[0] == '#'
&& IsAnInt( value_name.substr(1,3) ) && IsAnInt( value_name.substr(1,3) )
&& IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705" && IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705"
{ {
@@ -270,29 +270,46 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out )
int iRawTrackNum = atoi( value_name.substr(4,2).c_str() ); int iRawTrackNum = atoi( value_name.substr(4,2).c_str() );
CString &sNoteData = value_data; CString &sNoteData = value_data;
vector<bool> arrayNotes; vector<TapNote> vTapNotes;
for( int i=0; i+1<sNoteData.GetLength(); i+=2 ) for( int i=0; i+1<sNoteData.GetLength(); i+=2 )
{ {
bool bThisIsANote = sNoteData.substr(i,2) != "00"; CString sNoteId = sNoteData.substr(i,2);
arrayNotes.push_back( bThisIsANote ); if( sNoteId != "00" )
{
TapNote tn = TAP_ORIGINAL_TAP;
map<CString,unsigned>::const_iterator it = mapWavIdToKeysoundIndex.find(sNoteId);
if( it != mapWavIdToKeysoundIndex.end() )
{
tn.bKeysound = true;
tn.keysoundIndex = it->second;
}
vTapNotes.push_back( tn );
}
else
{
vTapNotes.push_back( TAP_EMPTY );
}
} }
const unsigned iNumNotesInThisMeasure = arrayNotes.size(); const unsigned uNumNotesInThisMeasure = vTapNotes.size();
for( unsigned j=0; j<iNumNotesInThisMeasure; j++ ) for( unsigned j=0; j<uNumNotesInThisMeasure; j++ )
{ {
if( arrayNotes[j] ) if( vTapNotes[j].type != TapNote::empty )
{ {
float fPercentThroughMeasure = (float)j/(float)iNumNotesInThisMeasure; float fPercentThroughMeasure = (float)j/(float)uNumNotesInThisMeasure;
const int iNoteIndex = (int) ( (iMeasureNo + fPercentThroughMeasure) const int iNoteIndex = (int) ( (iMeasureNo + fPercentThroughMeasure)
* BEATS_PER_MEASURE * ROWS_PER_BEAT ); * BEATS_PER_MEASURE * ROWS_PER_BEAT );
BmsTrack bmsTrack; BmsTrack bmsTrack;
TapNote tapNote; bool bIsHold;
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) )
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, tapNote) ) {
pNoteData->SetTapNote(bmsTrack, iNoteIndex, tapNote); TapNote tn = vTapNotes[j];
tn.type = bIsHold ? TapNote::hold_head : TapNote::tap;
pNoteData->SetTapNote(bmsTrack, iNoteIndex, tn);
}
} }
} }
} }
@@ -434,6 +451,8 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
{ {
LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir.c_str() ); LOG->Trace( "Song::LoadFromBMSDir(%s)", sDir.c_str() );
ASSERT( out.m_vsKeysoundFile.empty() );
CStringArray arrayBMSFileNames; CStringArray arrayBMSFileNames;
GetApplicableFiles( sDir, arrayBMSFileNames ); GetApplicableFiles( sDir, arrayBMSFileNames );
@@ -441,20 +460,9 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
* called to begin with. */ * called to begin with. */
ASSERT( arrayBMSFileNames.size() ); ASSERT( arrayBMSFileNames.size() );
// load the Steps from the rest of the BMS files // This maps from a BMS wav ID (e.g. "1A") to an entry in the Song's
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ ) // keysound vector. Fill this in below while parsing the song data.
{ map<CString,unsigned> mapWavIdToKeysoundIndex;
Steps* pNewNotes = new Steps;
const bool ok = LoadFromBMSFile( out.GetSongDir() + arrayBMSFileNames[i],
*pNewNotes );
if( ok )
out.AddSteps( pNewNotes );
else
delete pNewNotes;
}
SlideDuplicateDifficulties( out );
CString sPath = out.GetSongDir() + arrayBMSFileNames[0]; CString sPath = out.GetSongDir() + arrayBMSFileNames[0];
@@ -535,7 +543,15 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
{ {
out.m_sMusicFile = value_data; out.m_sMusicFile = value_data;
} }
else if( value_name.size() >= 6 && value_name[0] == '#' else if( value_name.size() == 6 && value_name.Left(4) == "#wav" ) // this is keysound file name. Looks like "#WAV1A"
{
CString sWavID = value_name.Right(2);
sWavID.MakeUpper(); // HACK: undo the MakeLower()
out.m_vsKeysoundFile.push_back( value_data );
mapWavIdToKeysoundIndex[ sWavID ] = out.m_vsKeysoundFile.size()-1;
LOG->Trace( "Inserting keysound index %u '%s'", out.m_vsKeysoundFile.size()-1, sWavID.c_str() );
}
else if( value_name.size() == 6 && value_name[0] == '#'
&& IsAnInt( value_name.substr(1,3) ) && IsAnInt( value_name.substr(1,3) )
&& IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705" && IsAnInt( value_name.substr(4,2) ) ) // this is step or offset data. Looks like "#00705"
{ {
@@ -730,6 +746,27 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
LOG->Trace( "There is a BPM change at beat %f, BPM %f, index %d", LOG->Trace( "There is a BPM change at beat %f, BPM %f, index %d",
out.m_Timing.m_BPMSegments[i].m_fStartBeat, out.m_Timing.m_BPMSegments[i].m_fBPM, i ); out.m_Timing.m_BPMSegments[i].m_fStartBeat, out.m_Timing.m_BPMSegments[i].m_fBPM, i );
// Now that we've parsed the keysound data, load the Steps from the rest
// of the .bms files.
for( unsigned i=0; i<arrayBMSFileNames.size(); i++ )
{
Steps* pNewNotes = new Steps;
const bool ok = LoadFromBMSFile(
out.GetSongDir() + arrayBMSFileNames[i],
*pNewNotes,
mapWavIdToKeysoundIndex );
if( ok )
out.AddSteps( pNewNotes );
else
delete pNewNotes;
}
SlideDuplicateDifficulties( out );
return true; return true;
} }
+2 -1
View File
@@ -4,13 +4,14 @@
#include "NotesLoader.h" #include "NotesLoader.h"
#include "GameConstantsAndTypes.h" #include "GameConstantsAndTypes.h"
#include "NoteTypes.h" #include "NoteTypes.h"
#include <map>
class Song; class Song;
class Steps; class Steps;
class BMSLoader: public NotesLoader class BMSLoader: public NotesLoader
{ {
bool LoadFromBMSFile( const CString &sPath, Steps &out1 ); bool LoadFromBMSFile( const CString &sPath, Steps &out1, const map<CString,unsigned> &mapWavIdToKeysoundIndex );
void SlideDuplicateDifficulties( Song &p ); void SlideDuplicateDifficulties( Song &p );
+20 -3
View File
@@ -335,7 +335,18 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
} }
} }
else if( 0==stricmp(sValueName,"NOTES") ) else if( 0==stricmp(sValueName,"KEYSOUNDS") )
{
CStringArray aKeysoundFiles;
split( sParams[1], ",", aKeysoundFiles );
for( unsigned k=0; k<aKeysoundFiles.size(); k++ )
{
out.m_vsKeysoundFile.push_back( aKeysoundFiles[k] );
}
}
else if( 0==stricmp(sValueName,"NOTES") || 0==stricmp(sValueName,"NOTES2") )
{ {
Steps* pNewNotes = new Steps; Steps* pNewNotes = new Steps;
ASSERT( pNewNotes ); ASSERT( pNewNotes );
@@ -347,8 +358,14 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
} }
LoadFromSMTokens( LoadFromSMTokens(
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], (iNumParams>=8)?sParams[7]:CString(""), sParams[1],
*pNewNotes); sParams[2],
sParams[3],
sParams[4],
sParams[5],
sParams[6],
(iNumParams>=8)?sParams[7]:CString(""),
*pNewNotes );
out.AddSteps( pNewNotes ); out.AddSteps( pNewNotes );
} }
+12 -3
View File
@@ -107,6 +107,15 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out )
} }
f.PutLine( ";" ); f.PutLine( ";" );
} }
f.Write( "#KEYSOUNDS:" );
for( unsigned i=0; i<out.m_vsKeysoundFile.size(); i++ )
{
f.Write( out.m_vsKeysoundFile[i] );
if( i != out.m_vsKeysoundFile.size()-1 )
f.Write( "," );
}
f.PutLine( ";" );
} }
static void WriteLineList( RageFile &f, vector<CString> &lines, bool SkipLeadingBlankLines, bool OmitLastNewline ) static void WriteLineList( RageFile &f, vector<CString> &lines, bool SkipLeadingBlankLines, bool OmitLastNewline )
@@ -127,12 +136,12 @@ static void WriteLineList( RageFile &f, vector<CString> &lines, bool SkipLeading
} }
} }
void NotesWriterSM::WriteSMNotesTag( const Steps &in, RageFile &f, bool bSavingCache ) void NotesWriterSM::WriteSMNotesTag( const Song &song, const Steps &in, RageFile &f, bool bSavingCache )
{ {
f.PutLine( "" ); f.PutLine( "" );
f.PutLine( ssprintf( "//---------------%s - %s----------------", f.PutLine( ssprintf( "//---------------%s - %s----------------",
GameManager::StepsTypeToString(in.m_StepsType).c_str(), in.GetDescription().c_str() ) ); GameManager::StepsTypeToString(in.m_StepsType).c_str(), in.GetDescription().c_str() ) );
f.PutLine( "#NOTES:" ); f.PutLine( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" );
f.PutLine( ssprintf( " %s:", GameManager::StepsTypeToString(in.m_StepsType).c_str() ) ); f.PutLine( ssprintf( " %s:", GameManager::StepsTypeToString(in.m_StepsType).c_str() ) );
f.PutLine( ssprintf( " %s:", in.GetDescription().c_str() ) ); f.PutLine( ssprintf( " %s:", in.GetDescription().c_str() ) );
f.PutLine( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); f.PutLine( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) );
@@ -217,7 +226,7 @@ bool NotesWriterSM::Write(CString sPath, const Song &out, bool bSavingCache)
if( pSteps->WasLoadedFromProfile() ) if( pSteps->WasLoadedFromProfile() )
continue; continue;
WriteSMNotesTag( *pSteps, f, bSavingCache ); WriteSMNotesTag( out, *pSteps, f, bSavingCache );
} }
return true; return true;
+1 -1
View File
@@ -9,7 +9,7 @@ class RageFile;
class NotesWriterSM class NotesWriterSM
{ {
void WriteGlobalTags( RageFile &f, const Song &out ); void WriteGlobalTags( RageFile &f, const Song &out );
void WriteSMNotesTag( const Steps &in, RageFile &f, bool bSavingCache ); void WriteSMNotesTag( const Song &song, const Steps &in, RageFile &f, bool bSavingCache );
public: public:
bool Write( CString sPath, const Song &out, bool bSavingCache ); bool Write( CString sPath, const Song &out, bool bSavingCache );
+7
View File
@@ -195,6 +195,13 @@ public:
int GetNumStepsLoadedFromProfile( ProfileSlot slot ) const; int GetNumStepsLoadedFromProfile( ProfileSlot slot ) const;
bool IsEditAlreadyLoaded( Steps* pSteps ) const; bool IsEditAlreadyLoaded( Steps* pSteps ) const;
// An array of keysound file names (e.g. "beep.wav").
// The index in this array corresponds to the index in TapNote. If you
// change the index in here, you must change all NoteData too.
// Any note that doesn't have a value in the range of this array
// means "this note doens't have a keysound".
vector<CString> m_vsKeysoundFile;
private: private:
void AdjustDuplicateSteps(); // part of TidyUpData void AdjustDuplicateSteps(); // part of TidyUpData
void DeleteDuplicateSteps( vector<Steps*> &vSteps ); void DeleteDuplicateSteps( vector<Steps*> &vSteps );