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;
TapNote tn;
tn.Set( TapNote::attack, TapNote::original, i );
tn.Set(
TapNote::attack,
TapNote::original,
true,
i,
false,
0 );
SetTapNote( track, row, tn );
return;
}
+52 -12
View File
@@ -92,25 +92,32 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData,
// if( 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;
char ch = sMeasureLine[c];
TapNote tn;
char ch = *p;
switch( ch )
{
case '0': t = TAP_EMPTY; break;
case '1': t = TAP_ORIGINAL_TAP; break;
case '2': t = TAP_ORIGINAL_HOLD_HEAD; break;
case '3': t = TAP_ORIGINAL_HOLD_TAIL; break;
case '0': tn = TAP_EMPTY; break;
case '1': tn = TAP_ORIGINAL_TAP; break;
case '2': tn = TAP_ORIGINAL_HOLD_HEAD; break;
case '3': tn = TAP_ORIGINAL_HOLD_TAIL; break;
// case 'm':
// Don't be loose with the definition. Use only 'M' since
// that's what we've been writing to disk. -Chris
case 'M':
t = TAP_ORIGINAL_MINE; break;
case 'M': tn = TAP_ORIGINAL_MINE; break;
default:
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
{
@@ -119,11 +126,39 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, CString sSMNoteData,
* due to invalid data. We should probably check for this when
* we load SM data for the first time ... */
// ASSERT(0);
t = TAP_EMPTY;
tn = TAP_EMPTY;
}
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;
}
sRet.append(1, c);
if( tn.bKeysound )
{
sRet.append( ssprintf("[%u]",tn.keysoundIndex) );
}
}
sRet.append(1, '\n');
+24 -6
View File
@@ -29,24 +29,42 @@ struct TapNote
// Equivalent to all 4s aside from the first one.
};
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_;
source = source_;
bAttack = bAttack_;
attackIndex = attackIndex_;
bKeysound = bKeysound_;
keysoundIndex = keysoundIndex_;
}
bool operator==( const TapNote &other )
{
#define COMPARE(x) if(x!=other.x) return false;
COMPARE(type);
COMPARE(source);
// COMPARE(bAttack);
// COMPARE(bKeysound);
COMPARE(bAttack);
COMPARE(bKeysound);
COMPARE(attackIndex);
// COMPARE(keysoundIndex);
COMPARE(keysoundIndex);
#undef COMPARE
return true;
}
+68 -31
View File
@@ -71,16 +71,16 @@ enum BmsTrack
BMS_TRACK_INVALID,
};
static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, TapNote &tapNoteOut )
static bool ConvertRawTrackToTapNote( int iRawTrack, BmsTrack &bmsTrackOut, bool &bIsHoldOut )
{
if( iRawTrack > 40 )
{
tapNoteOut = TAP_ORIGINAL_HOLD_HEAD;
bIsHoldOut = true;
iRawTrack -= 40;
}
else
{
tapNoteOut = TAP_ORIGINAL_TAP;
bIsHoldOut = false;
}
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() );
@@ -262,7 +262,7 @@ bool BMSLoader::LoadFromBMSFile( const CString &sPath, Steps &out )
{
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(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() );
CString &sNoteData = value_data;
vector<bool> arrayNotes;
vector<TapNote> vTapNotes;
for( int i=0; i+1<sNoteData.GetLength(); i+=2 )
{
bool bThisIsANote = sNoteData.substr(i,2) != "00";
arrayNotes.push_back( bThisIsANote );
CString sNoteId = sNoteData.substr(i,2);
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)
* BEATS_PER_MEASURE * ROWS_PER_BEAT );
BmsTrack bmsTrack;
TapNote tapNote;
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, tapNote) )
pNoteData->SetTapNote(bmsTrack, iNoteIndex, tapNote);
bool bIsHold;
if( ConvertRawTrackToTapNote(iRawTrackNum, bmsTrack, bIsHold) )
{
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() );
ASSERT( out.m_vsKeysoundFile.empty() );
CStringArray arrayBMSFileNames;
GetApplicableFiles( sDir, arrayBMSFileNames );
@@ -441,20 +460,9 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
* called to begin with. */
ASSERT( arrayBMSFileNames.size() );
// 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 );
if( ok )
out.AddSteps( pNewNotes );
else
delete pNewNotes;
}
SlideDuplicateDifficulties( out );
// This maps from a BMS wav ID (e.g. "1A") to an entry in the Song's
// keysound vector. Fill this in below while parsing the song data.
map<CString,unsigned> mapWavIdToKeysoundIndex;
CString sPath = out.GetSongDir() + arrayBMSFileNames[0];
@@ -535,7 +543,15 @@ bool BMSLoader::LoadFromDir( CString sDir, Song &out )
{
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(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",
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;
}
+2 -1
View File
@@ -4,13 +4,14 @@
#include "NotesLoader.h"
#include "GameConstantsAndTypes.h"
#include "NoteTypes.h"
#include <map>
class Song;
class Steps;
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 );
+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;
ASSERT( pNewNotes );
@@ -347,8 +358,14 @@ bool SMLoader::LoadFromSMFile( CString sPath, Song &out )
}
LoadFromSMTokens(
sParams[1], sParams[2], sParams[3], sParams[4], sParams[5], sParams[6], (iNumParams>=8)?sParams[7]:CString(""),
*pNewNotes);
sParams[1],
sParams[2],
sParams[3],
sParams[4],
sParams[5],
sParams[6],
(iNumParams>=8)?sParams[7]:CString(""),
*pNewNotes );
out.AddSteps( pNewNotes );
}
+12 -3
View File
@@ -107,6 +107,15 @@ void NotesWriterSM::WriteGlobalTags( RageFile &f, const Song &out )
}
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 )
@@ -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( ssprintf( "//---------------%s - %s----------------",
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:", in.GetDescription().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() )
continue;
WriteSMNotesTag( *pSteps, f, bSavingCache );
WriteSMNotesTag( out, *pSteps, f, bSavingCache );
}
return true;
+1 -1
View File
@@ -9,7 +9,7 @@ class RageFile;
class NotesWriterSM
{
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:
bool Write( CString sPath, const Song &out, bool bSavingCache );
+7
View File
@@ -195,6 +195,13 @@ public:
int GetNumStepsLoadedFromProfile( ProfileSlot slot ) 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:
void AdjustDuplicateSteps(); // part of TidyUpData
void DeleteDuplicateSteps( vector<Steps*> &vSteps );