2003-12-18 04:42:54 +00:00
|
|
|
/* Holds data for translating beats<->seconds. */
|
|
|
|
|
|
|
|
|
|
#include "global.h"
|
|
|
|
|
#include "TimingData.h"
|
|
|
|
|
#include "PrefsManager.h"
|
2004-01-28 21:28:43 +00:00
|
|
|
#include "RageUtil.h"
|
2003-12-18 04:42:54 +00:00
|
|
|
|
|
|
|
|
TimingData::TimingData()
|
|
|
|
|
{
|
|
|
|
|
m_fBeat0OffsetInSeconds = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int CompareBPMSegments(const BPMSegment &seg1, const BPMSegment &seg2)
|
|
|
|
|
{
|
|
|
|
|
return seg1.m_fStartBeat < seg2.m_fStartBeat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SortBPMSegmentsArray( vector<BPMSegment> &arrayBPMSegments )
|
|
|
|
|
{
|
|
|
|
|
sort( arrayBPMSegments.begin(), arrayBPMSegments.end(), CompareBPMSegments );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int CompareStopSegments(const StopSegment &seg1, const StopSegment &seg2)
|
|
|
|
|
{
|
|
|
|
|
return seg1.m_fStartBeat < seg2.m_fStartBeat;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SortStopSegmentsArray( vector<StopSegment> &arrayStopSegments )
|
|
|
|
|
{
|
|
|
|
|
sort( arrayStopSegments.begin(), arrayStopSegments.end(), CompareStopSegments );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut ) const
|
|
|
|
|
{
|
|
|
|
|
fMaxBPMOut = 0;
|
|
|
|
|
fMinBPMOut = 100000; // inf
|
|
|
|
|
for( unsigned i=0; i<m_BPMSegments.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
const BPMSegment &seg = m_BPMSegments[i];
|
|
|
|
|
fMaxBPMOut = max( seg.m_fBPM, fMaxBPMOut );
|
|
|
|
|
fMinBPMOut = min( seg.m_fBPM, fMinBPMOut );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void TimingData::AddBPMSegment( const BPMSegment &seg )
|
|
|
|
|
{
|
|
|
|
|
m_BPMSegments.push_back( seg );
|
|
|
|
|
SortBPMSegmentsArray( m_BPMSegments );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TimingData::AddStopSegment( const StopSegment &seg )
|
|
|
|
|
{
|
|
|
|
|
m_StopSegments.push_back( seg );
|
|
|
|
|
SortStopSegmentsArray( m_StopSegments );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TimingData::SetBPMAtBeat( float fBeat, float fBPM )
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
for( i=0; i<m_BPMSegments.size(); i++ )
|
|
|
|
|
if( m_BPMSegments[i].m_fStartBeat == fBeat )
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if( i == m_BPMSegments.size() ) // there is no BPMSegment at the current beat
|
|
|
|
|
{
|
|
|
|
|
// create a new BPMSegment
|
|
|
|
|
AddBPMSegment( BPMSegment(fBeat, fBPM) );
|
|
|
|
|
}
|
|
|
|
|
else // BPMSegment being modified is m_BPMSegments[i]
|
|
|
|
|
{
|
|
|
|
|
if( i > 0 && fabsf(m_BPMSegments[i-1].m_fBPM - fBPM) < 0.009f )
|
|
|
|
|
m_BPMSegments.erase( m_BPMSegments.begin()+i,
|
|
|
|
|
m_BPMSegments.begin()+i+1);
|
|
|
|
|
else
|
|
|
|
|
m_BPMSegments[i].m_fBPM = fBPM;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float TimingData::GetBPMAtBeat( float fBeat ) const
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
for( i=0; i<m_BPMSegments.size()-1; i++ )
|
|
|
|
|
if( m_BPMSegments[i+1].m_fStartBeat > fBeat )
|
|
|
|
|
break;
|
|
|
|
|
return m_BPMSegments[i].m_fBPM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BPMSegment& TimingData::GetBPMSegmentAtBeat( float fBeat )
|
|
|
|
|
{
|
|
|
|
|
unsigned i;
|
|
|
|
|
for( i=0; i<m_BPMSegments.size()-1; i++ )
|
|
|
|
|
if( m_BPMSegments[i+1].m_fStartBeat > fBeat )
|
|
|
|
|
break;
|
|
|
|
|
return m_BPMSegments[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut ) const
|
|
|
|
|
{
|
|
|
|
|
// LOG->Trace( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime );
|
|
|
|
|
|
|
|
|
|
fElapsedTime += PREFSMAN->m_fGlobalOffsetSeconds;
|
|
|
|
|
|
|
|
|
|
fElapsedTime += m_fBeat0OffsetInSeconds;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment
|
|
|
|
|
{
|
2004-01-28 21:28:43 +00:00
|
|
|
const float fStartBeatThisSegment = m_BPMSegments[i].m_fStartBeat;
|
2004-02-24 23:40:28 +00:00
|
|
|
const bool bIsFirstBPMSegment = i==0;
|
2004-01-28 21:28:43 +00:00
|
|
|
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1;
|
|
|
|
|
const float fStartBeatNextSegment = bIsLastBPMSegment ? 40000/*inf*/ : m_BPMSegments[i+1].m_fStartBeat;
|
|
|
|
|
const float fBPS = m_BPMSegments[i].m_fBPM / 60.0f;
|
2003-12-18 04:42:54 +00:00
|
|
|
|
2004-01-28 21:28:43 +00:00
|
|
|
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze
|
2003-12-18 04:42:54 +00:00
|
|
|
{
|
2004-02-24 23:40:28 +00:00
|
|
|
if( !bIsFirstBPMSegment && fStartBeatThisSegment >= m_StopSegments[j].m_fStartBeat )
|
2004-01-28 21:28:43 +00:00
|
|
|
continue;
|
|
|
|
|
if( !bIsLastBPMSegment && m_StopSegments[j].m_fStartBeat > fStartBeatNextSegment )
|
2003-12-18 04:42:54 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// this freeze lies within this BPMSegment
|
2004-01-28 21:28:43 +00:00
|
|
|
const float fBeatsSinceStartOfSegment = m_StopSegments[j].m_fStartBeat - fStartBeatThisSegment;
|
|
|
|
|
const float fFreezeStartSecond = fBeatsSinceStartOfSegment / fBPS;
|
|
|
|
|
|
|
|
|
|
if( fFreezeStartSecond >= fElapsedTime )
|
2003-12-18 04:42:54 +00:00
|
|
|
break;
|
|
|
|
|
|
2004-01-28 21:28:43 +00:00
|
|
|
// the freeze segment is <= current time
|
2003-12-18 04:42:54 +00:00
|
|
|
fElapsedTime -= m_StopSegments[j].m_fStopSeconds;
|
2004-01-28 21:28:43 +00:00
|
|
|
|
|
|
|
|
if( fFreezeStartSecond >= fElapsedTime )
|
2003-12-18 04:42:54 +00:00
|
|
|
{
|
2004-01-28 21:28:43 +00:00
|
|
|
/* The time lies within the stop. */
|
2003-12-18 04:42:54 +00:00
|
|
|
fBeatOut = m_StopSegments[j].m_fStartBeat;
|
|
|
|
|
fBPSOut = fBPS;
|
|
|
|
|
bFreezeOut = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-28 21:28:43 +00:00
|
|
|
const float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment;
|
|
|
|
|
const float fSecondsInThisSegment = fBeatsInThisSegment / fBPS;
|
|
|
|
|
if( bIsLastBPMSegment || fElapsedTime <= fSecondsInThisSegment )
|
|
|
|
|
{
|
|
|
|
|
// this BPMSegment IS the current segment
|
|
|
|
|
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
|
|
|
|
|
fBPSOut = fBPS;
|
|
|
|
|
bFreezeOut = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this BPMSegment is NOT the current segment
|
|
|
|
|
fElapsedTime -= fSecondsInThisSegment;
|
2003-12-18 04:42:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float TimingData::GetElapsedTimeFromBeat( float fBeat ) const
|
|
|
|
|
{
|
|
|
|
|
float fElapsedTime = 0;
|
|
|
|
|
fElapsedTime -= PREFSMAN->m_fGlobalOffsetSeconds;
|
|
|
|
|
fElapsedTime -= m_fBeat0OffsetInSeconds;
|
|
|
|
|
|
|
|
|
|
for( unsigned j=0; j<m_StopSegments.size(); j++ ) // foreach freeze
|
|
|
|
|
{
|
2004-01-28 21:28:43 +00:00
|
|
|
/* The exact beat of a stop comes before the stop, not after, so use >=, not >. */
|
2003-12-18 04:42:54 +00:00
|
|
|
if( m_StopSegments[j].m_fStartBeat >= fBeat )
|
|
|
|
|
break;
|
|
|
|
|
fElapsedTime += m_StopSegments[j].m_fStopSeconds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( unsigned i=0; i<m_BPMSegments.size(); i++ ) // foreach BPMSegment
|
|
|
|
|
{
|
|
|
|
|
const bool bIsLastBPMSegment = i==m_BPMSegments.size()-1;
|
|
|
|
|
const float fBPS = m_BPMSegments[i].m_fBPM / 60.0f;
|
|
|
|
|
|
2004-01-28 21:28:43 +00:00
|
|
|
if( bIsLastBPMSegment )
|
|
|
|
|
{
|
|
|
|
|
fElapsedTime += fBeat / fBPS;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const float fStartBeatThisSegment = m_BPMSegments[i].m_fStartBeat;
|
|
|
|
|
const float fStartBeatNextSegment = m_BPMSegments[i+1].m_fStartBeat;
|
|
|
|
|
const float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment;
|
|
|
|
|
fElapsedTime += min(fBeat, fBeatsInThisSegment) / fBPS;
|
|
|
|
|
fBeat -= fBeatsInThisSegment;
|
|
|
|
|
}
|
2003-12-18 04:42:54 +00:00
|
|
|
|
|
|
|
|
if( fBeat <= 0 )
|
|
|
|
|
return fElapsedTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fElapsedTime;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
void TimingData::ScaleRegion( float fScale, float fStartBeat, float fEndBeat )
|
|
|
|
|
{
|
2004-02-26 05:38:46 +00:00
|
|
|
ASSERT( fScale > 0 );
|
|
|
|
|
ASSERT( fStartBeat >= 0 );
|
|
|
|
|
ASSERT( fStartBeat < fEndBeat );
|
|
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
unsigned ix = 0;
|
2004-02-26 05:38:46 +00:00
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
for ( ix = 0; ix < m_BPMSegments.size(); ix++ )
|
|
|
|
|
{
|
2004-02-26 05:38:46 +00:00
|
|
|
const float fSegStart = m_BPMSegments[ix].m_fStartBeat;
|
2004-02-26 06:00:52 +00:00
|
|
|
if( fSegStart < fStartBeat )
|
2004-02-26 05:38:46 +00:00
|
|
|
continue;
|
2004-02-26 06:00:52 +00:00
|
|
|
else if( fSegStart > fEndBeat )
|
2004-02-26 05:38:46 +00:00
|
|
|
m_BPMSegments[ix].m_fStartBeat += (fEndBeat - fStartBeat) * (fScale - 1);
|
|
|
|
|
else
|
|
|
|
|
m_BPMSegments[ix].m_fStartBeat = (fSegStart - fStartBeat) * fScale + fStartBeat;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
for( ix = 0; ix < m_StopSegments.size(); ix++ )
|
|
|
|
|
{
|
2004-02-26 05:38:46 +00:00
|
|
|
const float fSegStart = m_StopSegments[ix].m_fStartBeat;
|
2004-02-26 06:00:52 +00:00
|
|
|
if( fSegStart < fStartBeat )
|
2004-02-26 05:38:46 +00:00
|
|
|
continue;
|
2004-02-26 06:00:52 +00:00
|
|
|
else if( fSegStart > fEndBeat )
|
2004-02-26 05:38:46 +00:00
|
|
|
m_StopSegments[ix].m_fStartBeat += (fEndBeat - fStartBeat) * (fScale - 1);
|
|
|
|
|
else
|
|
|
|
|
m_StopSegments[ix].m_fStartBeat = (fSegStart - fStartBeat) * fScale + fStartBeat;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
void TimingData::ShiftRows( float fStartBeat, float fBeatsToShift )
|
|
|
|
|
{
|
|
|
|
|
unsigned ix = 0;
|
2004-02-26 05:38:46 +00:00
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
for( ix = 0; ix < m_BPMSegments.size(); ix++ )
|
|
|
|
|
{
|
2004-02-26 06:46:36 +00:00
|
|
|
float &fSegStart = m_BPMSegments[ix].m_fStartBeat;
|
2004-02-26 06:00:52 +00:00
|
|
|
if( fSegStart < fStartBeat )
|
2004-02-26 05:38:46 +00:00
|
|
|
continue;
|
2004-02-26 06:00:52 +00:00
|
|
|
|
2004-02-26 06:46:36 +00:00
|
|
|
fSegStart += fBeatsToShift;
|
|
|
|
|
fSegStart = max( fSegStart, fStartBeat );
|
2004-02-26 05:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
2004-02-26 06:00:52 +00:00
|
|
|
for( ix = 0; ix < m_StopSegments.size(); ix++ )
|
|
|
|
|
{
|
2004-02-26 06:46:36 +00:00
|
|
|
float &fSegStart = m_StopSegments[ix].m_fStartBeat;
|
2004-02-26 06:00:52 +00:00
|
|
|
if( fSegStart < fStartBeat )
|
2004-02-26 05:38:46 +00:00
|
|
|
continue;
|
2004-02-26 06:46:36 +00:00
|
|
|
fSegStart += fBeatsToShift;
|
|
|
|
|
fSegStart = max( fSegStart, fStartBeat );
|
2004-02-26 05:38:46 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-08 07:49:01 +00:00
|
|
|
bool TimingData::HasBpmChangesOrStops() const
|
|
|
|
|
{
|
|
|
|
|
return m_BPMSegments.size()>1 || m_StopSegments.size()>0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-18 04:42:54 +00:00
|
|
|
/*
|
2004-02-26 05:38:46 +00:00
|
|
|
* Copyright (c) 2001-2004 by the person(s) listed below. All rights reserved.
|
2003-12-18 04:42:54 +00:00
|
|
|
* Chris Danford
|
|
|
|
|
* Glenn Maynard
|
|
|
|
|
*/
|