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; }
void RageDisplay::ResetStats() { g_iFPS = g_iVPF = 0; g_iFramesRenderedSinceLastCheck = g_iFramesRenderedSinceLastReset = 0; g_iNumChecksSinceLastReset = 0; g_iVertsRenderedSinceLastCheck = 0; g_LastCheckTimer.GetDeltaTime(); }
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(); }
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(); } } }
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 ); } }
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() ); } } }
/* 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; }
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; }
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(); }
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 ); }