예제 #1
0
OSStatus RageSound_CA::GetData(AudioDeviceID inDevice,
							   const AudioTimeStamp *inNow,
							   const AudioBufferList *inInputData,
							   const AudioTimeStamp *inInputTime,
							   AudioBufferList *outOutputData,
							   const AudioTimeStamp *inOutputTime,
							   void *inClientData)
{
	RageTimer tm;
	RageSound_CA *This = (RageSound_CA *)inClientData;
	AudioBuffer& buf = outOutputData->mBuffers[0];
	UInt32 dataPackets = buf.mDataByteSize >> 3; // 8 byes per packet
	int64_t decodePos = int64_t(inOutputTime->mSampleTime);
	int64_t now = int64_t(inNow->mSampleTime);
	
	RageTimer tm2;
	int16_t buffer[dataPackets * (kBytesPerPacket >> 1)];
		
	This->Mix(buffer, dataPackets, decodePos, now);
	g_fLastMixTimes[g_iLastMixTimePos] = tm2.GetDeltaTime();
	++g_iLastMixTimePos;
	wrap(g_iLastMixTimePos, NUM_MIX_TIMES);
		
	AudioConverterConvertBuffer(This->mConverter, dataPackets * kBytesPerPacket,
								buffer, &buf.mDataByteSize, buf.mData);
		
	g_fLastIOProcTime = tm.GetDeltaTime();
	++g_iNumIOProcCalls;
	
	return noErr;
}
예제 #2
0
void RageDisplay::ResetStats()
{
	g_iFPS = g_iVPF = 0;
	g_iFramesRenderedSinceLastCheck = g_iFramesRenderedSinceLastReset = 0;
	g_iNumChecksSinceLastReset = 0;
	g_iVertsRenderedSinceLastCheck = 0;
	g_LastCheckTimer.GetDeltaTime();
}
예제 #3
0
	void Paint()
	{
		/* We load songs much faster than we draw frames. Cap the draw rate,
		 * so we don't slow down the reload. */
		if( m_LastDraw.PeekDeltaTime() < 1.0f/DrawFrameRate )
			return;
		m_LastDraw.GetDeltaTime();

		SCREENMAN->Draw();
	}
예제 #4
0
void ConcurrentRenderer::RenderThread()
{
	ASSERT( SCREENMAN != NULL );

	while( !m_bShutdown )
	{
		m_Event.Lock();
		while( m_State == RENDERING_IDLE && !m_bShutdown )
			m_Event.Wait();
		m_Event.Unlock();

		if( m_State == RENDERING_START )
		{
			/* We're starting to render.  Set up, and then kick the event to wake
			 * up the calling thread. */
			DISPLAY->BeginConcurrentRendering();
			HOOKS->SetupConcurrentRenderingThread();

			LOG->Trace( "ConcurrentRenderer::RenderThread start" );

			m_Event.Lock();
			m_State = RENDERING_ACTIVE;
			m_Event.Signal();
			m_Event.Unlock();
		}

		/* This is started during Update().  The next thing the game loop
		 * will do is Draw, so shift operations around to put Draw at the
		 * top.  This makes sure updates are seamless. */
		if( m_State == RENDERING_ACTIVE )
		{
			SCREENMAN->Draw();

			float fDeltaTime = g_GameplayTimer.GetDeltaTime();
			SCREENMAN->Update( fDeltaTime );
		}

		if( m_State == RENDERING_END )
		{
			LOG->Trace( "ConcurrentRenderer::RenderThread done" );

			DISPLAY->EndConcurrentRendering();

			m_Event.Lock();
			m_State = RENDERING_IDLE;
			m_Event.Signal();
			m_Event.Unlock();
		}
	}
}
예제 #5
0
void RageDisplay::ProcessStatsOnFlip()
{
	g_iFramesRenderedSinceLastCheck++;
	g_iFramesRenderedSinceLastReset++;

	if( g_LastCheckTimer.PeekDeltaTime() >= 1.0f )	// update stats every 1 sec.
	{
		g_LastCheckTimer.GetDeltaTime();
		g_iNumChecksSinceLastReset++;
		g_iFPS = g_iFramesRenderedSinceLastCheck;
		g_iCFPS = g_iFramesRenderedSinceLastReset / g_iNumChecksSinceLastReset;
		g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFPS;
		g_iFramesRenderedSinceLastCheck = g_iVertsRenderedSinceLastCheck = 0;
		if( LOG_FPS )
			LOG->Trace( "FPS: %d, CFPS %d, VPF: %d", g_iFPS, g_iCFPS, g_iVPF );
	}
}
예제 #6
0
void RageDisplay::ProcessStatsOnFlip()
{
	g_iFramesRenderedSinceLastCheck++;
	g_iFramesRenderedSinceLastReset++;

	if( g_LastCheckTimer.PeekDeltaTime() >= 1.0f )	// update stats every 1 sec.
	{
		float fActualTime = g_LastCheckTimer.GetDeltaTime();
		g_iNumChecksSinceLastReset++;
		g_iFPS = lrintf( g_iFramesRenderedSinceLastCheck / fActualTime );
		g_iCFPS = g_iFramesRenderedSinceLastReset / g_iNumChecksSinceLastReset;
		g_iCFPS = lrintf( g_iCFPS / fActualTime );
		g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFramesRenderedSinceLastCheck;
		g_iFramesRenderedSinceLastCheck = g_iVertsRenderedSinceLastCheck = 0;
		if( LOG_FPS )
		{
			RString sStats = GetStats();
			sStats.Replace( "\n", ", " );
			LOG->Trace( "%s", sStats.c_str() );
		}
	}
}
예제 #7
0
/* For visibility testing: if bAbsolute is false, random modifiers must return the
 * minimum possible scroll speed. */
