bool RageDisplay::SaveScreenshot( RString sPath, GraphicsFileFormat format ) { RageTimer timer; RageSurface *surface = this->CreateScreenshot(); // LOG->Trace( "CreateScreenshot took %f seconds", timer.GetDeltaTime() ); /* Unless we're in lossless, resize the image to 640x480. If we're saving lossy, * there's no sense in saving 1280x960 screenshots, and we don't want to output * screenshots in a strange (non-1) sample aspect ratio. */ if( format != SAVE_LOSSLESS && format != SAVE_LOSSLESS_SENSIBLE ) { // Maintain the DAR. ASSERT( GetActualVideoModeParams().fDisplayAspectRatio > 0 ); int iHeight = 480; // This used to be lrintf. However, lrintf causes odd resolutions like // 639x480 (4:3) and 853x480 (16:9). ceilf gives correct values. -aj int iWidth = static_cast<int>(ceilf( iHeight * GetActualVideoModeParams().fDisplayAspectRatio )); timer.Touch(); RageSurfaceUtils::Zoom( surface, iWidth, iHeight ); // LOG->Trace( "%ix%i -> %ix%i (%.3f) in %f seconds", surface->w, surface->h, iWidth, iHeight, GetActualVideoModeParams().fDisplayAspectRatio, timer.GetDeltaTime() ); } RageFile out; if( !out.Open( sPath, RageFile::WRITE ) ) { LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() ); SAFE_DELETE( surface ); return false; } bool bSuccess = false; timer.Touch(); RString strError = ""; switch( format ) { case SAVE_LOSSLESS: bSuccess = RageSurfaceUtils::SaveBMP( surface, out ); break; case SAVE_LOSSLESS_SENSIBLE: bSuccess = RageSurfaceUtils::SavePNG( surface, out, strError ); break; case SAVE_LOSSY_LOW_QUAL: bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, false ); break; case SAVE_LOSSY_HIGH_QUAL: bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, true ); break; DEFAULT_FAIL( format ); } // LOG->Trace( "Saving Screenshot file took %f seconds.", timer.GetDeltaTime() ); SAFE_DELETE( surface ); if( !bSuccess ) { LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() ); return false; } return true; }
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 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 SongPosition::UpdateSongPosition( float fPositionSeconds, const TimingData &timing, const RageTimer ×tamp ) { if( !timestamp.IsZero() ) m_LastBeatUpdate = timestamp; else m_LastBeatUpdate.Touch(); TimingData::GetBeatArgs beat_info; beat_info.elapsed_time= fPositionSeconds; timing.GetBeatAndBPSFromElapsedTime(beat_info); m_fSongBeat= beat_info.beat; m_fCurBPS= beat_info.bps_out; m_bFreeze= beat_info.freeze_out; m_bDelay= beat_info.delay_out; m_iWarpBeginRow= beat_info.warp_begin_out; m_fWarpDestination= beat_info.warp_dest_out; // "Crash reason : -243478.890625 -48695.773438" // The question is why is -2000 used as the limit? -aj ASSERT_M( m_fSongBeat > -2000, ssprintf("Song beat %f at %f seconds is less than -2000!", m_fSongBeat, fPositionSeconds) ); m_fMusicSeconds = fPositionSeconds; m_fLightSongBeat = timing.GetBeatFromElapsedTime( fPositionSeconds + g_fLightsAheadSeconds ); m_fSongBeatNoOffset = timing.GetBeatFromElapsedTimeNoOffset( fPositionSeconds ); m_fMusicSecondsVisible = fPositionSeconds - g_fVisualDelaySeconds.Get(); beat_info.elapsed_time= m_fMusicSecondsVisible; timing.GetBeatAndBPSFromElapsedTime(beat_info); m_fSongBeatVisible= beat_info.beat; }
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 ); } }
ScreenUserPacks::ScreenUserPacks( CString sName ) : ScreenWithMenuElements( sName ) { m_bRestart = false; m_bPrompt = false; m_CurPlayer = PLAYER_INVALID; MEMCARDMAN->UnlockCards(); DrawTimer.SetZero(); }
void RageDisplay::ResetStats() { g_iFPS = g_iVPF = 0; g_iFramesRenderedSinceLastCheck = g_iFramesRenderedSinceLastReset = 0; g_iNumChecksSinceLastReset = 0; g_iVertsRenderedSinceLastCheck = 0; g_LastCheckTimer.GetDeltaTime(); }
bool UpdateXferProgress( uint64_t iBytesCurrent, uint64_t iBytesTotal ) { bool bInterrupt = false; FOREACH_EnabledPlayer(pn) { bInterrupt |= INPUTMAPPER->IsButtonDown( MenuInput(pn, MENU_BUTTON_SELECT) ); bInterrupt |= INPUTMAPPER->IsButtonDown(MenuInput(pn, MENU_BUTTON_LEFT)) && INPUTMAPPER->IsButtonDown(MenuInput(pn, MENU_BUTTON_RIGHT)); } if ( bInterrupt ) { InputEventArray throwaway; INPUTFILTER->GetInputEvents( throwaway ); return false; } // Draw() is very expensive: only do it on occasion. if( DrawTimer.Ago() < DRAW_UPDATE_TIME ) return true; /* this truncates to int, but that's okay for our purposes */ float iTransferRate = iBytesCurrent / g_UpdateDuration.Ago(); float fPercent = iBytesCurrent / (iBytesTotal/100); const CString sRate = FormatByteValue( iTransferRate ) + "/sec"; CString sMessage = ssprintf( "\n\n%s\n%.2f%% %s\n\n%s", USER_PACK_WAIT_TEXT.GetValue().c_str(), fPercent, sRate.c_str(), USER_PACK_CANCEL_TEXT.GetValue().c_str() ); SCREENMAN->OverlayMessage( sMessage ); SCREENMAN->Draw(); DrawTimer.Touch(); return true; }
void InputHandler_DInput::Update(float fDeltaTime) { RageTimer zero; zero.SetZero(); /* Handle polled devices. */ PollAndAcquireDevices(); for( unsigned i = 0; i < Devices.size(); ++i ) { if( !Devices[i].buffered ) UpdatePolled( Devices[i], zero ); else if( !InputThread.IsCreated() ) { /* If we have an input thread, it'll handle buffered devices. */ UpdateBuffered( Devices[i], zero ); } } InputHandler::UpdateTimer(); }
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() ); } } }
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(); } } }
/* 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 ScreenUserPacks::HandleScreenMessage( const ScreenMessage SM ) { #if 0 /* Not compatible with FileCopy callback system; was this ever used? -- vyhd */ if ( SM == SM_CancelTransfer ) { Dialog::OK("SM_CancelTransfer"); InterruptCopy(); InputEventArray throwaway; INPUTFILTER->GetInputEvents( throwaway ); LOG->Warn("Cancelled Transfer of user pack."); } #endif if ( SM == SM_LinkedMenuChange ) { m_pCurLOM = m_pCurLOM->SwitchToNextMenu(); return; } if ( SM == SM_ConfirmDeleteZip ) { SCREENMAN->Prompt( SM_AnswerConfirmDeleteZip, "Proceed to delete pack from machine?", PROMPT_YES_NO, ANSWER_NO ); } if ( SM == SM_AnswerConfirmDeleteZip ) { if (ScreenPrompt::s_LastAnswer == ANSWER_NO) return; CString sSelection = m_AddedZips.GetCurrentSelection(); g_CurSelection = sSelection; bool bSuccess = UPACKMAN->Remove( USER_PACK_SAVE_PATH + sSelection ); if (bSuccess) { m_SoundDelete.Play(); ReloadZips(); m_bRestart = true; } else { SCREENMAN->SystemMessage( "Failed to delete zip file from machine. Check your file permissions." ); } } if ( SM == SM_ConfirmAddZip ) { SCREENMAN->Prompt( SM_AnswerConfirmAddZip, "Proceed to add pack to machine?", PROMPT_YES_NO, ANSWER_NO ); } if ( SM == SM_AnswerConfirmAddZip ) { CString sError; m_bPrompt = false; if (ScreenPrompt::s_LastAnswer == ANSWER_NO) return; m_bStopThread = true; m_PlayerSongLoadThread.Wait(); MountMutex.Lock(); #if defined(LINUX) && defined(ITG_ARCADE) system( "mount -o remount,rw /itgdata" ); #endif MEMCARDMAN->LockCards(); MEMCARDMAN->MountCard(m_CurPlayer, 99999); CString sSelection = m_USBZips.GetCurrentSelection(); { //////////////////////// #define XFER_CLEANUP MEMCARDMAN->UnmountCard(m_CurPlayer); \ MEMCARDMAN->UnlockCards(); \ MountMutex.Unlock(); \ m_bStopThread = false; \ m_PlayerSongLoadThread.Create( InitSASSongThread, this ) //////////////////////// g_CurXferFile = MEM_CARD_MOUNT_POINT[m_CurPlayer] + "/" + USER_PACK_TRANSFER_PATH + sSelection; if ( !UPACKMAN->IsPackTransferable( sSelection, g_CurXferFile, sError ) || !UPACKMAN->IsPackMountable( g_CurXferFile, sError ) ) { SCREENMAN->SystemMessage( "Could not add pack to machine:\n" + sError ); XFER_CLEANUP; return; } sError = ""; // ?? RageTimer start; DrawTimer.Touch(); g_iLastCurrentBytes = 0; g_UpdateDuration.Touch(); if (!UPACKMAN->TransferPack( g_CurXferFile, sSelection, UpdateXferProgress, sError ) ) { SCREENMAN->SystemMessage( "Transfer error:\n" + sError ); XFER_CLEANUP; SCREENMAN->HideOverlayMessage(); SCREENMAN->ZeroNextUpdate(); return; } LOG->Debug( "Transferred %s in %f seconds.", g_CurXferFile.c_str(), start.Ago() ); } #if defined(LINUX) && defined(ITG_ARCADE) sync(); system( "mount -o remount,ro /itgdata" ); #endif SCREENMAN->HideOverlayMessage(); SCREENMAN->ZeroNextUpdate(); FILEMAN->FlushDirCache(USER_PACK_SAVE_PATH); m_bRestart = true; m_SoundTransferDone.Play(); ReloadZips(); XFER_CLEANUP; #undef XFER_CLEANUP } switch( SM ) { case SM_GoToNextScreen: case SM_GoToPrevScreen: SCREENMAN->SetNewScreen( PREV_SCREEN ); break; } }
void MemoryCardDriverThreaded_Linux::GetUSBStorageDevices( vector<UsbStorageDevice>& vDevicesOut ) { LOG->Trace( "GetUSBStorageDevices" ); vDevicesOut.clear(); { vector<RString> asDevices; RString sBlockDevicePath = "/sys/block/"; GetFileList( sBlockDevicePath, asDevices ); for( unsigned i = 0; i < asDevices.size(); ++i ) { const RString &sDevice = asDevices[i]; if( sDevice == "." || sDevice == ".." ) continue; UsbStorageDevice usbd; RString sPath = sBlockDevicePath + sDevice + "/"; usbd.sSysPath = sPath; /* Ignore non-removable devices. */ RString sBuf; if( !ReadFile( sPath + "removable", sBuf ) ) continue; // already warned if( atoi(sBuf) != 1 ) continue; /* * The kernel isn't exposing all of /sys atomically, so we end up missing * the partition due to it not being shown yet. It won't show up until the * kernel has scanned the partition table, which can take a variable amount * of time, sometimes over a second. Watch for the "queue" sysfs directory, * which is created after this, to tell when partition directories are created. */ RageTimer WaitUntil; WaitUntil += 5; RString sQueueFilePath = usbd.sSysPath + "queue"; while(1) { if( WaitUntil.Ago() >= 0 ) { LOG->Warn( "Timed out waiting for %s", sQueueFilePath.c_str() ); break; } if( access(usbd.sSysPath, F_OK) == -1 ) { LOG->Warn( "Block directory %s went away while we were waiting for %s", usbd.sSysPath.c_str(), sQueueFilePath.c_str() ); break; } if( access(sQueueFilePath, F_OK) != -1 ) break; usleep(10000); } /* Wait for udev to finish handling device node creation */ ExecuteCommand( "udevadm settle" ); /* If the first partition device exists, eg. /sys/block/uba/uba1, use it. */ if( access(usbd.sSysPath + sDevice + "1", F_OK) != -1 ) { LOG->Trace("OK"); usbd.sDevice = "/dev/" + sDevice + "1"; } else { LOG->Trace("error %s", strerror(errno)); usbd.sDevice = "/dev/" + sDevice; } /* * sPath/device should be a symlink to the actual device. For USB * devices, it looks like this: * * device -> ../../devices/pci0000:00/0000:00:02.1/usb2/2-1/2-1:1.0 * * "2-1" is "bus-port". */ char szLink[256]; int iRet = readlink( sPath + "device", szLink, sizeof(szLink) ); if( iRet == -1 ) { LOG->Warn( "readlink(\"%s\"): %s", (sPath + "device").c_str(), strerror(errno) ); } else { /* * The full path looks like * * ../../devices/pci0000:00/0000:00:02.1/usb2/2-2/2-2.1/2-2.1:1.0 * * In newer kernels, it looks like: * * ../../../3-2.1:1.0 * * Each path element refers to a new hop in the chain. * "usb2" = second USB host * 2- second USB host, * -2 port 1 on the host, * .1 port 1 on an attached hub * .2 ... port 2 on the next hub ... * * We want the bus number and the port of the last hop. The level is * the number of hops. */ szLink[iRet] = 0; vector<RString> asBits; split( szLink, "/", asBits ); RString sHostPort = asBits[asBits.size()-1]; if( !sHostPort.empty() ) { /* Strip off the endpoint information after the colon. */ size_t pos = sHostPort.find(':'); if( pos != string::npos ) sHostPort.erase( pos ); /* sHostPort is eg. 2-2.1. */ sHostPort.Replace( "-", "." ); asBits.clear(); split( sHostPort, ".", asBits ); if( asBits.size() > 1 ) { usbd.iBus = atoi( asBits[0] ); usbd.iPort = atoi( asBits[asBits.size()-1] ); usbd.iLevel = asBits.size() - 1; } } } if( ReadFile( sPath + "device/../idVendor", sBuf ) ) sscanf( sBuf, "%x", &usbd.idVendor ); if( ReadFile( sPath + "device/../idProduct", sBuf ) ) sscanf( sBuf, "%x", &usbd.idProduct ); if( ReadFile( sPath + "device/../serial", sBuf ) ) { usbd.sSerial = sBuf; TrimRight( usbd.sSerial ); } if( ReadFile( sPath + "device/../product", sBuf ) ) { usbd.sProduct = sBuf; TrimRight( usbd.sProduct ); } if( ReadFile( sPath + "device/../manufacturer", sBuf ) ) { usbd.sVendor = sBuf; TrimRight( usbd.sVendor ); } vDevicesOut.push_back( usbd ); } } { // Find where each device is mounted. Output looks like: // /dev/sda1 /mnt/flash1 auto noauto,owner 0 0 // /dev/sdb1 /mnt/flash2 auto noauto,owner 0 0 // /dev/sdc1 /mnt/flash3 auto noauto,owner 0 0 RString fn = "/rootfs/etc/fstab"; RageFile f; if( !f.Open(fn) ) { LOG->Warn( "can't open '%s': %s", fn.c_str(), f.GetError().c_str() ); return; } RString sLine; while( !f.AtEOF() ) { switch( f.GetLine(sLine) ) { case 0: continue; /* eof */ case -1: LOG->Warn( "error reading '%s': %s", fn.c_str(), f.GetError().c_str() ); return; } char szScsiDevice[1024]; char szMountPoint[1024]; int iRet = sscanf( sLine, "%s %s", szScsiDevice, szMountPoint ); if( iRet != 2 || szScsiDevice[0] == '#') continue; // don't process this line /* Get the real kernel device name, which should match * the name from /sys/block, by following symlinks in * /dev. This allows us to specify persistent names in * /etc/fstab using things like /dev/device/by-path. */ char szUnderlyingDevice[PATH_MAX]; if( realpath(szScsiDevice, szUnderlyingDevice) == NULL ) { // "No such file or directory" is understandable if (errno != ENOENT) LOG->Warn( "realpath(\"%s\"): %s", szScsiDevice, strerror(errno) ); continue; } RString sMountPoint = szMountPoint; TrimLeft( sMountPoint ); TrimRight( sMountPoint ); // search for the mountpoint corresponding to the device for( unsigned i=0; i<vDevicesOut.size(); i++ ) { UsbStorageDevice& usbd = vDevicesOut[i]; if( usbd.sDevice == szUnderlyingDevice ) // found our match { // Use the device entry from fstab so the mount command works usbd.sDevice = szScsiDevice; usbd.sOsMountDir = sMountPoint; break; // stop looking for a match } } } } for( unsigned i=0; i<vDevicesOut.size(); i++ ) { UsbStorageDevice& usbd = vDevicesOut[i]; LOG->Trace( " sDevice: %s, iBus: %d, iLevel: %d, iPort: %d, id: %04X:%04X, Vendor: '%s', Product: '%s', sSerial: \"%s\", sOsMountDir: %s", usbd.sDevice.c_str(), usbd.iBus, usbd.iLevel, usbd.iPort, usbd.idVendor, usbd.idProduct, usbd.sVendor.c_str(), usbd.sProduct.c_str(), usbd.sSerial.c_str(), usbd.sOsMountDir.c_str() ); } /* Remove any devices that we couldn't find a mountpoint for. */ for( unsigned i=0; i<vDevicesOut.size(); i++ ) { UsbStorageDevice& usbd = vDevicesOut[i]; if( usbd.sOsMountDir.empty() ) { LOG->Trace( "Ignoring %s (couldn't find in /etc/fstab)", usbd.sDevice.c_str() ); vDevicesOut.erase( vDevicesOut.begin()+i ); --i; } } LOG->Trace( "Done with GetUSBStorageDevices" ); }
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 ); }
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(); }