diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml
index f41bb848cb..966502afbc 100644
--- a/Docs/Luadoc/Lua.xml
+++ b/Docs/Luadoc/Lua.xml
@@ -1405,6 +1405,7 @@
+
diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml
index 39df9f4188..24492431aa 100644
--- a/Docs/Luadoc/LuaDocumentation.xml
+++ b/Docs/Luadoc/LuaDocumentation.xml
@@ -3516,6 +3516,9 @@
Returns true if the TimingData contains warps.
+
+ Returns true if the TimingData contains speed scrolling changes.
+
diff --git a/src/TimingData.cpp b/src/TimingData.cpp
index 2f93424bc7..45302128b8 100644
--- a/src/TimingData.cpp
+++ b/src/TimingData.cpp
@@ -68,6 +68,11 @@ void TimingData::AddLabelSegment( const LabelSegment &seg )
m_LabelSegments.insert( upper_bound(m_LabelSegments.begin(), m_LabelSegments.end(), seg), seg );
}
+void TimingData::AddSpeedSegment( const SpeedSegment &seg )
+{
+ m_SpeedSegments.insert( upper_bound(m_SpeedSegments.begin(), m_SpeedSegments.end(), seg), seg );
+}
+
/* Change an existing BPM segment, merge identical segments together or insert a new one. */
void TimingData::SetBPMAtRow( int iNoteRow, float fBPM )
{
@@ -250,6 +255,47 @@ void TimingData::SetLabelAtRow( int iRow, const RString sLabel )
}
}
+void TimingData::SetSpeedAtRow( int iRow, float fPercent, float fWait )
+{
+ unsigned i;
+ for( i = 0; i < m_SpeedSegments.size(); i++ )
+ {
+ if( m_SpeedSegments[i].m_iStartRow >= iRow)
+ break;
+ }
+
+ if ( i == m_SpeedSegments.size() || m_SpeedSegments[i].m_iStartRow != iRow )
+ {
+ if( i == 0 ||
+ ( m_SpeedSegments[i-1].m_fPercent != fPercent
+ || m_SpeedSegments[i-1].m_fWait != fWait ) )
+ AddSpeedSegment( SpeedSegment(iRow, fPercent, fWait) );
+ }
+ else
+ {
+ if( i > 0 && m_SpeedSegments[i-1].m_fPercent == fPercent
+ && m_SpeedSegments[i-1].m_fWait == fWait )
+ m_SpeedSegments.erase( m_SpeedSegments.begin()+i,
+ m_SpeedSegments.begin()+i+1 );
+ else
+ {
+ m_SpeedSegments[i].m_fPercent = fPercent;
+ m_SpeedSegments[i].m_fWait = fWait;
+ }
+ }
+}
+
+void TimingData::SetSpeedPercentAtRow( int iRow, float fPercent )
+{
+ SetSpeedAtRow( iRow, fPercent, GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fWait );
+}
+
+void TimingData::SetSpeedWaitAtRow( int iRow, float fWait )
+{
+ SetSpeedAtRow( iRow, GetSpeedSegmentAtBeat( NoteRowToBeat( iRow ) ).m_fPercent, fWait );
+}
+
+
float TimingData::GetStopAtRow( int iNoteRow, bool bDelay ) const
{
for( unsigned i=0; i(i);
}
+int TimingData::GetSpeedSegmentIndexAtRow( int iRow ) const
+{
+ unsigned i;
+ for (i=0; i < m_SpeedSegments.size() - 1; i++ )
+ if( m_SpeedSegments[i+1].m_iStartRow > iRow )
+ break;
+ return static_cast(i);
+}
+
BPMSegment& TimingData::GetBPMSegmentAtRow( int iNoteRow )
{
static BPMSegment empty;
@@ -451,6 +516,15 @@ TimeSignatureSegment& TimingData::GetTimeSignatureSegmentAtRow( int iRow )
return m_vTimeSignatureSegments[i];
}
+SpeedSegment& TimingData::GetSpeedSegmentAtRow( int iRow )
+{
+ unsigned i;
+ for( i=0; i iRow )
+ break;
+ return m_SpeedSegments[i];
+}
+
int TimingData::GetTimeSignatureNumeratorAtRow( int iRow )
{
return GetTimeSignatureSegmentAtRow( iRow ).m_iNumerator;
@@ -845,6 +919,17 @@ void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool
m_LabelSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex;
}
+ for ( unsigned i = 0; i < m_SpeedSegments.size(); i++ )
+ {
+ const int iSegStart = m_SpeedSegments[i].m_iStartRow;
+ if( iSegStart < iStartIndex )
+ continue;
+ else if( iSegStart > iEndIndex )
+ m_SpeedSegments[i].m_iStartRow += lrintf( (iEndIndex - iStartIndex) * (fScale - 1) );
+ else
+ m_SpeedSegments[i].m_iStartRow = lrintf( (iSegStart - iStartIndex) * fScale ) + iStartIndex;
+ }
+
// adjust BPM changes to preserve timing
if( bAdjustBPM )
{
@@ -929,6 +1014,14 @@ void TimingData::InsertRows( int iStartRow, int iRowsToAdd )
continue;
labl.m_iStartRow += iRowsToAdd;
}
+
+ for( unsigned i = 0; i < m_SpeedSegments.size(); i++ )
+ {
+ SpeedSegment &sped = m_SpeedSegments[i];
+ if( sped.m_iStartRow < iStartRow )
+ continue;
+ sped.m_iStartRow += iRowsToAdd;
+ }
if( iStartRow == 0 )
{
@@ -1086,6 +1179,22 @@ void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
}
labl.m_iStartRow -= iRowsToDelete;
}
+
+ for( unsigned i = 0; i < m_SpeedSegments.size(); i++ )
+ {
+ SpeedSegment &sped = m_SpeedSegments[i];
+
+ if( sped.m_iStartRow < iStartRow )
+ continue;
+
+ if( sped.m_iStartRow < iStartRow+iRowsToDelete )
+ {
+ m_SpeedSegments.erase( m_SpeedSegments.begin()+i, m_SpeedSegments.begin()+i+1 );
+ --i;
+ continue;
+ }
+ sped.m_iStartRow -= iRowsToDelete;
+ }
this->SetBPMAtRow( iStartRow, fNewBPM );
}
@@ -1133,7 +1242,13 @@ void TimingData::TidyUpData()
LabelSegment seg(0, "Song Start");
m_LabelSegments.push_back( seg );
}
-
+
+ // Always be sure there is a starting speed.
+ if( m_SpeedSegments.empty() )
+ {
+ SpeedSegment seg(0, 1, 0);
+ m_SpeedSegments.push_back( seg );
+ }
}
@@ -1155,6 +1270,11 @@ bool TimingData::HasWarps() const
return m_WarpSegments.size()>0;
}
+bool TimingData::HasSpeedChanges() const
+{
+ return m_SpeedSegments.size()>1;
+}
+
void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const
{
iMeasureIndexOut = 0;
@@ -1201,6 +1321,7 @@ public:
static int HasStops( T* p, lua_State *L ) { lua_pushboolean(L, p->HasStops()); return 1; }
static int HasBPMChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasBpmChanges()); return 1; }
static int HasWarps( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; }
+ static int HasSpeedChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSpeedChanges()); return 1; }
static int GetStops( T* p, lua_State *L )
{
vector vStops;
@@ -1288,6 +1409,7 @@ public:
ADD_METHOD( HasStops );
ADD_METHOD( HasBPMChanges );
ADD_METHOD( HasWarps );
+ ADD_METHOD( HasSpeedChanges );
ADD_METHOD( GetStops );
ADD_METHOD( GetDelays );
ADD_METHOD( GetBPMs );
diff --git a/src/TimingData.h b/src/TimingData.h
index c1d1b2ea87..d68e2ec98e 100644
--- a/src/TimingData.h
+++ b/src/TimingData.h
@@ -1339,6 +1339,101 @@ public:
*/
float GetNextLabelSegmentBeatAtBeat( float fBeat ) const { return GetNextLabelSegmentBeatAtRow( BeatToNoteRow(fBeat) ); }
+
+ /**
+ * @brief Retrieve the Speed's percent at the given row.
+ * @param iNoteRow the row in question.
+ * @return the percent.
+ */
+ float GetSpeedPercentAtRow( int iNoteRow );
+ /**
+ * @brief Retrieve the Speed's percent at the given beat.
+ * @param fBeat the beat in question.
+ * @return the percent.
+ */
+ float GetSpeedPercentAtBeat( float fBeat ) { return GetSpeedPercentAtRow( BeatToNoteRow(fBeat) ); }
+ /**
+ * @brief Retrieve the Speed's wait at the given row.
+ * @param iNoteRow the row in question.
+ * @return the wait.
+ */
+ float GetSpeedWaitAtRow( int iNoteRow );
+ /**
+ * @brief Retrieve the Speed's wait at the given beat.
+ * @param fBeat the beat in question.
+ * @return the wait.
+ */
+ float GetSpeedWaitAtBeat( float fBeat ) { return GetSpeedWaitAtRow( BeatToNoteRow(fBeat) ); }
+ /**
+ * @brief Set the row to have the new Speed.
+ * @param iNoteRow the row to have the new Speed.
+ * @param fPercent the percent.
+ * @param fWait the wait.
+ */
+ void SetSpeedAtRow( int iNoteRow, float fPercent, float fWait );
+ /**
+ * @brief Set the beat to have the new Speed.
+ * @param fBeat the beat to have the new Speed.
+ * @param fPercent the percent.
+ * @param fWait the wait.
+ */
+ void SetSpeedAtBeat( float fBeat, float fPercent, float fWait ) { SetSpeedAtRow( BeatToNoteRow(fBeat), fPercent, fWait ); }
+ /**
+ * @brief Set the row to have the new Speed percent.
+ * @param iNoteRow the row to have the new Speed percent.
+ * @param fPercent the percent.
+ */
+ void SetSpeedPercentAtRow( int iNoteRow, float fPercent );
+ /**
+ * @brief Set the beat to have the new Speed percent.
+ * @param fBeat the beat to have the new Speed percent.
+ * @param fPercent the percent.
+ */
+ void SetSpeedPercentAtBeat( float fBeat, float fPercent ) { SetSpeedPercentAtRow( BeatToNoteRow(fBeat), fPercent); }
+ /**
+ * @brief Set the row to have the new Speed wait.
+ * @param iNoteRow the row to have the new Speed wait.
+ * @param fWait the wait.
+ */
+ void SetSpeedWaitAtRow( int iNoteRow, float fWait );
+ /**
+ * @brief Set the beat to have the new Speed wait.
+ * @param fBeat the beat to have the new Speed wait.
+ * @param fWait the wait.
+ */
+ void SetSpeedWaitAtBeat( float fBeat, float fWait ) { SetSpeedWaitAtRow( BeatToNoteRow(fBeat), fWait); }
+ /**
+ * @brief Retrieve the SpeedSegment at the specified row.
+ * @param iNoteRow the row that has a SpeedSegment.
+ * @return the SpeedSegment in question.
+ */
+ SpeedSegment& GetSpeedSegmentAtRow( int iNoteRow );
+ /**
+ * @brief Retrieve the SpeedSegment at the specified beat.
+ * @param fBeat the beat that has a SpeedSegment.
+ * @return the SpeedSegment in question.
+ */
+ SpeedSegment& GetSpeedSegmentAtBeat( float fBeat ) { return GetSpeedSegmentAtRow( BeatToNoteRow(fBeat) ); }
+ /**
+ * @brief Retrieve the index of the SpeedSegments at the specified row.
+ * @param iNoteRow the row that has a SpeedSegment.
+ * @return the SpeedSegment's index in question.
+ */
+ int GetSpeedSegmentIndexAtRow( int iNoteRow ) const;
+ /**
+ * @brief Retrieve the index of the SpeedSegments at the specified beat.
+ * @param fBeat the beat that has a SpeedSegment.
+ * @return the SpeedSegment's index in question.
+ */
+ int GetSpeedSegmentIndexAtBeat( float fBeat ) const { return GetSpeedSegmentIndexAtRow( BeatToNoteRow(fBeat) ); }
+ /**
+ * @brief Add the SpeedSegment to the TimingData.
+ * @param seg the new SpeedSegment.
+ */
+ void AddSpeedSegment( const SpeedSegment &seg );
+
+
+
void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor );
void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const;
@@ -1381,6 +1476,10 @@ public:
* @return true if there is at least one warp, false otherwise.
*/
bool HasWarps() const;
+ /**
+ * @brief View the TimingData to see if a song changes its speed scrolling at any point.
+ * @return true if there is at least one change, false otherwise. */
+ bool HasSpeedChanges() const;
/**
* @brief Compare two sets of timing data to see if they are equal.
* @param other the other TimingData.