2011-03-17 01:47:30 -04:00
|
|
|
#include "global.h"
|
|
|
|
|
#include "TimingData.h"
|
|
|
|
|
#include "PrefsManager.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include "NoteTypes.h"
|
|
|
|
|
#include "Foreach.h"
|
|
|
|
|
#include <float.h>
|
|
|
|
|
|
2011-09-10 03:08:59 +00:00
|
|
|
TimingData::TimingData(float fOffset) : m_fBeat0OffsetInSeconds(fOffset)
|
2011-07-14 12:31:42 -04:00
|
|
|
{
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
2011-07-14 19:29:52 -04:00
|
|
|
TimingData::~TimingData()
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
// This is causing weird crashes, probably due to someone hanging onto pointers
|
|
|
|
|
// for too long. Commenting this out until we can track it down... -- vyhd
|
|
|
|
|
#if 0
|
|
|
|
|
/* Delete all pointers owned by this TimingData. */
|
|
|
|
|
FOREACH_TimingSegmentType( tst )
|
|
|
|
|
{
|
|
|
|
|
vector<TimingSegment*> &vSegs = m_avpTimingSegments[tst];
|
|
|
|
|
for( unsigned i = 0; i < vSegs.size(); ++i )
|
|
|
|
|
delete vSegs[i];
|
|
|
|
|
vSegs.clear();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2011-07-14 19:29:52 -04:00
|
|
|
}
|
|
|
|
|
|
2011-07-26 14:10:51 -04:00
|
|
|
bool TimingData::empty() const
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
FOREACH_TimingSegmentType( tst )
|
|
|
|
|
if( !GetTimingSegments(tst).empty() )
|
2011-07-26 14:10:51 -04:00
|
|
|
return false;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-07-26 14:10:51 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-26 14:13:10 -04:00
|
|
|
TimingData TimingData::CopyRange(int startRow, int endRow) const
|
2011-07-26 13:41:41 -04:00
|
|
|
{
|
2011-07-26 16:05:14 -04:00
|
|
|
TimingData ret;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
|
|
|
|
FOREACH_TimingSegmentType( tst )
|
2011-07-26 13:41:41 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment*> &vSegs = GetTimingSegments(tst);
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < vSegs.size(); i++)
|
2011-07-26 13:41:41 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const TimingSegment *seg = vSegs[i];
|
|
|
|
|
int row = seg->GetRow();
|
|
|
|
|
|
2011-07-26 13:41:41 -04:00
|
|
|
if (row >= startRow && row < endRow)
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
TimingSegment *cpy = seg->Copy();
|
|
|
|
|
|
|
|
|
|
// offset rows as though startRow were beat 0.
|
|
|
|
|
cpy->SetRow(seg->GetRow() - startRow);
|
|
|
|
|
ret.AddSegment(cpy);
|
2011-07-26 13:41:41 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-07-26 16:05:14 -04:00
|
|
|
return ret;
|
2011-07-26 13:41:41 -04:00
|
|
|
}
|
|
|
|
|
|
2011-07-03 13:57:22 -04:00
|
|
|
void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut, float highest ) const
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
fMinBPMOut = FLT_MAX;
|
|
|
|
|
fMaxBPMOut = 0;
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment*> &bpms = GetTimingSegments(SEGMENT_BPM);
|
|
|
|
|
|
2011-07-14 15:22:32 -04:00
|
|
|
for (unsigned i = 0; i < bpms.size(); i++)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const float fBPM = ToBPM(bpms[i])->GetBPM();
|
2011-07-03 13:57:22 -04:00
|
|
|
fMaxBPMOut = clamp(max( fBPM, fMaxBPMOut ), 0, highest);
|
2011-03-17 01:47:30 -04:00
|
|
|
fMinBPMOut = min( fBPM, fMinBPMOut );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-10 03:08:59 +00:00
|
|
|
float TimingData::GetNextSegmentBeatAtRow(TimingSegmentType tst, int row) const
|
2011-07-14 21:51:09 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> segs = GetTimingSegments(tst);
|
2011-07-14 23:54:06 -04:00
|
|
|
for (unsigned i = 0; i < segs.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if( segs[i]->GetRow() <= row )
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2011-07-27 23:32:37 -04:00
|
|
|
return segs[i]->GetBeat();
|
2011-07-14 23:54:06 -04:00
|
|
|
}
|
|
|
|
|
return NoteRowToBeat(row);
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-10 03:08:59 +00:00
|
|
|
float TimingData::GetPreviousSegmentBeatAtRow(TimingSegmentType tst, int row) const
|
2011-07-14 23:54:06 -04:00
|
|
|
{
|
|
|
|
|
float backup = -1;
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> segs = GetTimingSegments(tst);
|
2011-07-14 23:54:06 -04:00
|
|
|
for (unsigned i = 0; i < segs.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
if( segs[i]->GetRow() >= row )
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-07-27 23:32:37 -04:00
|
|
|
backup = segs[i]->GetBeat();
|
2011-07-14 23:54:06 -04:00
|
|
|
}
|
|
|
|
|
return (backup > -1) ? backup : NoteRowToBeat(row);
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
static const int INVALID_INDEX = -1;
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
int TimingData::GetSegmentIndexAtRow(TimingSegmentType tst, int iRow ) const
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment*> &vSegs = GetTimingSegments(tst);
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
if( vSegs.empty() )
|
|
|
|
|
return INVALID_INDEX;
|
2011-07-09 02:28:15 -04:00
|
|
|
|
2011-09-16 21:03:06 +07:00
|
|
|
int min = 0, max = vSegs.size() - 1;
|
|
|
|
|
int l = min, r = max;
|
|
|
|
|
while( l <= r )
|
2011-05-25 22:44:41 +07:00
|
|
|
{
|
2011-09-16 21:03:06 +07:00
|
|
|
int m = ( l + r ) / 2;
|
|
|
|
|
if( ( m == min || vSegs[m]->GetRow() <= iRow ) && ( m == max || iRow < vSegs[m + 1]->GetRow() ) )
|
|
|
|
|
{
|
|
|
|
|
return m;
|
|
|
|
|
}
|
|
|
|
|
else if( vSegs[m]->GetRow() <= iRow )
|
|
|
|
|
{
|
|
|
|
|
l = m + 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
r = m - 1;
|
|
|
|
|
}
|
2011-05-16 02:00:17 -04:00
|
|
|
}
|
2011-09-16 21:03:06 +07:00
|
|
|
|
|
|
|
|
return INVALID_INDEX; // this should not be reached. :(
|
2011-05-15 13:48:10 -04:00
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
struct ts_less : binary_function <TimingSegment*, TimingSegment*, bool>
|
2011-05-16 02:00:17 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
bool operator() (const TimingSegment *x, const TimingSegment *y) const
|
2011-05-16 02:00:17 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
return (*x) < (*y);
|
2011-05-16 02:00:17 -04:00
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
};
|
2011-05-16 02:00:17 -04:00
|
|
|
|
2011-03-17 01:47:30 -04:00
|
|
|
// Multiply the BPM in the range [fStartBeat,fEndBeat) by fFactor.
|
|
|
|
|
void TimingData::MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor )
|
|
|
|
|
{
|
|
|
|
|
// Change all other BPM segments in this range.
|
2011-09-10 03:08:59 +00:00
|
|
|
vector<TimingSegment *> &bpms = m_avpTimingSegments[SEGMENT_BPM];
|
2011-07-14 22:19:32 -04:00
|
|
|
for( unsigned i=0; i<bpms.size(); i++ )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
BPMSegment *bs = ToBPM(bpms[i]);
|
2011-07-14 22:19:32 -04:00
|
|
|
const int iStartIndexThisSegment = bs->GetRow();
|
|
|
|
|
const bool bIsLastBPMSegment = i == bpms.size()-1;
|
|
|
|
|
const int iStartIndexNextSegment = bIsLastBPMSegment ? INT_MAX : bpms[i+1]->GetRow();
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
if( iStartIndexThisSegment <= iStartIndex && iStartIndexNextSegment <= iStartIndex )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* If this BPM segment crosses the beginning of the range,
|
|
|
|
|
* split it into two. */
|
|
|
|
|
if( iStartIndexThisSegment < iStartIndex && iStartIndexNextSegment > iStartIndex )
|
|
|
|
|
{
|
2011-07-14 22:19:32 -04:00
|
|
|
BPMSegment * b = new BPMSegment(iStartIndexNextSegment,
|
|
|
|
|
bs->GetBPS());
|
|
|
|
|
bpms.insert(bpms.begin()+i+1, b);
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
/* Don't apply the BPM change to the first half of the segment we
|
|
|
|
|
* just split, since it lies outside the range. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this BPM segment crosses the end of the range, split it into two.
|
|
|
|
|
if( iStartIndexThisSegment < iEndIndex && iStartIndexNextSegment > iEndIndex )
|
|
|
|
|
{
|
2011-07-14 22:19:32 -04:00
|
|
|
BPMSegment * b = new BPMSegment(iEndIndex,
|
|
|
|
|
bs->GetBPS());
|
|
|
|
|
bpms.insert(bpms.begin()+i+1, b);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
else if( iStartIndexNextSegment > iEndIndex )
|
|
|
|
|
continue;
|
|
|
|
|
|
2011-07-14 22:19:32 -04:00
|
|
|
bs->SetBPM(bs->GetBPM() * fFactor);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
bool TimingData::IsWarpAtRow( int iNoteRow ) const
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &warps = GetTimingSegments(SEGMENT_WARP);
|
2011-07-14 22:19:32 -04:00
|
|
|
if( warps.empty() )
|
2011-03-25 23:40:03 +07:00
|
|
|
return false;
|
2011-09-10 03:08:59 +00:00
|
|
|
|
2011-07-14 21:51:09 -04:00
|
|
|
int i = GetSegmentIndexAtRow( SEGMENT_WARP, iNoteRow );
|
2011-09-11 17:13:10 +00:00
|
|
|
const WarpSegment *s = ToWarp(warps[i]);
|
2011-05-31 10:59:18 -04:00
|
|
|
float beatRow = NoteRowToBeat(iNoteRow);
|
2011-07-14 22:19:32 -04:00
|
|
|
if( s->GetBeat() <= beatRow && beatRow < (s->GetBeat() + s->GetLength() ) )
|
2011-03-26 22:07:24 +07:00
|
|
|
{
|
2011-05-24 13:07:59 +07:00
|
|
|
// Allow stops inside warps to allow things like stop, warp, stop, warp, stop, and so on.
|
2011-09-15 03:28:58 +00:00
|
|
|
if( GetTimingSegments(SEGMENT_STOP).empty() && GetTimingSegments(SEGMENT_DELAY).empty() )
|
2011-03-26 22:07:24 +07:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-03-27 15:56:40 +07:00
|
|
|
if( GetStopAtRow(iNoteRow) != 0.0f || GetDelayAtRow(iNoteRow) != 0.0f )
|
2011-03-26 22:07:24 +07:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2011-03-25 23:19:31 +07:00
|
|
|
}
|
|
|
|
|
|
2011-05-16 02:00:17 -04:00
|
|
|
bool TimingData::IsFakeAtRow( int iNoteRow ) const
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &fakes = GetTimingSegments(SEGMENT_FAKE);
|
2011-07-14 22:19:32 -04:00
|
|
|
if( fakes.empty() )
|
2011-05-16 02:00:17 -04:00
|
|
|
return false;
|
2011-09-10 03:08:59 +00:00
|
|
|
|
2011-07-14 21:51:09 -04:00
|
|
|
int i = GetSegmentIndexAtRow( SEGMENT_FAKE, iNoteRow );
|
2011-09-11 17:13:10 +00:00
|
|
|
const FakeSegment *s = ToFake(fakes[i]);
|
2011-05-31 00:51:25 -04:00
|
|
|
float beatRow = NoteRowToBeat(iNoteRow);
|
2011-07-14 22:19:32 -04:00
|
|
|
if( s->GetBeat() <= beatRow && beatRow < ( s->GetBeat() + s->GetLength() ) )
|
2011-05-16 02:00:17 -04:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
/* DummySegments: since our model relies on being able to get a segment at will,
|
|
|
|
|
* whether one exists or not, we have a bunch of dummies to return if there is
|
|
|
|
|
* no segment. It's kind of kludgy, but when we have functions making
|
|
|
|
|
* indiscriminate calls to get segments at arbitrary rows, I think it's the
|
|
|
|
|
* best solution we've got for now.
|
|
|
|
|
*
|
|
|
|
|
* Note that types whose SegmentEffectAreas are "Indefinite" are NULL here,
|
|
|
|
|
* because they should never need to be used; we always have at least one such
|
|
|
|
|
* segment in the TimingData, and if not, we'll crash anyway. -- vyhd */
|
|
|
|
|
static const TimingSegment* DummySegments[NUM_TimingSegmentType] =
|
|
|
|
|
{
|
|
|
|
|
NULL, // BPMSegment
|
|
|
|
|
new StopSegment,
|
|
|
|
|
new DelaySegment,
|
|
|
|
|
NULL, // TimeSignatureSegment
|
|
|
|
|
new WarpSegment,
|
|
|
|
|
NULL, // LabelSegment
|
|
|
|
|
NULL, // TickcountSegment
|
|
|
|
|
NULL, // ComboSegment
|
|
|
|
|
NULL, // SpeedSegment
|
|
|
|
|
NULL, // ScrollSegment
|
|
|
|
|
new FakeSegment
|
2011-09-10 03:08:59 +00:00
|
|
|
};
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
const TimingSegment* TimingData::GetSegmentAtRow( int iNoteRow, TimingSegmentType tst ) const
|
2011-03-25 00:54:33 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment*> &vSegments = GetTimingSegments(tst);
|
2011-03-25 00:54:33 -04:00
|
|
|
|
2011-09-10 03:08:59 +00:00
|
|
|
if( vSegments.empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
return DummySegments[tst];
|
2011-05-15 13:48:10 -04:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
int index = GetSegmentIndexAtRow( tst, iNoteRow );
|
|
|
|
|
const TimingSegment *seg = vSegments[index];
|
2011-09-10 03:08:59 +00:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
switch( seg->GetEffectType() )
|
|
|
|
|
{
|
|
|
|
|
case SegmentEffectType_Indefinite:
|
|
|
|
|
{
|
|
|
|
|
// this segment is in effect at this row
|
|
|
|
|
return seg;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
// if the returned segment isn't exactly on this row,
|
|
|
|
|
// we don't want it, return a dummy instead
|
|
|
|
|
if( seg->GetRow() == iNoteRow )
|
|
|
|
|
return seg;
|
|
|
|
|
else
|
|
|
|
|
return DummySegments[tst];
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-10 03:08:59 +00:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
ASSERT( 0 );
|
2011-05-25 22:44:41 +07:00
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
TimingSegment* GetSegmentAtRow( int iNoteRow, TimingSegmentType tst )
|
2011-03-25 00:54:33 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
return const_cast<TimingSegment*>( GetSegmentAtRow(iNoteRow, tst) );
|
2011-03-25 00:54:33 -04:00
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
static void EraseSegment( vector<TimingSegment*> &vSegs, int index, TimingSegment *cur )
|
2011-03-25 00:54:33 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
LOG->Trace( "EraseSegment(%d, %p)", index, cur );
|
|
|
|
|
cur->DebugPrint();
|
|
|
|
|
|
|
|
|
|
vSegs.erase( vSegs.begin() + index );
|
|
|
|
|
SAFE_DELETE( cur );
|
2011-03-25 00:54:33 -04:00
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
// NOTE: the pointer we're passed is a reference to a temporary,
|
|
|
|
|
// so we must deep-copy it (with ::Copy) for new allocations.
|
|
|
|
|
void TimingData::AddSegment( const TimingSegment *seg )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
LOG->Trace( "AddSegment( %s )", TimingSegmentTypeToString(seg->GetType()).c_str() );
|
|
|
|
|
seg->DebugPrint();
|
|
|
|
|
|
|
|
|
|
TimingSegmentType tst = seg->GetType();
|
|
|
|
|
vector<TimingSegment*> &vSegs = m_avpTimingSegments[tst];
|
|
|
|
|
|
|
|
|
|
// OPTIMIZATION: if this is our first segment, push and return.
|
|
|
|
|
if( vSegs.empty() )
|
|
|
|
|
{
|
|
|
|
|
vSegs.push_back( seg->Copy() );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int index = GetSegmentIndexAtRow( tst, seg->GetRow() );
|
|
|
|
|
ASSERT( index != INVALID_INDEX );
|
|
|
|
|
TimingSegment *cur = vSegs[index];
|
|
|
|
|
|
|
|
|
|
bool bIsNotable = seg->IsNotable();
|
|
|
|
|
bool bOnSameRow = seg->GetRow() == cur->GetRow();
|
|
|
|
|
|
|
|
|
|
// ignore changes that are zero and don't overwrite an existing segment
|
|
|
|
|
if( !bIsNotable && !bOnSameRow )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch( seg->GetEffectType() )
|
|
|
|
|
{
|
|
|
|
|
case SegmentEffectType_Row:
|
|
|
|
|
case SegmentEffectType_Range:
|
|
|
|
|
{
|
|
|
|
|
// if we're overwriting a change with a non-notable
|
|
|
|
|
// one, take it to mean deleting the existing segment
|
|
|
|
|
if( bOnSameRow && !bIsNotable )
|
|
|
|
|
{
|
|
|
|
|
EraseSegment( vSegs, index, cur );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SegmentEffectType_Indefinite:
|
|
|
|
|
{
|
|
|
|
|
TimingSegment *prev = cur;
|
|
|
|
|
|
|
|
|
|
// get the segment before last; if we're on the same
|
|
|
|
|
// row, get the segment in effect before 'cur'
|
|
|
|
|
if( bOnSameRow && index > 0 )
|
|
|
|
|
prev = vSegs[index - 1];
|
|
|
|
|
|
|
|
|
|
// if true, this is redundant segment change
|
2011-09-16 21:15:47 +07:00
|
|
|
if( (*prev) == (*seg) )
|
2011-09-15 03:28:58 +00:00
|
|
|
{
|
2011-09-16 21:15:47 +07:00
|
|
|
if( prev != cur )
|
|
|
|
|
EraseSegment( vSegs, index, cur );
|
2011-09-15 03:28:58 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the segment at or before this row is equal to the new one; ignore it
|
|
|
|
|
if( bOnSameRow && (*cur) == (*seg) )
|
|
|
|
|
{
|
|
|
|
|
LOG->Trace( "equals previous segment, ignoring" );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy() the segment (which allocates a new segment), assign it
|
|
|
|
|
// to the position of the old one, then delete the old pointer.
|
|
|
|
|
TimingSegment *cpy = seg->Copy();
|
|
|
|
|
|
|
|
|
|
if( bOnSameRow )
|
|
|
|
|
{
|
|
|
|
|
// delete the existing pointer and replace it
|
|
|
|
|
SAFE_DELETE( cur );
|
|
|
|
|
vSegs[index] = cpy;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// copy and insert a new segment
|
|
|
|
|
vector<TimingSegment*>::iterator it;
|
|
|
|
|
it = upper_bound( vSegs.begin(), vSegs.end(), cpy, ts_less() );
|
|
|
|
|
vSegs.insert( it, cpy );
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
bool TimingData::DoesLabelExist( const RString& sLabel ) const
|
2011-04-05 14:18:29 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &labels = GetTimingSegments(SEGMENT_LABEL);
|
2011-07-14 23:15:48 -04:00
|
|
|
for (unsigned i = 0; i < labels.size(); i++)
|
2011-04-05 14:18:29 -04:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
if (ToLabel(labels[i])->GetLabel() == sLabel)
|
2011-04-05 14:18:29 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-17 01:47:30 -04:00
|
|
|
void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpLengthOut ) const
|
|
|
|
|
{
|
|
|
|
|
fElapsedTime += PREFSMAN->m_fGlobalOffsetSeconds;
|
|
|
|
|
|
|
|
|
|
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut, iWarpBeginOut, fWarpLengthOut );
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
enum
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-03-25 23:19:31 +07:00
|
|
|
FOUND_WARP,
|
|
|
|
|
FOUND_WARP_DESTINATION,
|
|
|
|
|
FOUND_BPM_CHANGE,
|
|
|
|
|
FOUND_STOP,
|
2011-07-27 23:43:52 -04:00
|
|
|
FOUND_DELAY,
|
2011-08-15 18:12:12 -04:00
|
|
|
FOUND_STOP_DELAY, // we have these two on the same row.
|
2011-03-25 23:19:31 +07:00
|
|
|
FOUND_MARKER,
|
|
|
|
|
NOT_FOUND
|
|
|
|
|
};
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, float &fWarpDestinationOut ) const
|
|
|
|
|
{
|
2011-09-10 03:08:59 +00:00
|
|
|
const vector<TimingSegment *> * segs = m_avpTimingSegments;
|
2011-07-15 00:54:11 -04:00
|
|
|
vector<TimingSegment *>::const_iterator itBPMS = segs[SEGMENT_BPM].begin();
|
|
|
|
|
vector<TimingSegment *>::const_iterator itWS = segs[SEGMENT_WARP].begin();
|
2011-07-27 23:43:52 -04:00
|
|
|
vector<TimingSegment *>::const_iterator itSS = segs[SEGMENT_STOP].begin();
|
|
|
|
|
vector<TimingSegment *>::const_iterator itDS = segs[SEGMENT_DELAY].begin();
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
bFreezeOut = false;
|
|
|
|
|
bDelayOut = false;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
iWarpBeginOut = -1;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
int iLastRow = 0;
|
|
|
|
|
float fLastTime = -m_fBeat0OffsetInSeconds;
|
2011-06-12 03:37:10 -04:00
|
|
|
float fBPS = GetBPMAtRow(0) / 60.0f;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
float bIsWarping = false;
|
2011-06-12 03:37:10 -04:00
|
|
|
float fWarpDestination = 0;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-25 23:19:31 +07:00
|
|
|
for( ;; )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-03-25 23:19:31 +07:00
|
|
|
int iEventRow = INT_MAX;
|
|
|
|
|
int iEventType = NOT_FOUND;
|
|
|
|
|
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-03-25 23:19:31 +07:00
|
|
|
iEventRow = BeatToNoteRow(fWarpDestination);
|
|
|
|
|
iEventType = FOUND_WARP_DESTINATION;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-07-15 00:54:11 -04:00
|
|
|
if (itBPMS != segs[SEGMENT_BPM].end() &&
|
|
|
|
|
(*itBPMS)->GetRow() < iEventRow )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-07-15 00:54:11 -04:00
|
|
|
iEventRow = (*itBPMS)->GetRow();
|
2011-03-25 23:19:31 +07:00
|
|
|
iEventType = FOUND_BPM_CHANGE;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-07-27 23:43:52 -04:00
|
|
|
if (itDS != segs[SEGMENT_DELAY].end() &&
|
|
|
|
|
(*itDS)->GetRow() < iEventRow)
|
|
|
|
|
{
|
|
|
|
|
iEventRow = (*itDS)->GetRow();
|
|
|
|
|
iEventType = FOUND_DELAY;
|
|
|
|
|
}
|
|
|
|
|
if (itSS != segs[SEGMENT_STOP].end() &&
|
2011-08-15 17:54:06 -04:00
|
|
|
(*itSS)->GetRow() < iEventRow ) // && iEventType != FOUND_DELAY )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-08-15 18:12:12 -04:00
|
|
|
int tmpRow = iEventRow;
|
2011-07-15 00:54:11 -04:00
|
|
|
iEventRow = (*itSS)->GetRow();
|
2011-08-15 18:12:12 -04:00
|
|
|
iEventType = (tmpRow == iEventRow) ? FOUND_STOP_DELAY : FOUND_STOP;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-07-15 00:54:11 -04:00
|
|
|
if (itWS != segs[SEGMENT_WARP].end() &&
|
|
|
|
|
(*itWS)->GetRow() < iEventRow )
|
2011-03-26 22:07:24 +07:00
|
|
|
{
|
2011-07-15 00:54:11 -04:00
|
|
|
iEventRow = (*itWS)->GetRow();
|
2011-03-26 22:07:24 +07:00
|
|
|
iEventType = FOUND_WARP;
|
|
|
|
|
}
|
2011-03-25 23:19:31 +07:00
|
|
|
if( iEventType == NOT_FOUND )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-03-25 23:19:31 +07:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
|
|
|
|
|
float fNextEventTime = fLastTime + fTimeToNextEvent;
|
|
|
|
|
if ( fElapsedTime < fNextEventTime )
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fLastTime = fNextEventTime;
|
|
|
|
|
switch( iEventType )
|
|
|
|
|
{
|
2011-07-27 23:43:52 -04:00
|
|
|
case FOUND_WARP_DESTINATION:
|
|
|
|
|
bIsWarping = false;
|
|
|
|
|
break;
|
|
|
|
|
case FOUND_BPM_CHANGE:
|
2011-09-11 17:13:10 +00:00
|
|
|
fBPS = ToBPM(*itBPMS)->GetBPS();
|
2011-07-27 23:43:52 -04:00
|
|
|
itBPMS ++;
|
|
|
|
|
break;
|
|
|
|
|
case FOUND_DELAY:
|
2011-08-15 19:23:43 -04:00
|
|
|
case FOUND_STOP_DELAY:
|
2011-07-27 23:43:52 -04:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
const DelaySegment *ss = ToDelay(*itDS);
|
2011-07-27 23:43:52 -04:00
|
|
|
fTimeToNextEvent = ss->GetPause();
|
|
|
|
|
fNextEventTime = fLastTime + fTimeToNextEvent;
|
|
|
|
|
if ( fElapsedTime < fNextEventTime )
|
|
|
|
|
{
|
|
|
|
|
bFreezeOut = false;
|
|
|
|
|
bDelayOut = true;
|
|
|
|
|
fBeatOut = ss->GetBeat();
|
|
|
|
|
fBPSOut = fBPS;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fLastTime = fNextEventTime;
|
|
|
|
|
itDS ++;
|
2011-08-15 19:23:43 -04:00
|
|
|
if (iEventType == FOUND_DELAY)
|
|
|
|
|
break;
|
2011-07-27 23:43:52 -04:00
|
|
|
}
|
|
|
|
|
case FOUND_STOP:
|
2011-03-25 23:19:31 +07:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
const StopSegment *ss = ToStop(*itSS);
|
2011-07-15 00:54:11 -04:00
|
|
|
fTimeToNextEvent = ss->GetPause();
|
2011-03-26 11:59:48 -05:00
|
|
|
fNextEventTime = fLastTime + fTimeToNextEvent;
|
|
|
|
|
if ( fElapsedTime < fNextEventTime )
|
|
|
|
|
{
|
2011-07-27 23:43:52 -04:00
|
|
|
bFreezeOut = true;
|
|
|
|
|
bDelayOut = false;
|
2011-07-15 00:54:11 -04:00
|
|
|
fBeatOut = ss->GetBeat();
|
2011-03-26 11:59:48 -05:00
|
|
|
fBPSOut = fBPS;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fLastTime = fNextEventTime;
|
|
|
|
|
itSS ++;
|
2011-08-15 18:12:12 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2011-07-27 23:43:52 -04:00
|
|
|
case FOUND_WARP:
|
2011-03-26 22:07:24 +07:00
|
|
|
{
|
2011-05-16 10:24:59 -04:00
|
|
|
bIsWarping = true;
|
2011-09-11 17:13:10 +00:00
|
|
|
const WarpSegment *ws = ToWarp(*itWS);
|
2011-07-15 00:54:11 -04:00
|
|
|
float fWarpSum = ws->GetLength() + ws->GetBeat();
|
2011-05-16 10:24:59 -04:00
|
|
|
if( fWarpSum > fWarpDestination )
|
|
|
|
|
{
|
2011-05-16 22:05:23 +07:00
|
|
|
fWarpDestination = fWarpSum;
|
2011-05-16 10:24:59 -04:00
|
|
|
}
|
|
|
|
|
iWarpBeginOut = iEventRow;
|
|
|
|
|
fWarpDestinationOut = fWarpDestination;
|
|
|
|
|
itWS ++;
|
|
|
|
|
break;
|
2011-03-26 22:07:24 +07:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-03-25 23:19:31 +07:00
|
|
|
iLastRow = iEventRow;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-03-25 23:19:31 +07:00
|
|
|
|
|
|
|
|
fBeatOut = NoteRowToBeat( iLastRow ) + (fElapsedTime - fLastTime) * fBPS;
|
|
|
|
|
fBPSOut = fBPS;
|
|
|
|
|
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
|
|
|
|
|
{
|
|
|
|
|
return TimingData::GetElapsedTimeFromBeatNoOffset( fBeat ) - PREFSMAN->m_fGlobalOffsetSeconds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
|
|
|
|
|
{
|
2011-09-10 03:08:59 +00:00
|
|
|
const vector<TimingSegment *> * segs = m_avpTimingSegments;
|
2011-07-15 00:54:11 -04:00
|
|
|
vector<TimingSegment *>::const_iterator itBPMS = segs[SEGMENT_BPM].begin();
|
|
|
|
|
vector<TimingSegment *>::const_iterator itWS = segs[SEGMENT_WARP].begin();
|
2011-07-27 23:43:52 -04:00
|
|
|
vector<TimingSegment *>::const_iterator itSS = segs[SEGMENT_STOP].begin();
|
|
|
|
|
vector<TimingSegment *>::const_iterator itDS = segs[SEGMENT_DELAY].begin();
|
2011-03-25 23:19:31 +07:00
|
|
|
|
|
|
|
|
int iLastRow = 0;
|
|
|
|
|
float fLastTime = -m_fBeat0OffsetInSeconds;
|
2011-06-12 03:37:10 -04:00
|
|
|
float fBPS = GetBPMAtRow(0) / 60.0f;
|
2011-03-25 23:19:31 +07:00
|
|
|
|
|
|
|
|
float bIsWarping = false;
|
2011-06-12 03:37:10 -04:00
|
|
|
float fWarpDestination = 0;
|
2011-03-25 23:19:31 +07:00
|
|
|
|
|
|
|
|
for( ;; )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-03-25 23:19:31 +07:00
|
|
|
int iEventRow = INT_MAX;
|
|
|
|
|
int iEventType = NOT_FOUND;
|
|
|
|
|
if( bIsWarping && BeatToNoteRow(fWarpDestination) < iEventRow )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-03-25 23:19:31 +07:00
|
|
|
iEventRow = BeatToNoteRow(fWarpDestination);
|
|
|
|
|
iEventType = FOUND_WARP_DESTINATION;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-07-15 00:54:11 -04:00
|
|
|
if (itBPMS != segs[SEGMENT_BPM].end() &&
|
|
|
|
|
(*itBPMS)->GetRow() < iEventRow )
|
2011-03-25 23:19:31 +07:00
|
|
|
{
|
2011-07-15 00:54:11 -04:00
|
|
|
iEventRow = (*itBPMS)->GetRow();
|
2011-03-25 23:19:31 +07:00
|
|
|
iEventType = FOUND_BPM_CHANGE;
|
|
|
|
|
}
|
2011-07-27 23:43:52 -04:00
|
|
|
if (itDS != segs[SEGMENT_DELAY].end() &&
|
|
|
|
|
(*itDS)->GetRow() < iEventRow ) // delays (come before marker)
|
2011-03-25 23:19:31 +07:00
|
|
|
{
|
2011-07-27 23:43:52 -04:00
|
|
|
iEventRow = (*itDS)->GetRow();
|
|
|
|
|
iEventType = FOUND_DELAY;
|
2011-03-25 23:19:31 +07:00
|
|
|
}
|
|
|
|
|
if( BeatToNoteRow(fBeat) < iEventRow )
|
|
|
|
|
{
|
|
|
|
|
iEventRow = BeatToNoteRow(fBeat);
|
|
|
|
|
iEventType = FOUND_MARKER;
|
|
|
|
|
}
|
2011-07-27 23:43:52 -04:00
|
|
|
if (itSS != segs[SEGMENT_STOP].end() &&
|
2011-07-15 00:54:11 -04:00
|
|
|
(*itSS)->GetRow() < iEventRow ) // stops (come after marker)
|
2011-03-25 23:19:31 +07:00
|
|
|
{
|
2011-07-15 00:54:11 -04:00
|
|
|
iEventRow = (*itSS)->GetRow();
|
2011-03-25 23:19:31 +07:00
|
|
|
iEventType = FOUND_STOP;
|
|
|
|
|
}
|
2011-07-15 00:54:11 -04:00
|
|
|
if (itWS != segs[SEGMENT_WARP].end() &&
|
|
|
|
|
(*itWS)->GetRow() < iEventRow )
|
2011-03-26 22:07:24 +07:00
|
|
|
{
|
2011-07-15 00:54:11 -04:00
|
|
|
iEventRow = (*itWS)->GetRow();
|
2011-03-26 22:07:24 +07:00
|
|
|
iEventType = FOUND_WARP;
|
|
|
|
|
}
|
2011-03-25 23:19:31 +07:00
|
|
|
float fTimeToNextEvent = bIsWarping ? 0 : NoteRowToBeat( iEventRow - iLastRow ) / fBPS;
|
|
|
|
|
float fNextEventTime = fLastTime + fTimeToNextEvent;
|
|
|
|
|
fLastTime = fNextEventTime;
|
|
|
|
|
switch( iEventType )
|
|
|
|
|
{
|
|
|
|
|
case FOUND_WARP_DESTINATION:
|
|
|
|
|
bIsWarping = false;
|
|
|
|
|
break;
|
|
|
|
|
case FOUND_BPM_CHANGE:
|
2011-09-11 17:13:10 +00:00
|
|
|
fBPS = ToBPM(*itBPMS)->GetBPS();
|
2011-03-25 23:19:31 +07:00
|
|
|
itBPMS ++;
|
|
|
|
|
break;
|
|
|
|
|
case FOUND_STOP:
|
2011-09-11 17:13:10 +00:00
|
|
|
fTimeToNextEvent = ToStop(*itSS)->GetPause();
|
2011-03-25 23:19:31 +07:00
|
|
|
fNextEventTime = fLastTime + fTimeToNextEvent;
|
|
|
|
|
fLastTime = fNextEventTime;
|
|
|
|
|
itSS ++;
|
|
|
|
|
break;
|
2011-07-27 23:43:52 -04:00
|
|
|
case FOUND_DELAY:
|
2011-09-11 17:13:10 +00:00
|
|
|
fTimeToNextEvent = ToDelay(*itDS)->GetPause();
|
2011-07-27 23:43:52 -04:00
|
|
|
fNextEventTime = fLastTime + fTimeToNextEvent;
|
|
|
|
|
fLastTime = fNextEventTime;
|
|
|
|
|
itDS ++;
|
|
|
|
|
break;
|
2011-03-25 23:19:31 +07:00
|
|
|
case FOUND_MARKER:
|
|
|
|
|
return fLastTime;
|
2011-03-26 22:07:24 +07:00
|
|
|
case FOUND_WARP:
|
|
|
|
|
{
|
2011-05-16 10:24:59 -04:00
|
|
|
bIsWarping = true;
|
2011-09-11 17:13:10 +00:00
|
|
|
WarpSegment *ws = ToWarp(*itWS);
|
2011-07-15 00:54:11 -04:00
|
|
|
float fWarpSum = ws->GetLength() + ws->GetBeat();
|
2011-05-16 10:24:59 -04:00
|
|
|
if( fWarpSum > fWarpDestination )
|
|
|
|
|
{
|
2011-05-16 22:05:23 +07:00
|
|
|
fWarpDestination = fWarpSum;
|
2011-05-16 10:24:59 -04:00
|
|
|
}
|
|
|
|
|
itWS ++;
|
|
|
|
|
break;
|
2011-03-26 22:07:24 +07:00
|
|
|
}
|
2011-03-25 23:19:31 +07:00
|
|
|
}
|
|
|
|
|
iLastRow = iEventRow;
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
2011-03-25 23:19:31 +07:00
|
|
|
|
|
|
|
|
// won't reach here, unless BeatToNoteRow(fBeat == INT_MAX) (impossible)
|
|
|
|
|
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
float TimingData::GetDisplayedBeat( float fBeat ) const
|
|
|
|
|
{
|
2011-06-12 03:37:10 -04:00
|
|
|
float fOutBeat = 0;
|
2011-07-27 00:29:22 +07:00
|
|
|
unsigned i;
|
2011-09-10 03:08:59 +00:00
|
|
|
const vector<TimingSegment *> &scrolls = m_avpTimingSegments[SEGMENT_SCROLL];
|
2011-07-27 00:29:22 +07:00
|
|
|
for( i=0; i<scrolls.size()-1; i++ )
|
2011-05-25 22:44:41 +07:00
|
|
|
{
|
2011-07-27 00:29:22 +07:00
|
|
|
if( scrolls[i+1]->GetBeat() > fBeat )
|
2011-06-04 19:02:45 +07:00
|
|
|
break;
|
2011-09-11 17:13:10 +00:00
|
|
|
fOutBeat += (scrolls[i+1]->GetBeat() - scrolls[i]->GetBeat()) * ToScroll(scrolls[i])->GetRatio();
|
2011-05-25 22:44:41 +07:00
|
|
|
}
|
2011-09-11 17:13:10 +00:00
|
|
|
fOutBeat += (fBeat - scrolls[i]->GetBeat()) * ToScroll(scrolls[i])->GetRatio();
|
2011-05-25 22:44:41 +07:00
|
|
|
return fOutBeat;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-19 18:58:44 +07:00
|
|
|
void TimingData::ScaleRegion( float fScale, int iStartIndex, int iEndIndex, bool bAdjustBPM )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
ASSERT( fScale > 0 );
|
|
|
|
|
ASSERT( iStartIndex >= 0 );
|
|
|
|
|
ASSERT( iStartIndex < iEndIndex );
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-06-26 13:43:30 +07:00
|
|
|
int length = iEndIndex - iStartIndex;
|
|
|
|
|
int newLength = lrintf( fScale * length );
|
2011-09-15 03:28:58 +00:00
|
|
|
|
|
|
|
|
FOREACH_TimingSegmentType( tst )
|
|
|
|
|
for (unsigned j = 0; j < m_avpTimingSegments[tst].size(); j++)
|
|
|
|
|
m_avpTimingSegments[tst][j]->Scale(iStartIndex, length, newLength);
|
|
|
|
|
|
2011-03-19 18:58:44 +07:00
|
|
|
// adjust BPM changes to preserve timing
|
|
|
|
|
if( bAdjustBPM )
|
|
|
|
|
{
|
2011-06-26 13:43:30 +07:00
|
|
|
int iNewEndIndex = iStartIndex + newLength;
|
2011-03-19 18:58:44 +07:00
|
|
|
float fEndBPMBeforeScaling = GetBPMAtRow(iNewEndIndex);
|
2011-09-10 03:08:59 +00:00
|
|
|
vector<TimingSegment *> &bpms = m_avpTimingSegments[SEGMENT_BPM];
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-19 18:58:44 +07:00
|
|
|
// adjust BPM changes "between" iStartIndex and iNewEndIndex
|
2011-07-15 00:54:11 -04:00
|
|
|
for ( unsigned i = 0; i < bpms.size(); i++ )
|
2011-03-19 18:58:44 +07:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
BPMSegment *bpm = ToBPM(bpms[i]);
|
2011-07-15 00:54:11 -04:00
|
|
|
const int iSegStart = bpm->GetRow();
|
2011-03-19 18:58:44 +07:00
|
|
|
if( iSegStart <= iStartIndex )
|
|
|
|
|
continue;
|
|
|
|
|
else if( iSegStart >= iNewEndIndex )
|
|
|
|
|
continue;
|
|
|
|
|
else
|
2011-07-15 00:54:11 -04:00
|
|
|
bpm->SetBPM( bpm->GetBPM() * fScale );
|
2011-03-19 18:58:44 +07:00
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-19 18:58:44 +07:00
|
|
|
// set BPM at iStartIndex and iNewEndIndex.
|
|
|
|
|
SetBPMAtRow( iStartIndex, GetBPMAtRow(iStartIndex) * fScale );
|
|
|
|
|
SetBPMAtRow( iNewEndIndex, fEndBPMBeforeScaling );
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TimingData::InsertRows( int iStartRow, int iRowsToAdd )
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
FOREACH_TimingSegmentType( tst )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
vector<TimingSegment *> &segs = m_avpTimingSegments[tst];
|
2011-07-14 15:22:32 -04:00
|
|
|
for (unsigned j = 0; j < segs.size(); j++)
|
|
|
|
|
{
|
|
|
|
|
TimingSegment *seg = segs[j];
|
|
|
|
|
if (seg->GetRow() < iStartRow)
|
|
|
|
|
continue;
|
|
|
|
|
seg->SetRow(seg->GetRow() + iRowsToAdd);
|
|
|
|
|
}
|
2011-05-26 17:38:31 -04:00
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
if( iStartRow == 0 )
|
|
|
|
|
{
|
|
|
|
|
/* If we're shifting up at the beginning, we just shifted up the first
|
|
|
|
|
* BPMSegment. That segment must always begin at 0. */
|
2011-09-10 03:08:59 +00:00
|
|
|
vector<TimingSegment *> &bpms = m_avpTimingSegments[SEGMENT_BPM];
|
2011-07-14 15:22:32 -04:00
|
|
|
ASSERT_M( bpms.size() > 0, "There must be at least one BPM Segment in the chart!" );
|
|
|
|
|
bpms[0]->SetRow(0);
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete BPMChanges and StopSegments in [iStartRow,iRowsToDelete), and shift down.
|
|
|
|
|
void TimingData::DeleteRows( int iStartRow, int iRowsToDelete )
|
|
|
|
|
{
|
|
|
|
|
/* Remember the BPM at the end of the region being deleted. */
|
2011-09-10 03:08:59 +00:00
|
|
|
float fNewBPM = GetBPMAtBeat( NoteRowToBeat(iStartRow+iRowsToDelete) );
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
/* We're moving rows up. Delete any BPM changes and stops in the region
|
|
|
|
|
* being deleted. */
|
2011-09-15 03:28:58 +00:00
|
|
|
FOREACH_TimingSegmentType( tst )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
vector<TimingSegment *> &segs = m_avpTimingSegments[tst];
|
2011-07-14 15:22:32 -04:00
|
|
|
for (unsigned j = 0; j < segs.size(); j++)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-07-14 15:22:32 -04:00
|
|
|
TimingSegment *seg = segs[j];
|
|
|
|
|
// Before deleted region:
|
|
|
|
|
if (seg->GetRow() < iStartRow)
|
|
|
|
|
continue;
|
|
|
|
|
// Inside deleted region:
|
|
|
|
|
if (seg->GetRow() < iStartRow + iRowsToDelete)
|
|
|
|
|
{
|
|
|
|
|
segs.erase(segs.begin()+j, segs.begin()+j+1);
|
|
|
|
|
--j;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-07-14 15:22:32 -04:00
|
|
|
// After deleted regions:
|
|
|
|
|
seg->SetRow(seg->GetRow() - iRowsToDelete);
|
2011-05-16 02:00:17 -04:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-09-10 03:08:59 +00:00
|
|
|
SetBPMAtRow( iStartRow, fNewBPM );
|
2011-03-17 01:47:30 -04:00
|
|
|
}
|
|
|
|
|
|
2011-05-24 12:29:55 +07:00
|
|
|
float TimingData::GetDisplayedSpeedPercent( float fSongBeat, float fMusicSeconds ) const
|
2011-05-24 11:43:08 +07:00
|
|
|
{
|
2011-06-01 14:32:59 -04:00
|
|
|
/* HACK: Somehow we get called into this function when there is no
|
|
|
|
|
* TimingData to work with. This seems to happen the most upon
|
|
|
|
|
* leaving the editor. Still, cover our butts in case this instance
|
|
|
|
|
* isn't existing. */
|
2011-09-15 03:28:58 +00:00
|
|
|
/* ...but force a crash, so debuggers will catch it and stop here.
|
|
|
|
|
* That'll make us keep this bug in mind. -- vyhd */
|
|
|
|
|
if( !this )
|
|
|
|
|
{
|
|
|
|
|
DEBUG_ASSERT( this );
|
|
|
|
|
return 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const vector<TimingSegment *> &speeds = GetTimingSegments(SEGMENT_SPEED);
|
2011-07-15 00:54:11 -04:00
|
|
|
if( speeds.size() == 0 )
|
2011-06-01 14:32:59 -04:00
|
|
|
return 1.0f;
|
2011-05-24 11:43:08 +07:00
|
|
|
|
2011-07-14 21:51:09 -04:00
|
|
|
const int index = GetSegmentIndexAtBeat( SEGMENT_SPEED, fSongBeat );
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-09-11 17:13:10 +00:00
|
|
|
const SpeedSegment *seg = ToSpeed(speeds[index]);
|
2011-07-15 00:54:11 -04:00
|
|
|
float fStartBeat = seg->GetBeat();
|
2011-05-24 11:43:08 +07:00
|
|
|
float fStartTime = GetElapsedTimeFromBeat( fStartBeat ) - GetDelayAtBeat( fStartBeat );
|
|
|
|
|
float fEndTime;
|
|
|
|
|
float fCurTime = fMusicSeconds;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
|
|
|
|
if( seg->GetUnit() == SpeedSegment::UNIT_SECONDS )
|
2011-05-24 11:43:08 +07:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
fEndTime = fStartTime + seg->GetDelay();
|
2011-05-24 11:43:08 +07:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
fEndTime = GetElapsedTimeFromBeat( fStartBeat + seg->GetDelay() )
|
|
|
|
|
- GetDelayAtBeat( fStartBeat + seg->GetDelay() );
|
2011-05-24 11:43:08 +07:00
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-09-11 17:13:10 +00:00
|
|
|
SpeedSegment *first = ToSpeed(speeds[0]);
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-09-11 17:13:10 +00:00
|
|
|
if( ( index == 0 && first->GetDelay() > 0.0 ) && fCurTime < fStartTime )
|
2011-05-24 11:43:08 +07:00
|
|
|
{
|
2011-06-12 03:37:10 -04:00
|
|
|
return 1.0f;
|
2011-05-24 11:43:08 +07:00
|
|
|
}
|
2011-09-11 17:13:10 +00:00
|
|
|
else if( fEndTime >= fCurTime && ( index > 0 || first->GetDelay() > 0.0 ) )
|
2011-05-24 11:43:08 +07:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const float fPriorSpeed = (index == 0) ? 1 :
|
|
|
|
|
ToSpeed(speeds[index-1])->GetRatio();
|
|
|
|
|
|
2011-05-24 11:43:08 +07:00
|
|
|
float fTimeUsed = fCurTime - fStartTime;
|
|
|
|
|
float fDuration = fEndTime - fStartTime;
|
|
|
|
|
float fRatioUsed = fDuration == 0.0 ? 1 : fTimeUsed / fDuration;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-07-15 00:54:11 -04:00
|
|
|
float fDistance = fPriorSpeed - seg->GetRatio();
|
2011-05-24 11:43:08 +07:00
|
|
|
float fRatioNeed = fRatioUsed * -fDistance;
|
|
|
|
|
return (fPriorSpeed + fRatioNeed);
|
|
|
|
|
}
|
2011-09-15 03:28:58 +00:00
|
|
|
else
|
2011-05-24 11:43:08 +07:00
|
|
|
{
|
2011-07-15 00:54:11 -04:00
|
|
|
return seg->GetRatio();
|
2011-05-24 11:43:08 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-10 16:00:47 +07:00
|
|
|
void TimingData::TidyUpData()
|
|
|
|
|
{
|
|
|
|
|
// If there are no BPM segments, provide a default.
|
2011-09-10 03:08:59 +00:00
|
|
|
vector<TimingSegment *> *segs = m_avpTimingSegments;
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_BPM].empty() )
|
2011-05-10 16:00:47 +07:00
|
|
|
{
|
|
|
|
|
LOG->UserLog( "Song file", m_sFile, "has no BPM segments, default provided." );
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( BPMSegment(0, 60) );
|
2011-05-10 16:00:47 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure the first BPM segment starts at beat 0.
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_BPM][0]->GetRow() != 0 )
|
|
|
|
|
segs[SEGMENT_BPM][0]->SetRow(0);
|
2011-05-10 16:00:47 +07:00
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
// If no time signature specified, assume default time for the whole song.
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_TIME_SIG].empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( TimeSignatureSegment(0) );
|
|
|
|
|
|
2011-07-15 00:54:11 -04:00
|
|
|
// Likewise, if no tickcount signature is specified, assume 4 ticks
|
2011-09-15 03:28:58 +00:00
|
|
|
// per beat for the entire song. The default of 4 is chosen more
|
|
|
|
|
// for compatibility with the main Pump series than anything else.
|
|
|
|
|
// (TickcountSegment's constructor handles that now. -- vyhd)
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_TICKCOUNT].empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( TickcountSegment(0) );
|
|
|
|
|
|
2011-05-10 16:00:47 +07:00
|
|
|
// Have a default combo segment of one just in case.
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_COMBO].empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( ComboSegment(0) );
|
|
|
|
|
|
2011-05-10 16:00:47 +07:00
|
|
|
// Have a default label segment just in case.
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_LABEL].empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( LabelSegment(0, "Song Start") );
|
|
|
|
|
|
2011-05-15 13:48:10 -04:00
|
|
|
// Always be sure there is a starting speed.
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_SPEED].empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( SpeedSegment(0) );
|
|
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
// Always be sure there is a starting scrolling factor.
|
2011-07-15 00:54:11 -04:00
|
|
|
if( segs[SEGMENT_SCROLL].empty() )
|
2011-09-15 03:28:58 +00:00
|
|
|
AddSegment( ScrollSegment(0) );
|
2011-05-10 16:00:47 +07:00
|
|
|
}
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
void TimingData::SortSegments( TimingSegmentType tst )
|
2011-05-16 02:00:17 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
vector<TimingSegment*> &vSegments = m_avpTimingSegments[tst];
|
|
|
|
|
sort( vSegments.begin(), vSegments.end() );
|
2011-05-16 02:00:17 -04:00
|
|
|
}
|
|
|
|
|
|
2011-05-15 13:48:10 -04:00
|
|
|
bool TimingData::HasSpeedChanges() const
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &speeds = GetTimingSegments(SEGMENT_SPEED);
|
|
|
|
|
return (speeds.size()>1 || ToSpeed(speeds[0])->GetRatio() != 1);
|
2011-05-15 13:48:10 -04:00
|
|
|
}
|
|
|
|
|
|
2011-05-25 22:44:41 +07:00
|
|
|
bool TimingData::HasScrollChanges() const
|
|
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &scrolls = GetTimingSegments(SEGMENT_SCROLL);
|
|
|
|
|
return (scrolls.size()>1 || ToScroll(scrolls[0])->GetRatio() != 1);
|
2011-05-25 22:44:41 +07:00
|
|
|
}
|
|
|
|
|
|
2011-03-17 01:47:30 -04:00
|
|
|
void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const
|
|
|
|
|
{
|
|
|
|
|
iMeasureIndexOut = 0;
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &tSigs = GetTimingSegments(SEGMENT_TIME_SIG);
|
2011-07-15 00:54:11 -04:00
|
|
|
for (unsigned i = 0; i < tSigs.size(); i++)
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
2011-09-11 17:13:10 +00:00
|
|
|
TimeSignatureSegment *curSig = ToTimeSignature(tSigs[i]);
|
2011-07-15 00:54:11 -04:00
|
|
|
int iSegmentEndRow = (i + 1 == tSigs.size()) ? INT_MAX : curSig->GetRow();
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-07-15 00:54:11 -04:00
|
|
|
int iRowsPerMeasureThisSegment = curSig->GetNoteRowsPerMeasure();
|
2011-03-17 01:47:30 -04:00
|
|
|
|
2011-07-15 00:54:11 -04:00
|
|
|
if( iNoteRow >= curSig->GetRow() )
|
2011-03-17 01:47:30 -04:00
|
|
|
{
|
|
|
|
|
// iNoteRow lands in this segment
|
2011-07-15 00:54:11 -04:00
|
|
|
int iNumRowsThisSegment = iNoteRow - curSig->GetRow();
|
2011-03-17 01:47:30 -04:00
|
|
|
int iNumMeasuresThisSegment = (iNumRowsThisSegment) / iRowsPerMeasureThisSegment; // don't round up
|
|
|
|
|
iMeasureIndexOut += iNumMeasuresThisSegment;
|
|
|
|
|
iBeatIndexOut = iNumRowsThisSegment / iRowsPerMeasureThisSegment;
|
|
|
|
|
iRowsRemainder = iNumRowsThisSegment % iRowsPerMeasureThisSegment;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// iNoteRow lands after this segment
|
2011-07-15 00:54:11 -04:00
|
|
|
int iNumRowsThisSegment = iSegmentEndRow - curSig->GetRow();
|
|
|
|
|
int iNumMeasuresThisSegment = (iNumRowsThisSegment + iRowsPerMeasureThisSegment - 1)
|
|
|
|
|
/ iRowsPerMeasureThisSegment; // round up
|
2011-03-17 01:47:30 -04:00
|
|
|
iMeasureIndexOut += iNumMeasuresThisSegment;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ASSERT(0);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-27 22:33:50 -04:00
|
|
|
vector<RString> TimingData::ToVectorString(TimingSegmentType tst, int dec) const
|
2011-07-17 16:06:40 -04:00
|
|
|
{
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> segs = GetTimingSegments(tst);
|
2011-07-17 16:06:40 -04:00
|
|
|
vector<RString> ret;
|
2011-09-15 03:28:58 +00:00
|
|
|
|
2011-07-17 16:06:40 -04:00
|
|
|
for (unsigned i = 0; i < segs.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
ret.push_back(segs[i]->ToString(dec));
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
// lua start
|
|
|
|
|
#include "LuaBinding.h"
|
|
|
|
|
|
2011-09-15 03:28:58 +00:00
|
|
|
/** @brief Allow Lua to have access to the TimingData. */
|
2011-03-17 01:47:30 -04:00
|
|
|
class LunaTimingData: public Luna<TimingData>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
static int HasStops( T* p, lua_State *L ) { lua_pushboolean(L, p->HasStops()); return 1; }
|
2011-08-07 13:48:42 -05:00
|
|
|
static int HasDelays( T* p, lua_State *L ) { lua_pushboolean(L, p->HasDelays()); return 1; }
|
2011-03-17 01:47:30 -04:00
|
|
|
static int HasBPMChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasBpmChanges()); return 1; }
|
2011-03-24 20:21:43 -04:00
|
|
|
static int HasWarps( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; }
|
2011-05-16 02:00:17 -04:00
|
|
|
static int HasFakes( T* p, lua_State *L ) { lua_pushboolean(L, p->HasFakes()); return 1; }
|
2011-05-15 13:48:10 -04:00
|
|
|
static int HasSpeedChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSpeedChanges()); return 1; }
|
2011-06-01 10:12:10 -04:00
|
|
|
static int HasScrollChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasScrollChanges()); return 1; }
|
2011-06-15 16:56:01 -04:00
|
|
|
static int GetWarps( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_WARP), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetFakes( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_FAKE), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetScrolls( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_SCROLL), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetSpeeds( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_SPEED), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetTimeSignatures( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_TIME_SIG), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetCombos( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_COMBO), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetTickcounts( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_TICKCOUNT), L);
|
2011-06-15 16:56:01 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
static int GetStops( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-27 22:33:50 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_STOP), L);
|
2011-03-17 01:47:30 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetDelays( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-27 22:33:50 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_DELAY), L);
|
2011-03-17 01:47:30 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetBPMs( T* p, lua_State *L )
|
|
|
|
|
{
|
|
|
|
|
vector<float> vBPMs;
|
2011-09-15 03:28:58 +00:00
|
|
|
const vector<TimingSegment *> &bpms = p->GetTimingSegments(SEGMENT_BPM);
|
|
|
|
|
|
2011-07-14 15:22:32 -04:00
|
|
|
for (unsigned i = 0; i < bpms.size(); i++)
|
2011-09-15 03:28:58 +00:00
|
|
|
vBPMs.push_back( ToBPM(bpms[i])->GetBPM() );
|
2011-03-17 01:47:30 -04:00
|
|
|
|
|
|
|
|
LuaHelpers::CreateTableFromArray(vBPMs, L);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2011-04-04 23:03:10 -04:00
|
|
|
static int GetLabels( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_LABEL), L);
|
2011-04-04 23:03:10 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2011-03-17 01:47:30 -04:00
|
|
|
static int GetBPMsAndTimes( T* p, lua_State *L )
|
|
|
|
|
{
|
2011-07-17 16:06:40 -04:00
|
|
|
LuaHelpers::CreateTableFromArray(p->ToVectorString(SEGMENT_BPM), L);
|
2011-03-17 01:47:30 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
static int GetActualBPM( T* p, lua_State *L )
|
|
|
|
|
{
|
|
|
|
|
// certainly there's a better way to do it than this? -aj
|
|
|
|
|
float fMinBPM, fMaxBPM;
|
|
|
|
|
p->GetActualBPM( fMinBPM, fMaxBPM );
|
|
|
|
|
vector<float> fBPMs;
|
|
|
|
|
fBPMs.push_back( fMinBPM );
|
|
|
|
|
fBPMs.push_back( fMaxBPM );
|
|
|
|
|
LuaHelpers::CreateTableFromArray(fBPMs, L);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2011-06-09 18:16:23 -04:00
|
|
|
static int HasNegativeBPMs( T* p, lua_State *L ) { lua_pushboolean(L, p->HasWarps()); return 1; }
|
2011-03-17 01:47:30 -04:00
|
|
|
// formerly in Song.cpp in sm-ssc private beta 1.x:
|
|
|
|
|
static int GetBPMAtBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBPMAtBeat(FArg(1))); return 1; }
|
|
|
|
|
static int GetBeatFromElapsedTime( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBeatFromElapsedTime(FArg(1))); return 1; }
|
|
|
|
|
static int GetElapsedTimeFromBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->GetElapsedTimeFromBeat(FArg(1))); return 1; }
|
|
|
|
|
|
|
|
|
|
LunaTimingData()
|
|
|
|
|
{
|
|
|
|
|
ADD_METHOD( HasStops );
|
2011-08-07 13:48:42 -05:00
|
|
|
ADD_METHOD( HasDelays );
|
2011-03-17 01:47:30 -04:00
|
|
|
ADD_METHOD( HasBPMChanges );
|
2011-03-24 20:21:43 -04:00
|
|
|
ADD_METHOD( HasWarps );
|
2011-05-16 02:00:17 -04:00
|
|
|
ADD_METHOD( HasFakes );
|
2011-05-15 13:48:10 -04:00
|
|
|
ADD_METHOD( HasSpeedChanges );
|
2011-06-01 10:12:10 -04:00
|
|
|
ADD_METHOD( HasScrollChanges );
|
2011-03-17 01:47:30 -04:00
|
|
|
ADD_METHOD( GetStops );
|
|
|
|
|
ADD_METHOD( GetDelays );
|
|
|
|
|
ADD_METHOD( GetBPMs );
|
2011-06-15 16:56:01 -04:00
|
|
|
ADD_METHOD( GetWarps );
|
|
|
|
|
ADD_METHOD( GetFakes );
|
|
|
|
|
ADD_METHOD( GetTimeSignatures );
|
|
|
|
|
ADD_METHOD( GetTickcounts );
|
|
|
|
|
ADD_METHOD( GetSpeeds );
|
|
|
|
|
ADD_METHOD( GetScrolls );
|
|
|
|
|
ADD_METHOD( GetCombos );
|
2011-04-04 23:03:10 -04:00
|
|
|
ADD_METHOD( GetLabels );
|
2011-03-17 01:47:30 -04:00
|
|
|
ADD_METHOD( GetBPMsAndTimes );
|
|
|
|
|
ADD_METHOD( GetActualBPM );
|
|
|
|
|
ADD_METHOD( HasNegativeBPMs );
|
|
|
|
|
// formerly in Song.cpp in sm-ssc private beta 1.x:
|
|
|
|
|
ADD_METHOD( GetBPMAtBeat );
|
|
|
|
|
ADD_METHOD( GetBeatFromElapsedTime );
|
|
|
|
|
ADD_METHOD( GetElapsedTimeFromBeat );
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
LUA_REGISTER_CLASS( TimingData )
|
|
|
|
|
// lua end
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* (c) 2001-2004 Chris Danford, Glenn Maynard
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the
|
|
|
|
|
* "Software"), to deal in the Software without restriction, including
|
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
|
|
|
* whom the Software is furnished to do so, provided that the above
|
|
|
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
|
|
|
* the Software and that both the above copyright notice(s) and this
|
|
|
|
|
* permission notice appear in supporting documentation.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
|
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
|
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
|
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
|
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
|
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|