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:
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ¬es_out );
|
||||
void SplitCompositeNoteData( const NoteData &in, vector<NoteData> &out );
|
||||
|
||||
+96
-36
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user