float ArrowEffects::GetYOffset( const PlayerState* pPlayerState, int iCol, float fNoteBeat, float &fPeakYOffsetOut, bool &bIsPastPeakOut, bool bAbsolute )
{
	// Default values that are returned if boomerang is off.
	fPeakYOffsetOut = FLT_MAX;
	bIsPastPeakOut = true;


	float fYOffset = 0;

	/* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or
	 * entirely time spacing (respectively).  Occasionally, we tween between them. */
	if( pPlayerState->m_CurrentPlayerOptions.m_fTimeSpacing != 1.0f )
	{
		float fSongBeat = GAMESTATE->m_fSongBeat;
		float fBeatsUntilStep = fNoteBeat - fSongBeat;
		float fYOffsetBeatSpacing = fBeatsUntilStep * ARROW_SPACING;
		fYOffset += fYOffsetBeatSpacing * (1-pPlayerState->m_CurrentPlayerOptions.m_fTimeSpacing);
	}

	if( pPlayerState->m_CurrentPlayerOptions.m_fTimeSpacing != 0.0f )
	{
		float fSongSeconds = GAMESTATE->m_fMusicSeconds;
		float fNoteSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fNoteBeat);
		float fSecondsUntilStep = fNoteSeconds - fSongSeconds;
		float fBPM = pPlayerState->m_CurrentPlayerOptions.m_fScrollBPM;
		float fBPS = fBPM/60.f;
		float fYOffsetTimeSpacing = fSecondsUntilStep * fBPS * ARROW_SPACING;
		fYOffset += fYOffsetTimeSpacing * pPlayerState->m_CurrentPlayerOptions.m_fTimeSpacing;
	}

	// don't mess with the arrows after they've crossed 0
	if( fYOffset < 0 )
		return fYOffset * pPlayerState->m_CurrentPlayerOptions.m_fScrollSpeed;

	const float* fAccels = pPlayerState->m_CurrentPlayerOptions.m_fAccels;
	//const float* fEffects = pPlayerState->m_CurrentPlayerOptions.m_fEffects;


	float fYAdjust = 0;	// fill this in depending on PlayerOptions

	if( fAccels[PlayerOptions::ACCEL_BOOST] != 0 )
	{
		float fEffectHeight = GetNoteFieldHeight(pPlayerState);
		float fNewYOffset = fYOffset * 1.5f / ((fYOffset+fEffectHeight/1.2f)/fEffectHeight); 
		float fAccelYAdjust =	fAccels[PlayerOptions::ACCEL_BOOST] * (fNewYOffset - fYOffset);
		// TRICKY:	Clamp this value, or else BOOST+BOOMERANG will draw a ton of arrows on the screen.
		CLAMP( fAccelYAdjust, -400.f, 400.f );
		fYAdjust += fAccelYAdjust;
	}
	if( fAccels[PlayerOptions::ACCEL_BRAKE] != 0 )
	{
		float fEffectHeight = GetNoteFieldHeight(pPlayerState);
		float fScale = SCALE( fYOffset, 0.f, fEffectHeight, 0, 1.f );
		float fNewYOffset = fYOffset * fScale; 
		float fBrakeYAdjust = fAccels[PlayerOptions::ACCEL_BRAKE] * (fNewYOffset - fYOffset);
		// TRICKY:	Clamp this value the same way as BOOST so that in BOOST+BRAKE, BRAKE doesn't overpower BOOST
		CLAMP( fBrakeYAdjust, -400.f, 400.f );
		fYAdjust += fBrakeYAdjust;
	}
	if( fAccels[PlayerOptions::ACCEL_WAVE] != 0 )
		fYAdjust +=	fAccels[PlayerOptions::ACCEL_WAVE] * 20.0f*RageFastSin( fYOffset/38.0f );

	fYOffset += fYAdjust;

	//
	// Factor in boomerang
	//
	if( fAccels[PlayerOptions::ACCEL_BOOMERANG] != 0 )
	{
		float fOriginalYOffset = fYOffset;

		fYOffset = (-1*fOriginalYOffset*fOriginalYOffset/SCREEN_HEIGHT) + 1.5f*fOriginalYOffset;
		float fPeakAtYOffset = SCREEN_HEIGHT * 0.75f;	// zero point of function above
		fPeakYOffsetOut = (-1*fPeakAtYOffset*fPeakAtYOffset/SCREEN_HEIGHT) + 1.5f*fPeakAtYOffset;
		bIsPastPeakOut = fOriginalYOffset < fPeakAtYOffset;
	}

	//
	// Factor in scroll speed
	//
	float fScrollSpeed = pPlayerState->m_CurrentPlayerOptions.m_fScrollSpeed;
	if( pPlayerState->m_CurrentPlayerOptions.m_fRandomSpeed > 0 && !bAbsolute )
	{
		int seed = GAMESTATE->m_iStageSeed + ( BeatToNoteRow( fNoteBeat ) << 8 ) + (iCol * 100);

		/* Temporary hack: the first call to RandomFloat isn't "random"; it takes an extra
		 * call to get the RNG rolling. */
		RandomFloat( seed );
		float fRandom = RandomFloat( seed );

		/* Random speed always increases speed: a random speed of 10 indicates [1,11].
		 * This keeps it consistent with other mods: 0 means no effect. */
		fScrollSpeed *=
				SCALE( fRandom,
						0.0f, 1.0f,
						1.0f, pPlayerState->m_CurrentPlayerOptions.m_fRandomSpeed + 1.0f );
	}	


	if( fAccels[PlayerOptions::ACCEL_EXPAND] != 0 )
	{
		/* Timers can't be global, since they'll be initialized before SDL. */
		static RageTimer timerExpand;
		if( !GAMESTATE->m_bFreeze )
			g_fExpandSeconds += timerExpand.GetDeltaTime();
		else
			timerExpand.GetDeltaTime();	// throw away
		float fExpandMultiplier = SCALE( RageFastCos(g_fExpandSeconds*3), -1, 1, 0.75f, 1.75f );
		fScrollSpeed *=	SCALE( fAccels[PlayerOptions::ACCEL_EXPAND], 0.f, 1.f, 1.f, fExpandMultiplier );
	}

	fYOffset *= fScrollSpeed;
	fPeakYOffsetOut *= fScrollSpeed;

	return fYOffset;
}
예제 #8
0
float ArrowGetYOffset( PlayerNumber pn, int iCol, float fNoteBeat )
{
	float fYOffset = 0;

	/* Usually, fTimeSpacing is 0 or 1, in which case we use entirely beat spacing or
	 * entirely time spacing (respectively).  Occasionally, we tween between them. */
	if( GAMESTATE->m_CurrentPlayerOptions[pn].m_fTimeSpacing != 1.0f )
	{
		float fSongBeat = GAMESTATE->m_fSongBeat;
		float fBeatsUntilStep = fNoteBeat - fSongBeat;
		float fYOffsetBeatSpacing = fBeatsUntilStep * ARROW_SPACING;
		fYOffset += fYOffsetBeatSpacing * (1-GAMESTATE->m_CurrentPlayerOptions[pn].m_fTimeSpacing);
	}

	if( GAMESTATE->m_CurrentPlayerOptions[pn].m_fTimeSpacing != 0.0f )
	{
		float fSongSeconds = GAMESTATE->m_fMusicSeconds;
		float fNoteSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fNoteBeat);
		float fSecondsUntilStep = fNoteSeconds - fSongSeconds;
		float fBPM = GAMESTATE->m_CurrentPlayerOptions[pn].m_fScrollBPM;
		float fBPS = fBPM/60.f;
		float fYOffsetTimeSpacing = fSecondsUntilStep * fBPS * ARROW_SPACING;
		fYOffset += fYOffsetTimeSpacing * GAMESTATE->m_CurrentPlayerOptions[pn].m_fTimeSpacing;
	}

	// don't mess with the arrows after they've crossed 0
	if( fYOffset < 0 )
		return fYOffset * GAMESTATE->m_CurrentPlayerOptions[pn].m_fScrollSpeed;

	const float* fAccels = GAMESTATE->m_CurrentPlayerOptions[pn].m_fAccels;
	//const float* fEffects = GAMESTATE->m_CurrentPlayerOptions[pn].m_fEffects;


	float fYAdjust = 0;	// fill this in depending on PlayerOptions

	if( fAccels[PlayerOptions::ACCEL_BOOST] > 0 )
	{
		float fEffectHeight = GetNoteFieldHeight(pn);
		float fNewYOffset = fYOffset * 1.5f / ((fYOffset+fEffectHeight/1.2f)/fEffectHeight); 
		float fAccelYAdjust = fAccels[PlayerOptions::ACCEL_BOOST] * (fNewYOffset - fYOffset);
		// TRICKY:	Clamp this value, or else BOOST+BOOMERANG will draw a ton of arrows on the screen.
		CLAMP( fAccelYAdjust, -400.f, 400.f );
		fYAdjust += fAccelYAdjust;
	}
	if( fAccels[PlayerOptions::ACCEL_BRAKE] > 0 )
	{
		float fEffectHeight = GetNoteFieldHeight(pn);
		float fScale = SCALE( fYOffset, 0.f, fEffectHeight, 0, 1.f );
		float fNewYOffset = fYOffset * fScale; 
		float fBrakeYAdjust = fAccels[PlayerOptions::ACCEL_BRAKE] * (fNewYOffset - fYOffset);
		// TRICKY:	Clamp this value the same way as BOOST so that in BOOST+BRAKE, BRAKE doesn't overpower BOOST
		CLAMP( fBrakeYAdjust, -400.f, 400.f );
		fYAdjust += fBrakeYAdjust;
	}
	if( fAccels[PlayerOptions::ACCEL_WAVE] > 0 )
		fYAdjust +=	fAccels[PlayerOptions::ACCEL_WAVE] * 20.0f*sinf( fYOffset/38.0f );

	fYOffset += fYAdjust;

	if( fAccels[PlayerOptions::ACCEL_BOOMERANG] > 0 )
		fYOffset +=	fAccels[PlayerOptions::ACCEL_BOOMERANG] * (fYOffset * SCALE( fYOffset, 0.f, SCREEN_HEIGHT, 1.5f, 0.5f )- fYOffset);

	float fScrollSpeed = GAMESTATE->m_CurrentPlayerOptions[pn].m_fScrollSpeed;

	if( fAccels[PlayerOptions::ACCEL_EXPAND] > 0 )
	{
		/* Timers can't be global, since they'll be initialized before SDL. */
		static RageTimer timerExpand;
		if( !GAMESTATE->m_bFreeze )
			g_fExpandSeconds += timerExpand.GetDeltaTime();
		else
			timerExpand.GetDeltaTime();	// throw away
		float fExpandMultiplier = SCALE( cosf(g_fExpandSeconds*3), -1, 1, 0.5f, 1.5f );
		fScrollSpeed *=	SCALE( fAccels[PlayerOptions::ACCEL_EXPAND], 0.f, 1.f, 1.f, fExpandMultiplier );
	}

	fYOffset *= fScrollSpeed;

	return fYOffset;
}
예제 #9
0
void GameLoop::RunGameLoop()
{
	/* People may want to do something else while songs are loading, so do
	 * this after loading songs. */
	if( ChangeAppPri() )
		HOOKS->BoostPriority();

	while( !ArchHooks::UserQuit() )
	{
		if( !g_sNewTheme.empty() )
			DoChangeTheme();

		/*
		 * Update
		 */
		float fDeltaTime = g_GameplayTimer.GetDeltaTime();

		if( g_fConstantUpdateDeltaSeconds > 0 )
			fDeltaTime = g_fConstantUpdateDeltaSeconds;
		
		CheckGameLoopTimerSkips( fDeltaTime );

		fDeltaTime *= g_fUpdateRate;

		/* XXX
		PitchDetectionTest::Update();
		*/

		CheckFocus();

		/* Update SOUNDMAN early (before any RageSound::GetPosition calls), to flush position data. */
		SOUNDMAN->Update();

		/* Update song beat information -before- calling update on all the classes that
		 * depend on it.  If you don't do this first, the classes are all acting on old 
		 * information and will lag.  (but no longer fatally, due to timestamping -glenn) */
		SOUND->Update( fDeltaTime );
		TEXTUREMAN->Update( fDeltaTime );
		GAMESTATE->Update( fDeltaTime );
		SCREENMAN->Update( fDeltaTime );
		MEMCARDMAN->Update();
		NSMAN->Update( fDeltaTime );

		/* Important:  Process input AFTER updating game logic, or input will be acting on song beat from last frame */
		HandleInputEvents( fDeltaTime );

		if( INPUTMAN->DevicesChanged() )
		{
			INPUTFILTER->Reset();	// fix "buttons stuck" if button held while unplugged
			INPUTMAN->LoadDrivers();
			RString sMessage;
			if( INPUTMAPPER->CheckForChangedInputDevicesAndRemap(sMessage) )
				SCREENMAN->SystemMessage( sMessage );
		}

		LIGHTSMAN->Update( fDeltaTime );

		/*
		 * Render
		 */
		SCREENMAN->Draw();
	}

	/* If we ended mid-game, finish up. */
	GAMESTATE->SaveLocalData();

	if( ChangeAppPri() )
		HOOKS->UnBoostPriority();
}
예제 #10
0
void run()
{
#define CHECK(call, exp) \
{ \
	float ret = call; \
	if( call != exp ) { \
		LOG->Warn( "Line %i: Got %f, expected %f", __LINE__, ret, exp); \
		return; \
	} \
}

	TimingData test;
	test.AddBPMSegment( BPMSegment(0, 60) );
	
	/* First, trivial sanity checks. */
	CHECK( test.GetBeatFromElapsedTime(60), 60.0f );
	CHECK( test.GetElapsedTimeFromBeat(60), 60.0f );

	/* The first BPM segment extends backwards in time. */
	CHECK( test.GetBeatFromElapsedTime(-60), -60.0f );
	CHECK( test.GetElapsedTimeFromBeat(-60), -60.0f );
	
	CHECK( test.GetBeatFromElapsedTime(100000), 100000.0f );
	CHECK( test.GetElapsedTimeFromBeat(100000), 100000.0f );
	CHECK( test.GetBeatFromElapsedTime(-100000), -100000.0f );
	CHECK( test.GetElapsedTimeFromBeat(-100000), -100000.0f );
	
	CHECK( test.GetBPMAtBeat(0), 60.0f );
	CHECK( test.GetBPMAtBeat(100000), 60.0f );
	CHECK( test.GetBPMAtBeat(-100000), 60.0f );

	/* 120BPM at beat 10: */
	test.AddBPMSegment( BPMSegment(10, 120) );
	CHECK( test.GetBPMAtBeat(9.99), 60.0f );
	CHECK( test.GetBPMAtBeat(10), 120.0f );

	CHECK( test.GetBeatFromElapsedTime(9), 9.0f );
	CHECK( test.GetBeatFromElapsedTime(10), 10.0f );
	CHECK( test.GetBeatFromElapsedTime(10.5), 11.0f );

	CHECK( test.GetElapsedTimeFromBeat(9), 9.0f );
	CHECK( test.GetElapsedTimeFromBeat(10), 10.0f );
	CHECK( test.GetElapsedTimeFromBeat(11), 10.5f );

	/* Add a 5-second stop at beat 10. */
	test.AddStopSegment( StopSegment(10, 5) );

	/* The stop shouldn't affect GetBPMAtBeat at all. */
	CHECK( test.GetBPMAtBeat(9.99), 60.0f );
	CHECK( test.GetBPMAtBeat(10), 120.0f );

	CHECK( test.GetBeatFromElapsedTime(9), 9.0f );
	CHECK( test.GetBeatFromElapsedTime(10), 10.0f );
	CHECK( test.GetBeatFromElapsedTime(12), 10.0f );
	CHECK( test.GetBeatFromElapsedTime(14), 10.0f );
	CHECK( test.GetBeatFromElapsedTime(15), 10.0f );
	CHECK( test.GetBeatFromElapsedTime(15.5), 11.0f );
	
	CHECK( test.GetElapsedTimeFromBeat(9), 9.0f );
	CHECK( test.GetElapsedTimeFromBeat(10), 10.0f );
	CHECK( test.GetElapsedTimeFromBeat(11), 15.5f );

	/* Add a 2-second stop at beat 5 and a 5-second stop at beat 15. */
	test.m_StopSegments.clear();
	test.AddStopSegment( StopSegment(5, 2) );
	test.AddStopSegment( StopSegment(15, 5) );
	CHECK( test.GetBPMAtBeat(9.99), 60.0f );
	CHECK( test.GetBPMAtBeat(10), 120.0f );

	CHECK( test.GetBeatFromElapsedTime(1), 1.0f );
	CHECK( test.GetBeatFromElapsedTime(2), 2.0f );
	CHECK( test.GetBeatFromElapsedTime(5), 5.0f ); // stopped
	CHECK( test.GetBeatFromElapsedTime(6), 5.0f ); // stopped
	CHECK( test.GetBeatFromElapsedTime(7), 5.0f ); // stop finished
	CHECK( test.GetBeatFromElapsedTime(8), 6.0f );
	CHECK( test.GetBeatFromElapsedTime(12), 10.0f ); // bpm changes to 120
	CHECK( test.GetBeatFromElapsedTime(13), 12.0f );
	CHECK( test.GetBeatFromElapsedTime(14), 14.0f );
	CHECK( test.GetBeatFromElapsedTime(14.5f), 15.0f ); // stopped
	CHECK( test.GetBeatFromElapsedTime(15), 15.0f ); // stopped
	CHECK( test.GetBeatFromElapsedTime(17), 15.0f ); // stopped
	CHECK( test.GetBeatFromElapsedTime(19.5f), 15.0f ); // stop finished
	CHECK( test.GetBeatFromElapsedTime(20), 16.0f );

	CHECK( test.GetElapsedTimeFromBeat(1), 1.0f );
	CHECK( test.GetElapsedTimeFromBeat(2), 2.0f );
	CHECK( test.GetElapsedTimeFromBeat(5), 5.0f ); // stopped
	CHECK( test.GetElapsedTimeFromBeat(6), 8.0f );
	CHECK( test.GetElapsedTimeFromBeat(10), 12.0f ); // bpm changes to 120
	CHECK( test.GetElapsedTimeFromBeat(12), 13.0f );
	CHECK( test.GetElapsedTimeFromBeat(14), 14.0f );
	CHECK( test.GetElapsedTimeFromBeat(15.0f), 14.5f ); // stopped
	CHECK( test.GetElapsedTimeFromBeat(16), 20.0f );

RageTimer foobar;
	/* We can look up the time of any given beat, then look up the beat of that
	 * time and get the original value.  (We can't do this in reverse; the beat
	 * doesn't move during stop segments.) */
int q = 0;
	for( float f = -10; f < 250; f += 0.002 )
	{
		++q;
//		const float t = test.GetElapsedTimeFromBeat( f );
		const float b = test.GetBeatFromElapsedTime( f );

		/* b == f */
	
//		if( fabsf(b-f) > 0.001 )
//		{
//			LOG->Warn( "%f != %f", b, f );
//			return;
//		}
	}
LOG->Trace("... %i in %f", q, foobar.GetDeltaTime());

	TimingData test2;
	test2.AddBPMSegment( BPMSegment(0, 60) );
	test2.AddStopSegment( StopSegment(0, 1) );
	CHECK( test2.GetBeatFromElapsedTime(-1), -1.0f );
	CHECK( test2.GetBeatFromElapsedTime(0), 0.0f );
	CHECK( test2.GetBeatFromElapsedTime(1), 0.0f );
	CHECK( test2.GetBeatFromElapsedTime(2), 1.0f );
	CHECK( test2.GetElapsedTimeFromBeat(-1), -1.0f );
	CHECK( test2.GetElapsedTimeFromBeat(0), 0.0f );
	CHECK( test2.GetElapsedTimeFromBeat(1), 2.0f );
	CHECK( test2.GetElapsedTimeFromBeat(2), 3.0f );
}