700 lines
22 KiB
C++
700 lines
22 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE : ReplayPlaybackController.cpp
|
|
// PURPOSE : Implementation of Playback controller interface, to feed data to replay system.
|
|
//
|
|
// AUTHOR : james.strain
|
|
//
|
|
// Copyright (C) 1999-2014 Rockstar Games. All Rights Reserved.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
#include "ReplayPlaybackController.h"
|
|
|
|
#if GTA_REPLAY
|
|
|
|
// rage
|
|
#include "math/amath.h"
|
|
|
|
// framework
|
|
#include "video/IVideoRecordingDataAdapter.h"
|
|
|
|
// game
|
|
#include "audio/frontendaudioentity.h"
|
|
#include "audio/music/musicplayer.h"
|
|
#include "audio/replayaudioentity.h"
|
|
#include "camera/replay/ReplayDirector.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "control/replay/replay.h"
|
|
#include "control/replay/ReplayMarkerContext.h"
|
|
#include "replaycoordinator/ReplayCoordinator.h"
|
|
#include "replaycoordinator/storage/Montage.h"
|
|
|
|
|
|
REPLAY_COORDINATOR_OPTIMISATIONS();
|
|
|
|
CReplayPlaybackController::CReplayPlaybackController()
|
|
: m_activeMontage( NULL )
|
|
, m_activeClip( INDEX_NONE )
|
|
, m_pendingJumpClip( INDEX_NONE )
|
|
, m_pendingJumpClipNonDilatedTimeMs( INDEX_NONE )
|
|
, m_singleClip( true )
|
|
, m_recordingDataProvider( NULL )
|
|
, m_startClipNextFrame( 0 )
|
|
{
|
|
|
|
}
|
|
|
|
void CReplayPlaybackController::Initialize( CMontage& montage, s32 const activeClip, bool const singleClipMode,
|
|
IVideoRecordingDataProvider const * const exportMode )
|
|
{
|
|
CReplayMarkerContext::Reset();
|
|
m_activeMontage = &montage;
|
|
m_singleClip = singleClipMode;
|
|
m_recordingDataProvider = exportMode;
|
|
SetActiveClip( activeClip );
|
|
ClearPendingJumpClipNonDilatedTimeMs();
|
|
m_possibleFirstClipOfProject = true;
|
|
}
|
|
|
|
void CReplayPlaybackController::Reset()
|
|
{
|
|
CReplayMarkerContext::Reset();
|
|
SetActiveClip( INDEX_NONE );
|
|
m_activeMontage = NULL;
|
|
m_singleClip = true;
|
|
m_recordingDataProvider = NULL;
|
|
ClearPendingJumpClipNonDilatedTimeMs();
|
|
m_possibleFirstClipOfProject = false;
|
|
}
|
|
|
|
char const * CReplayPlaybackController::GetProjectName() const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetName() : "";
|
|
}
|
|
|
|
IReplayMarkerStorage* CReplayPlaybackController::TryGetCurrentMarkerStorage()
|
|
{
|
|
return IsValid() ? (IReplayMarkerStorage*)GetCurrentClip() : NULL;
|
|
}
|
|
|
|
IReplayMarkerStorage const* CReplayPlaybackController::TryGetCurrentMarkerStorage() const
|
|
{
|
|
return IsValid() ? (IReplayMarkerStorage*)GetCurrentClip() : NULL;
|
|
}
|
|
|
|
void CReplayPlaybackController::UpdateMarkersForPlayback( float const clipTimeMs )
|
|
{
|
|
IReplayMarkerStorage* markerStorage = TryGetCurrentMarkerStorage();
|
|
if( markerStorage == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
sReplayMarkerInfo* pCurrentMarker = NULL;
|
|
sReplayMarkerInfo* pNextMarker = NULL;
|
|
|
|
// Reset current marker index.
|
|
CReplayMarkerContext::SetCurrentMarkerIndex( -1 );
|
|
|
|
int const c_markerCount = markerStorage->GetMarkerCount();
|
|
if( c_markerCount == 0 )
|
|
{
|
|
CReplayMgr::SetMarkerSpeed( 1.f );
|
|
}
|
|
|
|
for( int i = 0; i < c_markerCount; ++i )
|
|
{
|
|
pCurrentMarker = markerStorage->GetMarker( i );
|
|
pNextMarker = markerStorage->TryGetMarker( i + 1 );
|
|
|
|
g_ReplayAudioEntity.SetMarkerSFXValid(i, true);
|
|
if(pCurrentMarker)
|
|
{
|
|
if(pCurrentMarker->GetSfxOSVolume() == 0)
|
|
{
|
|
g_ReplayAudioEntity.SetMarkerSFXValid(i, false);
|
|
}
|
|
else if(pNextMarker)
|
|
{
|
|
static float minTimeBetweenSFXMarkers = 450.0f;
|
|
f32 currentMarkerTime = ConvertNonDilatedTimeToTimeMs(GetCurrentClipIndex(), pCurrentMarker->GetNonDilatedTimeMs());
|
|
f32 nextMarkerTime = ConvertNonDilatedTimeToTimeMs(GetCurrentClipIndex(), pNextMarker->GetNonDilatedTimeMs());
|
|
if(pNextMarker->GetSfxHash() != g_NullSoundHash && ((nextMarkerTime - currentMarkerTime) < minTimeBetweenSFXMarkers) )
|
|
{
|
|
g_ReplayAudioEntity.SetMarkerSFXValid(i, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( pCurrentMarker && clipTimeMs >= pCurrentMarker->GetNonDilatedTimeMs() &&
|
|
( !pNextMarker || ( pNextMarker && clipTimeMs < pNextMarker->GetNonDilatedTimeMs() ) ) )
|
|
{
|
|
CReplayMarkerContext::SetCurrentMarkerIndex( i );
|
|
|
|
//Set Speed.
|
|
if ( !pCurrentMarker->IsAnchor() && pCurrentMarker != markerStorage->GetLastMarker())
|
|
{
|
|
eReplayMicType const c_micType = GetMicrophoneType( (eMarkerMicrophoneType)pCurrentMarker->GetMicrophoneType() );
|
|
naMicrophones::SetReplayMicType( (u8)c_micType );
|
|
|
|
if( CReplayMgr::IsPlaying() )
|
|
{
|
|
CReplayMgr::SetMarkerSpeed( pCurrentMarker->GetSpeedValueFloat() );
|
|
}
|
|
|
|
// Need to update volumes even whilst not playing so that we start playback on the correct volume
|
|
g_FrontendAudioEntity.SetClipSpeechOn( pCurrentMarker->IsSpeechOn() );
|
|
if(!g_ReplayAudioEntity.ShouldDuckMusic()) // once we're ducking only the play marker of the current SFX can control the music volume
|
|
{
|
|
g_FrontendAudioEntity.SetClipMusicVolumeIndex( pCurrentMarker->GetMusicVolume() );
|
|
}
|
|
g_FrontendAudioEntity.SetClipSFXVolumeIndex( pCurrentMarker->GetSfxVolume() );
|
|
g_FrontendAudioEntity.SetClipDialogVolumeIndex( pCurrentMarker->GetDialogVolume() );
|
|
g_FrontendAudioEntity.SetClipCustomAmbienceVolumeIndex( pCurrentMarker->GetAmbientVolume() );
|
|
if(pCurrentMarker->GetSfxHash() != g_NullSoundHash && g_ReplayAudioEntity.IsMarkerSFXValid(i))
|
|
{
|
|
g_FrontendAudioEntity.SetClipSFXOneshotVolumeIndex( pCurrentMarker->GetSfxOSVolume() );
|
|
}
|
|
|
|
float timeSinceIntensitySwitch = 0;
|
|
bool switchMoodsInstantly = false;
|
|
bool isDoingFullPlayback = CReplayCoordinator::IsExportingToVideoFile() || CReplayCoordinator::IsPreviewingVideo();
|
|
|
|
// If this is the very first clip, or we're just editing a single clip, or we're editing a single clip and we've paused,
|
|
// then switch moods instantly
|
|
if((pCurrentMarker == markerStorage->GetFirstMarker() && GetCurrentClipIndex() == 0) ||
|
|
(!isDoingFullPlayback && !CReplayMgr::IsPlaying()))
|
|
{
|
|
switchMoodsInstantly = true;
|
|
}
|
|
|
|
eReplayMarkerAudioIntensity prevReplayMarkerIntensity = MARKER_AUDIO_INTENSITY_MAX;
|
|
|
|
if(IsValid() && !isDoingFullPlayback)
|
|
{
|
|
// Need to find the previous music intensity marke so that we can correctly reproduce any
|
|
// intensity crossfade behaviour if playback resumes mid-crossfade
|
|
for( int prevMarkerIndex = i - 1; prevMarkerIndex >= 0; prevMarkerIndex-- )
|
|
{
|
|
sReplayMarkerInfo* pPrevMarker = markerStorage->GetMarker( prevMarkerIndex );
|
|
|
|
if(pPrevMarker && pPrevMarker->GetScoreIntensity() != pCurrentMarker->GetScoreIntensity())
|
|
{
|
|
prevReplayMarkerIntensity = eReplayMarkerAudioIntensity( pPrevMarker->GetScoreIntensity() );
|
|
CClip* clip = m_activeMontage->GetClip(m_activeClip);
|
|
float currentTimeMs = CReplayMgr::GetCurrentTimeRelativeMsFloat();
|
|
float prevMarkerTimeMs = clip->ConvertNonDilatedTimeMsToTimeMs( pCurrentMarker->GetNonDilatedTimeMs() );
|
|
timeSinceIntensitySwitch = currentTimeMs - prevMarkerTimeMs;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_InteractiveMusicManager.SetReplayScoreIntensity( eReplayMarkerAudioIntensity( pCurrentMarker->GetScoreIntensity() ), prevReplayMarkerIntensity, switchMoodsInstantly, (u32)timeSinceIntensitySwitch );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pCurrentMarker == markerStorage->GetFirstMarker() && clipTimeMs < pCurrentMarker->GetNonDilatedTimeMs() )
|
|
{
|
|
|
|
g_FrontendAudioEntity.SetClipSpeechOn(g_FrontendAudioEntity.GetSpeechOn()); // if we are before a marker then use the global setting.
|
|
g_FrontendAudioEntity.SetClipMusicVolumeIndex(8);
|
|
g_FrontendAudioEntity.SetClipSFXVolumeIndex(8);
|
|
g_FrontendAudioEntity.SetClipDialogVolumeIndex(8);
|
|
g_FrontendAudioEntity.SetClipCustomAmbienceVolumeIndex(8);
|
|
g_FrontendAudioEntity.SetClipSFXOneshotVolumeIndex(8);
|
|
CReplayMgr::SetMarkerSpeed( 1.f );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
g_ReplayAudioEntity.SetSfxVolumeAffectingMusic(g_SilenceVolume); // base line is no sfx volume so no duck
|
|
// replay marker one shot sfx
|
|
if(c_markerCount > 1)// && (CReplayMgr::IsPreCachingScene() || CReplayMgr::IsJustPlaying()))
|
|
{
|
|
g_ReplayAudioEntity.SetShouldDuckMusic(false);
|
|
g_ReplayAudioEntity.InvalidateSFX();
|
|
bool foundPlayCandidate = false;
|
|
for( int i = c_markerCount-1; i >= 0; i-- )
|
|
{
|
|
pCurrentMarker = markerStorage->GetMarker( i );
|
|
|
|
if(!pCurrentMarker->IsAnchor())
|
|
{
|
|
u32 const c_sfxHash = pCurrentMarker->GetSfxHash();
|
|
u32 sfxDuration = c_sfxHash != g_NullSoundHash ? CReplayAudioTrackProvider::GetMusicTrackDurationMs(c_sfxHash) : 0;
|
|
|
|
// Assuming that the SFX doesn't get pitched up/down by replay marker speeds, the duration is measured in real (ie. dilated) time.
|
|
// We need to know the end time in non dilated time so that we can compare it to the current playback head.
|
|
f32 const sfxEndTimeMs = ConvertTimeToNonDilatedTimeMs(GetCurrentClipIndex(), ConvertNonDilatedTimeToTimeMs(GetCurrentClipIndex(), pCurrentMarker->GetNonDilatedTimeMs()) + sfxDuration);
|
|
if(pCurrentMarker && c_sfxHash != g_NullSoundHash && g_ReplayAudioEntity.IsMarkerSFXValid(i))
|
|
{
|
|
if(!foundPlayCandidate && clipTimeMs >= pCurrentMarker->GetNonDilatedTimeMs() - REPLAY_SFX_PRELAOD_MS && clipTimeMs < sfxEndTimeMs)
|
|
{
|
|
u32 startOffset = 0;
|
|
if(clipTimeMs > pCurrentMarker->GetNonDilatedTimeMs())
|
|
{
|
|
// As above, assuming that SFX doesn't get pitched up or down, we need to calculate the offset based on the real (ie. dilated) time since the marker was hit
|
|
startOffset = (u32)(ConvertNonDilatedTimeToTimeMs(GetCurrentClipIndex(), clipTimeMs) - ConvertNonDilatedTimeToTimeMs(GetCurrentClipIndex(), pCurrentMarker->GetNonDilatedTimeMs()));
|
|
}
|
|
if(startOffset < sfxDuration)
|
|
{
|
|
g_ReplayAudioEntity.Preload(i, c_sfxHash, REPLAY_STREAM_SFX, startOffset);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_ReplayAudioEntity.Stop(i, c_sfxHash, true);
|
|
}
|
|
if(!foundPlayCandidate && clipTimeMs >= pCurrentMarker->GetNonDilatedTimeMs())
|
|
{
|
|
foundPlayCandidate = true;
|
|
}
|
|
|
|
if(clipTimeMs >= pCurrentMarker->GetNonDilatedTimeMs() - 1000 && clipTimeMs < sfxEndTimeMs && g_ReplayAudioEntity.IsLoadedOrPlaying(i, c_sfxHash))
|
|
{
|
|
//should duck music
|
|
f32 sfxVolume = g_FrontendAudioEntity.VolumeIndexToDb(pCurrentMarker->GetSfxOSVolume());
|
|
f32 sfxVolumeAffectingMusic = g_ReplayAudioEntity.GetSFXVolumeAffectingMusic();
|
|
if(sfxVolume > sfxVolumeAffectingMusic) // music is affected by the loudest SFX volume within the window
|
|
{
|
|
g_ReplayAudioEntity.SetSfxVolumeAffectingMusic(sfxVolume);
|
|
}
|
|
g_ReplayAudioEntity.SetShouldDuckMusic(true);
|
|
}
|
|
}
|
|
|
|
if(CReplayMgr::IsJustPlaying() && !CReplayMgr::IsSettingUp() && !CReplayMgr::IsPreCachingScene())
|
|
{
|
|
if(c_sfxHash != g_NullSoundHash && clipTimeMs >= pCurrentMarker->GetNonDilatedTimeMs() && clipTimeMs < sfxEndTimeMs && g_ReplayAudioEntity.IsReadyToPlay(i, c_sfxHash))
|
|
{
|
|
Displayf("Play SFX on marker %d", i);
|
|
// stop any playing sfx before we start a new one
|
|
g_ReplayAudioEntity.StopAllPlayingSFX();
|
|
g_ReplayAudioEntity.Play(i, c_sfxHash);
|
|
g_FrontendAudioEntity.SetClipMusicVolumeIndex( pCurrentMarker->GetMusicVolume() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
g_ReplayAudioEntity.ClearInvalidSFX();
|
|
}
|
|
|
|
s32 currIndex = CReplayMarkerContext::GetCurrentMarkerIndex();
|
|
CReplayMarkerContext::SetPreviousActiveMarkerIndex( currIndex );
|
|
}
|
|
|
|
void CReplayPlaybackController::UpdateMontageAudioFadeOut()
|
|
{
|
|
const f32 currentTimeMs = CReplayMgr::GetCurrentTimeRelativeMsFloat();
|
|
f32 sfxVolLinear = 1.0f;
|
|
|
|
if(IsValid())
|
|
{
|
|
f32 fraction = 0.0f;
|
|
ReplayMarkerTransitionType transition;
|
|
u32 clipIndex = GetCurrentClipIndex();
|
|
clipIndex = CReplayCoordinator::IsPendingNextClip() && clipIndex > 0 ? clipIndex - 1 : clipIndex;
|
|
m_activeMontage->GetActiveTransition(clipIndex, currentTimeMs, transition, fraction);
|
|
|
|
if(transition != MARKER_TRANSITIONTYPE_MAX &&
|
|
transition != MARKER_TRANSITIONTYPE_NONE)
|
|
{
|
|
sfxVolLinear = 1.0f - fraction;
|
|
}
|
|
}
|
|
|
|
g_FrontendAudioEntity.SetClipFadeVolLinear(sfxVolLinear);
|
|
}
|
|
|
|
CClip const* CReplayPlaybackController::GetClip( s32 clipIndex ) const
|
|
{
|
|
clipIndex = clipIndex < 0 ? m_activeClip : clipIndex;
|
|
return IsValid() ? m_activeMontage->GetClip( clipIndex ) : NULL;
|
|
}
|
|
|
|
bool CReplayPlaybackController::IsExportingToVideoFile() const
|
|
{
|
|
return m_recordingDataProvider && m_recordingDataProvider->IsRecordingVideo();
|
|
}
|
|
|
|
bool CReplayPlaybackController::IsExportingPaused() const
|
|
{
|
|
return m_recordingDataProvider && m_recordingDataProvider->IsRecordingPaused();
|
|
}
|
|
|
|
bool CReplayPlaybackController::IsExportReadyForReplay() const
|
|
{
|
|
return m_recordingDataProvider && m_recordingDataProvider->IsRecordingReadyForSamples();
|
|
}
|
|
|
|
bool CReplayPlaybackController::IsExportingAudioFrameCaptureAllowed() const
|
|
{
|
|
return m_recordingDataProvider && m_recordingDataProvider->IsAudioFrameCaptureAllowed();
|
|
}
|
|
|
|
bool CReplayPlaybackController::HasExportErrored() const
|
|
{
|
|
return m_recordingDataProvider && m_recordingDataProvider->HasRecordingErrored();
|
|
}
|
|
|
|
float CReplayPlaybackController::GetExportFrameDurationMs() const
|
|
{
|
|
return m_recordingDataProvider ? m_recordingDataProvider->GetExportFrameDurationMs() : 33.f;
|
|
}
|
|
|
|
u64 CReplayPlaybackController::GetCurrentRawClipOwnerId() const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetClip( m_activeClip )->GetOwnerId() : 0;
|
|
}
|
|
|
|
char const * CReplayPlaybackController::GetCurrentRawClipFileName() const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetClip( m_activeClip )->GetName() : NULL;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetCurrentClipIndex() const
|
|
{
|
|
return IsValid() ? m_activeClip : -1;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetTotalClipCount() const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetClipCount() : -1;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetClipStartNonDilatedTimeMs( s32 const clipIndex ) const
|
|
{
|
|
CClip const * activeClip = GetClip( clipIndex );
|
|
return activeClip ? activeClip->GetStartNonDilatedTimeMs() : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetClipNonDilatedEndTimeMs( s32 const clipIndex ) const
|
|
{
|
|
CClip const * activeClip = GetClip( clipIndex );
|
|
return activeClip ? activeClip->GetEndNonDilatedTimeMs() : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetClipNonDilatedDurationMs( s32 const clipIndex ) const
|
|
{
|
|
CClip const * activeClip = GetClip( clipIndex );
|
|
return activeClip ? activeClip->GetRawDurationMs() : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetClipRawEndTimeMs( s32 const clipIndex ) const
|
|
{
|
|
CClip const * activeClip = GetClip( clipIndex );
|
|
return activeClip ? activeClip->GetRawDurationMs() : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetClipTrimmedTimeMs( s32 const clipIndex ) const
|
|
{
|
|
CClip const * activeClip = GetClip( clipIndex );
|
|
return activeClip ? activeClip->GetTrimmedTimeMs() : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetLengthTimeToClipMs( s32 const clipIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetTrimmedTimeToClipMs( clipIndex ) : 0;
|
|
}
|
|
|
|
u64 CReplayPlaybackController::GetLengthTimeToClipNs( s32 const clipIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetTrimmedTimeToClipNs( clipIndex ) : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetLengthNonDilatedTimeToClipMs( s32 const clipIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetTrimmedNonDilatedTimeToClipMs( clipIndex ) : 0;
|
|
}
|
|
|
|
float CReplayPlaybackController::ConvertNonDilatedTimeToTimeMs( s32 const clipIndex, float const currentTimeMs ) const
|
|
{
|
|
float realTimeMs = (float)GetLengthTimeToClipMs( clipIndex );
|
|
|
|
CClip const* activeClip = GetClip( clipIndex );
|
|
if( activeClip )
|
|
{
|
|
realTimeMs += activeClip->ConvertNonDilatedTimeMsToTimeMs( currentTimeMs );
|
|
}
|
|
|
|
return realTimeMs;
|
|
}
|
|
|
|
float CReplayPlaybackController::ConvertTimeToNonDilatedTimeMs( s32 const clipIndex, float const currentTimeMS ) const
|
|
{
|
|
float nonDilatedTimeMS = GetLengthNonDilatedTimeToClipMs( clipIndex );
|
|
|
|
CClip const* activeClip = GetClip( clipIndex );
|
|
if( activeClip )
|
|
{
|
|
nonDilatedTimeMS += activeClip->ConvertTimeMsToNonDilatedTimeMs( currentTimeMS );
|
|
}
|
|
|
|
return nonDilatedTimeMS;
|
|
}
|
|
|
|
float CReplayPlaybackController::ConvertMusicBeatTimeToNonDilatedTimeMs( s32 const clipIndex, float const beatTimeMs ) const
|
|
{
|
|
float clipBeatTime = -1.f;
|
|
|
|
if( IsValid() )
|
|
{
|
|
s32 beatClipIndex = -1;
|
|
|
|
// Given a montage-relative music beat time, convert this to an offset within the given clip
|
|
m_activeMontage->ConvertMusicBeatTimeToNonDilatedTimeMs( beatTimeMs, beatClipIndex, clipBeatTime );
|
|
|
|
// Check that the beat is actually within the selected clip
|
|
clipBeatTime = (beatClipIndex == clipIndex) ? clipBeatTime : -1.f;
|
|
}
|
|
|
|
return clipBeatTime;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetMusicIndexAtCurrentNonDilatedTimeMs( s32 const clipIndex, float const currentTimeMs ) const
|
|
{
|
|
s32 const index = IsValid() ? m_activeMontage->GetActiveMusicIndex( clipIndex, currentTimeMs ) : -1;
|
|
return index;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetNextMusicIndexFromCurrentNonDilatedTimeMs( s32 const clipIndex, float const currentTimeMs ) const
|
|
{
|
|
s32 const index = IsValid() ? m_activeMontage->GetNextActiveMusicIndex( clipIndex, currentTimeMs ) : -1;
|
|
return index;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetPreviousMusicIndexFromCurrentNonDilatedTimeMs( s32 const clipIndex, float const currentTimeMs ) const
|
|
{
|
|
s32 const index = IsValid() ? m_activeMontage->GetPreviousActiveMusicIndex( clipIndex, currentTimeMs ) : -1;
|
|
return index;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetMusicStartTimeMs( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetMusicStartTimeMs( musicIndex ) : -1;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetMusicStartOffsetMs( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetMusicStartOffsetMs( musicIndex ) : -1;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetMusicDurationMs( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetMusicDurationMs( musicIndex ) : -1;
|
|
}
|
|
|
|
u32 CReplayPlaybackController::GetMusicSoundHash( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetMusicClipSoundHash( musicIndex ) : 0;
|
|
}
|
|
|
|
bool CReplayPlaybackController::PauseMusicOnClipEnd() const
|
|
{
|
|
return IsValid() && !CReplayCoordinator::IsRenderingVideo();
|
|
}
|
|
|
|
//Ambient track
|
|
|
|
s32 CReplayPlaybackController::GetAmbientTrackIndexAtCurrentNonDilatedTimeMs( s32 const clipIndex, float const currentTimeMs ) const
|
|
{
|
|
s32 const index = IsValid() ? m_activeMontage->GetActiveAmbientTrackIndex( clipIndex, currentTimeMs ) : -1;
|
|
return index;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetAmbientTrackStartTimeMs( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetAmbientStartTimeMs( musicIndex ) : -1;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetAmbientTrackStartOffsetMs( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetAmbientStartOffsetMs( musicIndex ) : -1;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetAmbientTrackDurationMs( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetAmbientDurationMs( musicIndex ) : -1;
|
|
}
|
|
|
|
u32 CReplayPlaybackController::GetAmbientTrackSoundHash( u32 const musicIndex ) const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetAmbientClipSoundHash( musicIndex ) : 0;
|
|
}
|
|
|
|
bool CReplayPlaybackController::CanUpdateGameTimer() const
|
|
{
|
|
return IsValid();
|
|
}
|
|
|
|
float CReplayPlaybackController::GetTotalPlaybackTimeMs() const
|
|
{
|
|
return IsValid() ? m_activeMontage->GetTotalClipLengthTimeMs() : 0;
|
|
}
|
|
|
|
void CReplayPlaybackController::JumpToClip( s32 const clipIndex, float const clipNonDilatedTimeMs )
|
|
{
|
|
m_pendingJumpClip = clipIndex;
|
|
m_pendingJumpClipNonDilatedTimeMs = clipNonDilatedTimeMs;
|
|
}
|
|
|
|
s32 CReplayPlaybackController::GetPendingJumpClip() const
|
|
{
|
|
return m_pendingJumpClip;
|
|
}
|
|
|
|
float CReplayPlaybackController::GetPendingJumpClipNonDilatedTimeMs() const
|
|
{
|
|
return m_pendingJumpClipNonDilatedTimeMs;
|
|
}
|
|
|
|
void CReplayPlaybackController::ClearPendingJumpClipNonDilatedTimeMs()
|
|
{
|
|
m_pendingJumpClipNonDilatedTimeMs = INDEX_NONE;
|
|
}
|
|
|
|
bool CReplayPlaybackController::IsPendingClipJump() const
|
|
{
|
|
return GetPendingJumpClip() != INDEX_NONE;
|
|
}
|
|
|
|
bool CReplayPlaybackController::OnClipChanged( s32 const clipIndex )
|
|
{
|
|
bool success = false;
|
|
|
|
if( GetClip( clipIndex ) != NULL )
|
|
{
|
|
if( clipIndex != m_activeClip )
|
|
{
|
|
OnCurrentClipFinished();
|
|
}
|
|
|
|
SetActiveClip( clipIndex );
|
|
success = true;
|
|
}
|
|
|
|
m_pendingJumpClip = INDEX_NONE;
|
|
return success;
|
|
}
|
|
|
|
void CReplayPlaybackController::OnCurrentClipFinished()
|
|
{
|
|
|
|
}
|
|
|
|
void CReplayPlaybackController::OnPlaybackFinished()
|
|
{
|
|
if( CReplayCoordinator::IsRenderingVideo() )
|
|
{
|
|
CReplayCoordinator::KillPlaybackOrBake( false );
|
|
}
|
|
}
|
|
|
|
void CReplayPlaybackController::OnClipPlaybackStart()
|
|
{
|
|
//ensure the correct playback states for the various modes are set correctly
|
|
if( CReplayCoordinator::IsRenderingVideo() )
|
|
{
|
|
#if USE_SRLS_IN_REPLAY
|
|
if ( CReplayMgr::IsCreatingSRL())
|
|
{
|
|
CReplayMgr::SetNextPlayBackState(REPLAY_STATE_PLAY | REPLAY_DIRECTION_FWD);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
CReplayMgr::SetNextPlayBackState(REPLAY_STATE_PAUSE);
|
|
m_startClipNextFrame = true;
|
|
}
|
|
}
|
|
else if(CReplayCoordinator::IsPreviewingClip())
|
|
{
|
|
CReplayMgr::SetNextPlayBackState(REPLAY_STATE_PAUSE);
|
|
}
|
|
else if( CReplayCoordinator::IsPreviewingVideo())
|
|
{
|
|
if(m_possibleFirstClipOfProject && m_activeClip == 0)
|
|
{
|
|
CReplayMgr::SetNextPlayBackState(REPLAY_STATE_PAUSE);
|
|
m_startClipNextFrame = true;
|
|
}
|
|
|
|
m_possibleFirstClipOfProject = false;
|
|
}
|
|
}
|
|
|
|
void CReplayPlaybackController::OnClipPlaybackEnd()
|
|
{
|
|
if( CReplayCoordinator::IsRenderingVideo() )
|
|
{
|
|
audNorthAudioEngine::PumpReplayAudio();
|
|
CReplayCoordinator::PauseBake();
|
|
}
|
|
}
|
|
|
|
void CReplayPlaybackController::OnClipPlaybackUpdate()
|
|
{
|
|
// should only get called when rendering a video, so don't bother doing another test
|
|
if( m_startClipNextFrame )
|
|
{
|
|
// playback resumed a frame later, to give camera an opportunity to get set up
|
|
// perhaps tie into camera returning back it's ready to go, but overkill???
|
|
CReplayMgr::ResumeNormalPlayback();
|
|
CReplayCoordinator::ResumeBake();
|
|
m_startClipNextFrame = false;
|
|
}
|
|
}
|
|
|
|
void CReplayPlaybackController::SetActiveClip( s32 const activeClip )
|
|
{
|
|
m_activeClip = activeClip;
|
|
CReplayMarkerContext::SetCurrentMarkerStorage( TryGetCurrentMarkerStorage() );
|
|
|
|
camInterface::GetReplayDirector().OnReplayClipChanged();
|
|
}
|
|
|
|
CClip* CReplayPlaybackController::GetClip( s32 clipIndex )
|
|
{
|
|
clipIndex = clipIndex < 0 ? m_activeClip : clipIndex;
|
|
return IsValid() ? m_activeMontage->GetClip( clipIndex ) : NULL;
|
|
}
|
|
|
|
eReplayMicType CReplayPlaybackController::GetMicrophoneType( eMarkerMicrophoneType const markerMicrophoneType )
|
|
{
|
|
eReplayMicType micType = replayMicDefault;
|
|
|
|
switch( markerMicrophoneType )
|
|
{
|
|
case MARKER_MICROPHONE_AT_CAMERA:
|
|
{
|
|
micType = replayMicAtCamera;
|
|
break;
|
|
}
|
|
|
|
case MARKER_MICROPHONE_AT_TARGET:
|
|
{
|
|
micType = replayMicAtTarget;
|
|
break;
|
|
}
|
|
|
|
case MARKER_MICROPHONE_CINEMATIC:
|
|
{
|
|
micType = replayMicCinematic;
|
|
break;
|
|
}
|
|
|
|
case MARKER_MICROPHONE_DEFAULT:
|
|
default:
|
|
{
|
|
// NOP
|
|
}
|
|
}
|
|
|
|
return micType;
|
|
}
|
|
|
|
#endif // GTA_REPLAY
|