experimental time signature support. Background changes and beat bars are currently the only things that are aware of time signature. Note coloring isn't aware of time signature yet.

This commit is contained in:
Chris Danford
2007-04-30 05:12:48 +00:00
parent e5dd10fbef
commit d2f6f8e03f
18 changed files with 314 additions and 69 deletions
+2 -2
View File
@@ -30,8 +30,8 @@ void Attack::GetRealtimeAttackBeats( const Song *pSong, const PlayerState* pPlay
ASSERT( pPlayerState );
ASSERT( pSong );
/* If reasonable, push the attack forward so notes on screen don't change suddenly. */
fStartBeat = min( GAMESTATE->m_fSongBeat+BEATS_PER_MEASURE*2, pPlayerState->m_fLastDrawnBeat );
/* If reasonable, push the attack forward 8 beats so that notes on screen don't change suddenly. */
fStartBeat = min( GAMESTATE->m_fSongBeat+8, pPlayerState->m_fLastDrawnBeat );
fStartBeat = truncf(fStartBeat)+1;
const float fStartSecond = pSong->GetElapsedTimeFromBeat( fStartBeat );
+30 -12
View File
@@ -433,30 +433,48 @@ BackgroundDef BackgroundImpl::Layer::CreateRandomBGA( const Song *pSong, const R
void BackgroundImpl::LoadFromRandom( float fFirstBeat, float fEndBeat, const BackgroundChange &change )
{
int iStartRow = BeatToNoteRow(fFirstBeat);
int iEndRow = BeatToNoteRow(fEndBeat);
const TimingData &timing = m_pSong->m_Timing;
// change BG every 4 bars
for( float f=fFirstBeat; f<fEndBeat; f+=BEATS_PER_MEASURE*4 )
// change BG every time signature change or 4 measures
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
{
// Don't fade. It causes frame rate dip, especially on slower machines.
BackgroundDef bd = m_Layer[0].CreateRandomBGA( m_pSong, change.m_def.m_sEffect, m_RandomBGAnimations, this );
if( !bd.IsEmpty() )
vector<TimeSignatureSegment>::const_iterator next = iter;
next++;
int iSegmentEndRow = (next == timing.m_vTimeSignatureSegments.end()) ? iEndRow : next->m_iStartRow;
for( int i=max(iter->m_iStartRow,iStartRow); i<min(iEndRow,iSegmentEndRow); i+=iter->GetNoteRowsPerMeasure() )
{
BackgroundChange c = change;
c.m_def = bd;
c.m_fStartBeat = f;
m_Layer[0].m_aBGChanges.push_back( c );
// Don't fade. It causes frame rate dip, especially on slower machines.
BackgroundDef bd = m_Layer[0].CreateRandomBGA( m_pSong, change.m_def.m_sEffect, m_RandomBGAnimations, this );
if( !bd.IsEmpty() )
{
BackgroundChange c = change;
c.m_def = bd;
c.m_fStartBeat = NoteRowToBeat(i);
m_Layer[0].m_aBGChanges.push_back( c );
}
}
}
// change BG every BPM change that is at the beginning of a measure
int iStartRow = BeatToNoteRow(fFirstBeat);
int iEndRow = BeatToNoteRow(fEndBeat);
for( unsigned i=0; i<timing.m_BPMSegments.size(); i++ )
{
const BPMSegment& bpmseg = timing.m_BPMSegments[i];
if( bpmseg.m_iStartRow % BeatToNoteRow((float) BEATS_PER_MEASURE) != 0 )
bool bAtBeginningOfMeasure = false;
FOREACH_CONST( TimeSignatureSegment, timing.m_vTimeSignatureSegments, iter )
{
if( (bpmseg.m_iStartRow - iter->m_iStartRow) % iter->GetNoteRowsPerMeasure() == 0 )
{
bAtBeginningOfMeasure = true;
break;
}
}
if( !bAtBeginningOfMeasure )
continue; // skip
// start so that we don't create a BGChange right on top of fEndBeat
+16 -8
View File
@@ -27,7 +27,8 @@ void GameplayAssist::PlayTicks( const NoteData &nd )
* come out on time; the actual precise timing is handled by SetStartTime. */
float fPositionSeconds = GAMESTATE->m_fMusicSeconds;
fPositionSeconds += SOUNDMAN->GetPlayLatency() + (float)CommonMetrics::TICK_EARLY_SECONDS + 0.250f;
const float fSongBeat = GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds );
const TimingData &timing = GAMESTATE->m_pCurSong->m_Timing;
const float fSongBeat = timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds );
const int iSongRow = max( 0, BeatToNoteRowNotRounded( fSongBeat ) );
static int iRowLastCrossed = -1;
@@ -45,7 +46,7 @@ void GameplayAssist::PlayTicks( const NoteData &nd )
if( iClapRow != -1 )
{
const float fTickBeat = NoteRowToBeat( iClapRow );
const float fTickSecond = GAMESTATE->m_pCurSong->m_Timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
const float fTickSecond = timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
float fSecondsUntil = fTickSecond - GAMESTATE->m_fMusicSeconds;
fSecondsUntil /= GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; /* 2x music rate means the time until the tick is halved */
@@ -59,22 +60,29 @@ void GameplayAssist::PlayTicks( const NoteData &nd )
{
//iRowLastCrossed+1, iSongRow+1
int iLastCrossedBeat = iRowLastCrossed / ROWS_PER_BEAT;
int iCurrentBeat = iSongRow / ROWS_PER_BEAT;
int iLastCrossedMeasureIndex;
int iLastCrossedBeatIndex;
int iLastCrossedRowsRemainder;
timing.NoteRowToMeasureAndBeat( iRowLastCrossed, iLastCrossedMeasureIndex, iLastCrossedBeatIndex, iLastCrossedRowsRemainder );
int iCurrentMeasureIndex;
int iCurrentBeatIndex;
int iCurrentRowsRemainder;
timing.NoteRowToMeasureAndBeat( iSongRow, iCurrentMeasureIndex, iCurrentBeatIndex, iCurrentRowsRemainder );
int iMetronomeRow = -1;
bool bIsMeasure = false;
if( iLastCrossedBeat != iCurrentBeat )
if( iLastCrossedMeasureIndex != iCurrentMeasureIndex || iLastCrossedBeatIndex != iCurrentBeatIndex )
{
iMetronomeRow = iCurrentBeat * ROWS_PER_BEAT;
bIsMeasure = (iCurrentBeat % BEATS_PER_MEASURE) == 0;
iMetronomeRow = iSongRow - iCurrentRowsRemainder;
bIsMeasure = iCurrentBeatIndex == 0 && iCurrentRowsRemainder == 0;
}
if( iMetronomeRow != -1 )
{
const float fTickBeat = NoteRowToBeat( iMetronomeRow );
const float fTickSecond = GAMESTATE->m_pCurSong->m_Timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
const float fTickSecond = timing.GetElapsedTimeFromBeatNoOffset( fTickBeat );
float fSecondsUntil = fTickSecond - GAMESTATE->m_fMusicSeconds;
fSecondsUntil /= GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; /* 2x music rate means the time until the tick is halved */
+12 -3
View File
@@ -10,11 +10,20 @@
#include "Foreach.h"
#include <utility>
NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex )
// TODO: Remove these constants that aren't time signature-aware
static const int BEATS_PER_MEASURE = 4;
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &nd, int iMeasureIndex )
{
const int iMeasureStartIndex = iMeasureIndex * ROWS_PER_MEASURE;
const int iMeasureLastIndex = (iMeasureIndex+1) * ROWS_PER_MEASURE - 1;
const int iMeasureEndIndex = (iMeasureIndex+1) * ROWS_PER_MEASURE;
return NoteDataUtil::GetSmallestNoteTypeInRange( nd, iMeasureStartIndex, iMeasureEndIndex );
}
NoteType NoteDataUtil::GetSmallestNoteTypeInRange( const NoteData &n, int iStartIndex, int iEndIndex )
{
// probe to find the smallest note type
NoteType nt;
for( nt=(NoteType)0; nt<NUM_NoteType; nt=NoteType(nt+1) ) // for each NoteType, largest to largest
@@ -24,7 +33,7 @@ NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMe
bool bFoundSmallerNote = false;
// for each index in this measure
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( n, i, iMeasureStartIndex, iMeasureLastIndex )
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( n, i, iStartIndex, iEndIndex )
{
if( i % iRowSpacing == 0 )
continue; // skip
+2 -1
View File
@@ -18,7 +18,8 @@ struct AttackArray;
* the future. */
namespace NoteDataUtil
{
NoteType GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex );
NoteType GetSmallestNoteTypeForMeasure( const NoteData &nd, int iMeasureIndex );
NoteType GetSmallestNoteTypeInRange( const NoteData &nd, int iStartIndex, int iEndIndex );
void LoadFromSMNoteDataString( NoteData &out, const RString &sSMNoteData, bool bComposite );
void GetSMNoteDataString( const NoteData &in, RString &notes_out );
void SplitCompositeNoteData( const NoteData &in, vector<NoteData> &out );
+96 -36
View File
@@ -243,13 +243,9 @@ float NoteField::GetWidth() const
return (fMaxX - fMinX + ARROW_SIZE) * fYZoom;
}
void NoteField::DrawBeatBar( const float fBeat )
void NoteField::DrawBeatBar( const float fBeat, BeatBarType type, int iMeasureIndex )
{
bool bIsMeasure = fmodf( fBeat, (float)BEATS_PER_MEASURE ) == 0;
int iMeasureIndex = (int)fBeat / BEATS_PER_MEASURE;
int iMeasureNoDisplay = iMeasureIndex+1;
NoteType nt = BeatToNoteType( fBeat );
bool bIsMeasure = type == measure;
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
const float fYPos = ArrowEffects::GetYPos( m_pPlayerState, 0, fYOffset, m_fYReverseOffsetPixels );
@@ -267,12 +263,13 @@ void NoteField::DrawBeatBar( const float fBeat )
float fScrollSpeed = m_pPlayerState->m_PlayerOptions.GetCurrent().m_fScrollSpeed;
if( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fTimeSpacing > 0 )
fScrollSpeed = 4;
switch( nt )
switch( type )
{
default: ASSERT(0);
case NOTE_TYPE_4TH: fAlpha = BAR_4TH_ALPHA; iState = 1; break;
case NOTE_TYPE_8TH: fAlpha = SCALE(fScrollSpeed,1.0f,2.0f,0.0f,BAR_8TH_ALPHA); iState = 2; break;
case NOTE_TYPE_16TH: fAlpha = SCALE(fScrollSpeed,2.0f,4.0f,0.0f,BAR_16TH_ALPHA); iState = 3; break;
DEFAULT_FAIL( type );
case measure:
case beat: fAlpha = BAR_4TH_ALPHA; iState = 1; break;
case half_beat: fAlpha = SCALE(fScrollSpeed,1.0f,2.0f,0.0f,BAR_8TH_ALPHA); iState = 2; break;
case quarter_beat: fAlpha = SCALE(fScrollSpeed,2.0f,4.0f,0.0f,BAR_16TH_ALPHA); iState = 3; break;
}
CLAMP( fAlpha, 0, 1 );
}
@@ -291,6 +288,8 @@ void NoteField::DrawBeatBar( const float fBeat )
if( GAMESTATE->IsEditing() && bIsMeasure )
{
int iMeasureNoDisplay = iMeasureIndex+1;
m_textMeasureNumber.SetDiffuse( RageColor(1,1,1,1) );
m_textMeasureNumber.SetGlow( RageColor(1,1,1,0) );
m_textMeasureNumber.SetHorizAlign( align_right );
@@ -392,6 +391,21 @@ void NoteField::DrawFreezeText( const float fBeat, const float fSecs )
m_textMeasureNumber.Draw();
}
void NoteField::DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator )
{
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
const float fYPos = ArrowEffects::GetYPos( m_pPlayerState, 0, fYOffset, m_fYReverseOffsetPixels );
const float fZoom = ArrowEffects::GetZoom( m_pPlayerState );
m_textMeasureNumber.SetZoom( fZoom );
m_textMeasureNumber.SetHorizAlign( align_right );
m_textMeasureNumber.SetDiffuse( RageColor(0.8f,0.8f,0,1) );
m_textMeasureNumber.SetGlow( RageColor(1,1,1,RageFastCos(RageTimer::GetTimeSinceStartFast()*2)/2+0.5f) );
m_textMeasureNumber.SetText( ssprintf("%d\n--\n%d", iNumerator, iDenominator) );
m_textMeasureNumber.SetXY( -GetWidth()/2.f - 30*fZoom, fYPos );
m_textMeasureNumber.Draw();
}
void NoteField::DrawAttackText( const float fBeat, const Attack &attack )
{
const float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, 0, fBeat );
@@ -495,7 +509,7 @@ float FindLastDisplayedBeat( const PlayerState* pPlayerState, int iDrawDistanceB
bool NoteField::IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const
{
// TRICKY: If boomerang is on, then ones in the range
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test to see if this beat is visible before drawing.
float fYOffset = ArrowEffects::GetYOffset( m_pPlayerState, iCol, fBeat );
if( fYOffset > iDrawDistanceBeforeTargetsPixels ) // off screen
@@ -545,11 +559,11 @@ void NoteField::DrawPrimitives()
m_pPlayerState->m_fLastDrawnBeat = fLastBeatToDraw;
const int iFirstIndexToDraw = BeatToNoteRow(fFirstBeatToDraw);
const int iLastIndexToDraw = BeatToNoteRow(fLastBeatToDraw);
const int iFirstRowToDraw = BeatToNoteRow(fFirstBeatToDraw);
const int iLastRowToDraw = BeatToNoteRow(fLastBeatToDraw);
// LOG->Trace( "start = %f.1, end = %f.1", fFirstBeatToDraw-fSongBeat, fLastBeatToDraw-fSongBeat );
// LOG->Trace( "Drawing elements %d through %d", iFirstIndexToDraw, iLastIndexToDraw );
// LOG->Trace( "Drawing elements %d through %d", iFirstRowToDraw, iLastRowToDraw );
#define IS_ON_SCREEN( fBeat ) IsOnScreen( fBeat, 0, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels )
@@ -573,11 +587,42 @@ void NoteField::DrawPrimitives()
//
if( GAMESTATE->IsEditing() || SHOW_BEAT_BARS )
{
float fStartDrawingMeasureBars = max( 0, Quantize(fFirstBeatToDraw-0.25f,0.25f) );
for( float f=fStartDrawingMeasureBars; f<fLastBeatToDraw; f+=0.25f )
const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments;
int iMeasureIndex = 0;
FOREACH_CONST( TimeSignatureSegment, vTimeSignatureSegments, iter )
{
if( IS_ON_SCREEN(f) )
DrawBeatBar( f );
vector<TimeSignatureSegment>::const_iterator next = iter;
next++;
int iSegmentEndRow = (next == vTimeSignatureSegments.end()) ? iLastRowToDraw : next->m_iStartRow;
// beat bars every 16th note
int iDrawBeatBarsEveryRows = BeatToNoteRow( ((float)iter->m_iDenominator) / 4 ) / 4;
// In 4/4, every 16th beat bar is a measure
int iMeasureBarFrequency = iter->m_iNumerator * 4;
int iBeatBarsDrawn = 0;
for( int i=iter->m_iStartRow; i < iSegmentEndRow; i += iDrawBeatBarsEveryRows )
{
bool bMeasureBar = iBeatBarsDrawn % iMeasureBarFrequency == 0;
BeatBarType type = quarter_beat;
if( bMeasureBar )
type = measure;
else if( iBeatBarsDrawn % 4 == 0 )
type = beat;
else if( iBeatBarsDrawn % 2 == 0 )
type = half_beat;
float fBeat = NoteRowToBeat(i);
if( IS_ON_SCREEN(fBeat) )
{
DrawBeatBar( fBeat, type, iMeasureIndex );
}
iBeatBarsDrawn++;
if( bMeasureBar )
iMeasureIndex++;
}
}
}
@@ -591,8 +636,8 @@ void NoteField::DrawPrimitives()
const vector<BPMSegment> &aBPMSegments = GAMESTATE->m_pCurSong->m_Timing.m_BPMSegments;
for( unsigned i=0; i<aBPMSegments.size(); i++ )
{
if( aBPMSegments[i].m_iStartRow >= iFirstIndexToDraw &&
aBPMSegments[i].m_iStartRow <= iLastIndexToDraw)
if( aBPMSegments[i].m_iStartRow >= iFirstRowToDraw &&
aBPMSegments[i].m_iStartRow <= iLastRowToDraw)
{
float fBeat = NoteRowToBeat(aBPMSegments[i].m_iStartRow);
if( IS_ON_SCREEN(fBeat) )
@@ -606,8 +651,8 @@ void NoteField::DrawPrimitives()
const vector<StopSegment> &aStopSegments = GAMESTATE->m_pCurSong->m_Timing.m_StopSegments;
for( unsigned i=0; i<aStopSegments.size(); i++ )
{
if( aStopSegments[i].m_iStartRow >= iFirstIndexToDraw &&
aStopSegments[i].m_iStartRow <= iLastIndexToDraw)
if( aStopSegments[i].m_iStartRow >= iFirstRowToDraw &&
aStopSegments[i].m_iStartRow <= iLastRowToDraw)
{
float fBeat = NoteRowToBeat(aStopSegments[i].m_iStartRow);
if( IS_ON_SCREEN(fBeat) )
@@ -615,6 +660,21 @@ void NoteField::DrawPrimitives()
}
}
//
// Time Signature text
//
const vector<TimeSignatureSegment> &vTimeSignatureSegments = GAMESTATE->m_pCurSong->m_Timing.m_vTimeSignatureSegments;
for( unsigned i=0; i<vTimeSignatureSegments.size(); i++ )
{
if( vTimeSignatureSegments[i].m_iStartRow >= iFirstRowToDraw &&
vTimeSignatureSegments[i].m_iStartRow <= iLastRowToDraw)
{
float fBeat = NoteRowToBeat(vTimeSignatureSegments[i].m_iStartRow);
if( IS_ON_SCREEN(fBeat) )
DrawTimeSignatureText( fBeat, vTimeSignatureSegments[i].m_iNumerator, vTimeSignatureSegments[i].m_iDenominator );
}
}
//
// Course mods text
//
@@ -629,8 +689,8 @@ void NoteField::DrawPrimitives()
float fSecond = a->fStartSecond;
float fBeat = GAMESTATE->m_pCurSong->m_Timing.GetBeatFromElapsedTime( fSecond );
if( BeatToNoteRow(fBeat) >= iFirstIndexToDraw &&
BeatToNoteRow(fBeat) <= iLastIndexToDraw)
if( BeatToNoteRow(fBeat) >= iFirstRowToDraw &&
BeatToNoteRow(fBeat) <= iLastRowToDraw)
{
if( IS_ON_SCREEN(fBeat) )
DrawAttackText( fBeat, *a );
@@ -713,20 +773,20 @@ void NoteField::DrawPrimitives()
{
int iBegin = m_iBeginMarker;
int iEnd = m_iEndMarker;
CLAMP( iBegin, iFirstIndexToDraw, iLastIndexToDraw );
CLAMP( iEnd, iFirstIndexToDraw, iLastIndexToDraw );
CLAMP( iBegin, iFirstRowToDraw, iLastRowToDraw );
CLAMP( iEnd, iFirstRowToDraw, iLastRowToDraw );
DrawAreaHighlight( iBegin, iEnd );
}
else if( m_iBeginMarker != -1 )
{
if( m_iBeginMarker >= iFirstIndexToDraw &&
m_iBeginMarker <= iLastIndexToDraw )
if( m_iBeginMarker >= iFirstRowToDraw &&
m_iBeginMarker <= iLastRowToDraw )
DrawMarkerBar( m_iBeginMarker );
}
else if( m_iEndMarker != -1 )
{
if( m_iEndMarker >= iFirstIndexToDraw &&
m_iEndMarker <= iLastIndexToDraw )
if( m_iEndMarker >= iFirstRowToDraw &&
m_iEndMarker <= iLastRowToDraw )
DrawMarkerBar( m_iEndMarker );
}
}
@@ -752,7 +812,7 @@ void NoteField::DrawPrimitives()
//
{
NoteData::TrackMap::const_iterator begin, end;
m_pNoteData->GetTapNoteRangeInclusive( c, iFirstIndexToDraw, iLastIndexToDraw+1, begin, end );
m_pNoteData->GetTapNoteRangeInclusive( c, iFirstRowToDraw, iLastRowToDraw+1, begin, end );
for( ; begin != end; ++begin )
{
@@ -768,7 +828,7 @@ void NoteField::DrawPrimitives()
int iEndRow = iStartRow + tn.iDuration;
// TRICKY: If boomerang is on, then all notes in the range
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing
float fThrowAway;
bool bStartIsPastPeak = false;
@@ -815,7 +875,7 @@ void NoteField::DrawPrimitives()
// draw notes from furthest to closest
NoteData::TrackMap::const_iterator begin, end;
m_pNoteData->GetTapNoteRange( c, iFirstIndexToDraw, iLastIndexToDraw+1, begin, end );
m_pNoteData->GetTapNoteRange( c, iFirstRowToDraw, iLastRowToDraw+1, begin, end );
for( ; begin != end; ++begin )
{
int i = begin->first;
@@ -833,12 +893,12 @@ void NoteField::DrawPrimitives()
// TRICKY: If boomerang is on, then all notes in the range
// [iFirstIndexToDraw,iLastIndexToDraw] aren't necessarily visible.
// [iFirstRowToDraw,iLastRowToDraw] aren't necessarily visible.
// Test every note to make sure it's on screen before drawing
if( !IsOnScreen( NoteRowToBeat(i), c, iDrawDistanceAfterTargetsPixels, iDrawDistanceBeforeTargetsPixels ) )
continue; // skip
ASSERT_M( NoteRowToBeat(i) > -2000, ssprintf("%i %i %i, %f %f", i, iLastIndexToDraw, iFirstIndexToDraw, GAMESTATE->m_fSongBeat, GAMESTATE->m_fMusicSeconds) );
ASSERT_M( NoteRowToBeat(i) > -2000, ssprintf("%i %i %i, %f %f", i, iLastRowToDraw, iFirstRowToDraw, GAMESTATE->m_fSongBeat, GAMESTATE->m_fMusicSeconds) );
// See if there is a hold step that begins on this index. Only do this
// if the note skin cares.
+4 -1
View File
@@ -52,11 +52,14 @@ protected:
bool IsOnScreen( float fBeat, int iCol, int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels ) const;
void DrawBoard( int iDrawDistanceAfterTargetsPixels, int iDrawDistanceBeforeTargetsPixels );
void DrawBeatBar( const float fBeat );
enum BeatBarType { measure, beat, half_beat, quarter_beat };
void DrawBeatBar( const float fBeat, BeatBarType type, int iMeasureIndex );
void DrawMarkerBar( int fBeat );
void DrawAreaHighlight( int iStartBeat, int iEndBeat );
void DrawBPMText( const float fBeat, const float fBPM );
void DrawFreezeText( const float fBeat, const float fBPM );
void DrawTimeSignatureText( const float fBeat, int iNumerator, int iDenominator );
void DrawAttackText( const float fBeat, const Attack &attack );
void DrawBGChangeText( const float fBeat, const RString sNewBGName );
float GetWidth() const;
+4
View File
@@ -49,6 +49,10 @@ float NoteTypeToBeat( NoteType nt )
}
}
// FIXME: Remove hard-coded beat values and instead look at the time signature in the song.
static const int BEATS_PER_MEASURE = 4;
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
NoteType GetNoteType( int row )
{
if( row % (ROWS_PER_MEASURE/4) == 0) return NOTE_TYPE_4TH;
+1 -2
View File
@@ -164,8 +164,7 @@ const int ROWS_PER_BEAT = 48;
/* In the editor, enforce a reasonable limit on the number of notes. */
const int MAX_NOTES_PER_MEASURE = 50;
const int BEATS_PER_MEASURE = 4;
const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
const int MAX_NOTE_ROW = (1<<30);
enum NoteType
+3
View File
@@ -422,6 +422,9 @@ static void ReadTimeSigs( const NameToData_t &mapNameToData, MeasureToTimeSig_t
}
}
static const int BEATS_PER_MEASURE = 4;
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameToData, Steps &out,
const MeasureToTimeSig_t &sigAdjustments, const map<RString,int> &idToKeySoundIndex )
{
+2
View File
@@ -113,6 +113,8 @@ static bool Is192( const RString &sStepData, size_t pos )
return false;
}
const int BEATS_PER_MEASURE = 4;
static bool LoadFromDWITokens(
RString sMode,
RString sDescription,
+46
View File
@@ -151,6 +151,52 @@ void SMLoader::LoadTimingFromSMFile( const MsdFile &msd, TimingData &out )
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid BPM change at beat %f, BPM %f.", fBeat, fNewBPM );
}
}
else if( sValueName=="TIMESIGNATURES" )
{
vector<RString> vs1;
split( sParams[1], ",", vs1 );
FOREACH_CONST( RString, vs1, s1 )
{
vector<RString> vs2;
split( *s1, "=", vs2 );
if( vs2.size() < 3 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with %i values.", (int)vs2.size() );
continue;
}
const float fBeat = StringToFloat( vs2[0] );
TimeSignatureSegment seg;
seg.m_iStartRow = BeatToNoteRow(fBeat);
seg.m_iNumerator = atoi( vs2[1] );
seg.m_iDenominator = atoi( vs2[2] );
if( fBeat < 0 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f.", fBeat );
continue;
}
if( seg.m_iNumerator < 1 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iNumerator %i.", fBeat, seg.m_iNumerator );
continue;
}
if( seg.m_iDenominator < 1 )
{
LOG->UserLog( "Song file", "(UNKNOWN)", "has an invalid time signature change with beat %f, iDenominator %i.", fBeat, seg.m_iDenominator );
continue;
}
out.AddTimeSignatureSegment( seg );
}
}
}
}
+2
View File
@@ -191,6 +191,8 @@ RString OptimizeDWIString( RString holds, RString taps )
return ssprintf( "<%s>", ret.c_str() );
}
static const int BEATS_PER_MEASURE = 4;
static void WriteDWINotesField( RageFile &f, const Steps &out, int start )
{
NoteData notedata;
+12
View File
@@ -106,6 +106,18 @@ static void WriteGlobalTags( RageFile &f, const Song &out )
}
f.PutLine( ";" );
ASSERT( !out.m_Timing.m_vTimeSignatureSegments.empty() );
f.Write( "#TIMESIGNATURES:" );
FOREACH_CONST( TimeSignatureSegment, out.m_Timing.m_vTimeSignatureSegments, iter )
{
f.PutLine( ssprintf( "%.3f=%d=%d", NoteRowToBeat(iter->m_iStartRow), iter->m_iNumerator, iter->m_iDenominator ) );
vector<TimeSignatureSegment>::const_iterator iter2 = iter;
iter2++;
if( iter2 != out.m_Timing.m_vTimeSignatureSegments.end() )
f.Write( "," );
}
f.PutLine( ";" );
FOREACH_BackgroundLayer( b )
{
if( b==0 )
+4
View File
@@ -58,6 +58,10 @@ const float RECORD_HOLD_SECONDS = 0.3f;
#define PLAY_RECORD_HELP_TEXT THEME->GetString(m_sName,"PlayRecordHelpText")
#define EDIT_HELP_TEXT THEME->GetString(m_sName,"EditHelpText")
// FIXME: Remove hard-coded beat values and instead look at the time signature in the song.
static const int BEATS_PER_MEASURE = 4;
static const int ROWS_PER_MEASURE = ROWS_PER_BEAT * BEATS_PER_MEASURE;
AutoScreenMessage( SM_UpdateTextInfo )
AutoScreenMessage( SM_BackFromMainMenu )
AutoScreenMessage( SM_BackFromAreaMenu )
+11 -1
View File
@@ -33,7 +33,7 @@
#include <set>
#include <float.h>
const int FILE_CACHE_VERSION = 154; // increment this to invalidate cache
const int FILE_CACHE_VERSION = 156; // increment this to invalidate cache
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
@@ -689,6 +689,16 @@ void Song::TidyUpData()
}
}
}
/* If no time signature specified, assume 4/4 time for the whole song. */
if( m_Timing.m_vTimeSignatureSegments.empty() )
{
TimeSignatureSegment seg;
seg.m_iStartRow = 0;
seg.m_iNumerator = 4;
seg.m_iDenominator = 4;
m_Timing.m_vTimeSignatureSegments.push_back( seg );
}
}
void Song::TranslateTitles()
+41
View File
@@ -7,6 +7,7 @@
#include "Foreach.h"
#include <float.h>
TimingData::TimingData()
{
m_fBeat0OffsetInSeconds = 0;
@@ -35,6 +36,11 @@ void TimingData::AddStopSegment( const StopSegment &seg )
m_StopSegments.insert( upper_bound(m_StopSegments.begin(), m_StopSegments.end(), seg), seg );
}
void TimingData::AddTimeSignatureSegment( const TimeSignatureSegment &seg )
{
m_vTimeSignatureSegments.insert( upper_bound(m_vTimeSignatureSegments.begin(), m_vTimeSignatureSegments.end(), seg), seg );
}
/* Change an existing BPM segment, merge identical segments together or insert a new one. */
void TimingData::SetBPMAtRow( int iNoteRow, float fBPM )
{
@@ -392,6 +398,41 @@ bool TimingData::HasStops() const
return m_StopSegments.size()>0;
}
void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const
{
iMeasureIndexOut = 0;
FOREACH_CONST( TimeSignatureSegment, m_vTimeSignatureSegments, iter )
{
vector<TimeSignatureSegment>::const_iterator next = iter;
next++;
int iSegmentEndRow = (next == m_vTimeSignatureSegments.end()) ? INT_MAX : next->m_iStartRow;
int iRowsPerMeasureThisSegment = iter->GetNoteRowsPerMeasure();
if( iNoteRow >= iter->m_iStartRow )
{
// iNoteRow lands in this segment
int iNumRowsThisSegment = iNoteRow - iter->m_iStartRow;
int iNumMeasuresThisSegment = (iNumRowsThisSegment) / iRowsPerMeasureThisSegment; // don't round up
iMeasureIndexOut += iNumMeasuresThisSegment;
iBeatIndexOut = iNumRowsThisSegment / iRowsPerMeasureThisSegment;
iRowsRemainder = iNumRowsThisSegment % iRowsPerMeasureThisSegment;
return;
}
else
{
// iNoteRow lands after this segment
int iNumRowsThisSegment = iSegmentEndRow - iter->m_iStartRow;
int iNumMeasuresThisSegment = (iNumRowsThisSegment + iRowsPerMeasureThisSegment - 1) / iRowsPerMeasureThisSegment; // round up
iMeasureIndexOut += iNumMeasuresThisSegment;
}
}
ASSERT(0);
return;
}
/*
* (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
+26 -3
View File
@@ -44,6 +44,26 @@ struct StopSegment
bool operator<( const StopSegment &other ) const { return m_iStartRow < other.m_iStartRow; }
};
struct TimeSignatureSegment
{
TimeSignatureSegment() : m_iStartRow(-1), m_iNumerator(4), m_iDenominator(4) { }
int m_iStartRow;
int m_iNumerator;
int m_iDenominator;
int GetNoteRowsPerMeasure() const { return (BeatToNoteRow(1) * 4) / m_iDenominator; }
bool operator==( const TimeSignatureSegment &other ) const
{
COMPARE( m_iStartRow );
COMPARE( m_iNumerator );
COMPARE( m_iDenominator );
return true;
}
bool operator!=( const TimeSignatureSegment &other ) const { return !operator==(other); }
bool operator<( const TimeSignatureSegment &other ) const { return m_iStartRow < other.m_iStartRow; }
};
class TimingData
{
public:
@@ -59,8 +79,10 @@ public:
void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor );
void AddBPMSegment( const BPMSegment &seg );
void AddStopSegment( const StopSegment &seg );
void AddTimeSignatureSegment( const TimeSignatureSegment &seg );
int GetBPMSegmentIndexAtBeat( float fBeat );
BPMSegment& GetBPMSegmentAtBeat( float fBeat );
void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const;
void GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut ) const;
float GetBeatFromElapsedTime( float fElapsedTime ) const // shortcut for places that care only about the beat
@@ -102,9 +124,10 @@ public:
void InsertRows( int iStartRow, int iRowsToAdd );
void DeleteRows( int iStartRow, int iRowsToDelete );
RString m_sFile; // informational only
vector<BPMSegment> m_BPMSegments; // this must be sorted before gameplay
vector<StopSegment> m_StopSegments; // this must be sorted before gameplay
RString m_sFile; // informational only
vector<BPMSegment> m_BPMSegments; // this must be sorted before gameplay
vector<StopSegment> m_StopSegments; // this must be sorted before gameplay
vector<TimeSignatureSegment> m_vTimeSignatureSegments; // this must be sorted before gameplay
float m_fBeat0OffsetInSeconds;
};