Files
GTASource/game/audio/cutsceneaudioentity.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

2271 lines
66 KiB
C++

//
// audio/cutsceneaudioentity.cpp
//
// Copyright (C) 1999-2006 Rockstar Games. All Rights Reserved.
//
#include "cutsceneaudioentity.h"
#include "northaudioengine.h"
#include "dynamicmixer.h"
#include "radioaudioentity.h"
#include "scriptaudioentity.h"
#include "audioengine/engine.h"
#include "audiohardware/waveslot.h"
#include "audiosoundtypes/sounddefs.h"
#include "audiosoundtypes/streamingsound.h"
#include "audioengine/categorymanager.h"
#include "camera/CamInterface.h"
#include "cutscene/CutSceneManagerNew.h"
#include "scriptaudioentity.h"
#include "audioengine/controller.h"
#include "audiohardware/device.h"
#include "audiohardware/driver.h"
#include "audiohardware/syncsource.h"
#include "audiohardware/waveplayer.h"
#include "control/replay/audio/CutSceneAudioPacket.h"
#include "system/bootmgr.h"
#if RSG_DURANGO
#include "audiohardware/decoder_xbonexma.h"
#endif
#include "debugaudio.h"
AUDIO_OPTIMISATIONS()
RAGE_DEFINE_SUBCHANNEL(Audio,CutsceneAudio)
namespace rage
{
XPARAM(noaudio);
}
PARAM(allcutsceneaudio, "Allows the use of non-mastered cutscene audio");
PARAM(forcetrimmedcutscenes, "Force cutscene audio to use trimmed audio files (i.e. final disc versions)");
audCutsceneAudioEntity g_CutsceneAudioEntity;
bool g_CutsceneAudioPaused = false;
float g_CutsceneAudioCatchupFactor = 0.95f;
bool g_DidCutsceneJumpThisFrame = false;
bool g_GameTimerCutscenes = false;
PARAM(gametimercutscenes, "Cutscenes will be driven by the fwTimer - only use for debugging");
const char *g_CutsceneSoundNamePrefix = "CUTSCENES_";
BANK_ONLY(bool g_UseEditedCutscenes = true;)
BANK_ONLY(bool g_UseMasterCutscenes = true;)
audCutsceneAudioEntity::audCutsceneAudioEntity()
{
for(u32 i=0; i<2; i++)
{
m_SoundNames[i][0] = '\0';
m_SoundHashes[i] = 0;
m_Sounds[i] = NULL;
m_StreamSlots[i] = NULL;
m_StartPrepareTime[i] = 0;
m_SyncIds[i] = audMixerSyncManager::InvalidId;
m_Scenes[i] = NULL;
m_ScenePartialHashes[i] = 0;
m_CutsceneAudioType[i] = AUD_CUTSCENE_TYPE_NONE;
m_IsPrepared[i] = false;
m_UsePlayPosition[i] = false;
#if __BANK
m_SceneNames[i][0] = '\0';
#endif
}
m_NextWaveSlotIndex = 0;
}
void audCutsceneAudioEntity::Init(void)
{
naAudioEntity::Init();
audEntity::SetName("audCutsceneAudioEntity");
m_PlayedThisFrame = false;
m_LastStopTime = 0;
m_WasFaded = false;
}
void audCutsceneAudioEntity::Shutdown(void)
{
Stop(true);
StopSyncScene();
audEntity::Shutdown();
}
u32 audCutsceneAudioEntity::GetEngineTime()
{
if(PARAM_noaudio.Get() || PARAM_gametimercutscenes.Get() || g_GameTimerCutscenes)
{
return fwTimer::GetTimeInMilliseconds();
}
return g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
}
void audCutsceneAudioEntity::SetVariableOnSound(u32 variableName, float value, s32 slotIndex)
{
if(slotIndex == -1)
{
slotIndex = (m_NextWaveSlotIndex+1)%2;
}
if(m_Sounds[slotIndex])
{
m_Sounds[slotIndex]->FindAndSetVariableValue(variableName, value);
}
}
bool audCutsceneAudioEntity::IsPrepared(s32 slot)
{
if (PARAM_gametimercutscenes.Get() || g_GameTimerCutscenes)
{
return true;
}
if (slot >= 0)
{
return m_IsPrepared[slot];
}
return false;
}
audPrepareState audCutsceneAudioEntity::Prepare(audCutSceneEvent &cutEvent, const CutsceneAudioType type, s32 slotIndex)
{
if(PARAM_gametimercutscenes.Get() || g_GameTimerCutscenes)
{
return AUD_PREPARED;
}
if(slotIndex == -1)
{
slotIndex = m_NextWaveSlotIndex;
}
#if __BANK
caDisplayf("Cutscene Audio Prepare (new): %s, type %d, offset %d, slot %d, time %d, FC: %u", cutEvent.TrackName, type, cutEvent.StartOffset, slotIndex, fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
if(m_IsPrepared[slotIndex])
{
caErrorf("Cutscene track is already prepared on entry to Prepare()");
}
#endif
u32 soundHash = atFinalizeHash(cutEvent.PartialHash);
u32 editedSoundHash = 0, trimmedSoundHash = 0;
u32 masteredSoundHash = 0, masteredOnlySoundHash = 0;
REPLAY_ONLY(u32 masteredReplaySoundHash = 0, masteredOnlyReplaySoundHash = 0;)
REPLAY_ONLY(u32 customReplaySoundHash = 0;)
if(type == AUD_CUTSCENE_TYPE_CUTSCENE)
{
if(m_CutsceneTrack.GetScriptSuffix())
{
char cutsceneSuffix[128] = {0};
formatf(cutsceneSuffix, "%s%s", m_CutsceneTrack.GetScriptSuffix(), "_EDITED");
editedSoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s%s", m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_TRIMMED");
trimmedSoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s%s", m_CutsceneTrack.GetScriptSuffix(), "_MASTERED");
masteredSoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s%s", m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_ONLY");
masteredOnlySoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
#if GTA_REPLAY
formatf(cutsceneSuffix, "%s%s", m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_REPLAY");
masteredReplaySoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s%s", m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_REPLAY_ONLY");
masteredOnlyReplaySoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
#endif
}
else
{
char cutsceneSuffix[128] = {0};
formatf(cutsceneSuffix, "%s", "_EDITED");
editedSoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s", "_MASTERED_TRIMMED");
trimmedSoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s", "_MASTERED");
masteredSoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "%s", "_MASTERED_ONLY");
masteredOnlySoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
#if GTA_REPLAY
formatf(cutsceneSuffix, "_MASTERED_REPLAY");
masteredReplaySoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
formatf(cutsceneSuffix, "_MASTERED_REPLAY_ONLY");
masteredOnlyReplaySoundHash = atStringHash(cutsceneSuffix, cutEvent.PartialHash);
#endif
}
}
else if(type == AUD_CUTSCENE_TYPE_SYNCHED_SCENE)
{
editedSoundHash = atStringHash("_CUSTOM", cutEvent.PartialHash);
masteredSoundHash = atStringHash("_SYNC_MASTERED", cutEvent.PartialHash);
trimmedSoundHash = atStringHash("_SYNC_MASTERED_TRIMMED", cutEvent.PartialHash);
masteredOnlySoundHash = atStringHash("_SYNC_MASTERED_ONLY", cutEvent.PartialHash);
#if GTA_REPLAY
masteredReplaySoundHash = atStringHash("_SYNC_MASTERED_REPLAY", cutEvent.PartialHash);
masteredOnlyReplaySoundHash = atStringHash("_SYNC_MASTERED_REPLAY_ONLY", cutEvent.PartialHash);
#endif
}
REPLAY_ONLY(customReplaySoundHash = atStringHash("_CUSTOM_REPLAY", cutEvent.PartialHash);)
#if 1 //__BANK
char masteredSoundName[64] = {0};
char editedSoundName[64] = {0};
REPLAY_ONLY(char customReplaySoundName[64] = {0};)
char masteredOnlySoundName[64] = {0};
char trimmedSoundName[64] = {0};
#if GTA_REPLAY
char masteredReplaySoundName[64] = {0};
char masteredOnlyReplaySoundName[64] = {0};
#endif
REPLAY_ONLY(formatf(customReplaySoundName, "%s%s", cutEvent.TrackName, "_CUSTOM_REPLAY");)
if(m_CutsceneTrack.GetScriptSuffix() && type == AUD_CUTSCENE_TYPE_CUTSCENE)
{
formatf(masteredSoundName, "%s%s%s", cutEvent.TrackName, m_CutsceneTrack.GetScriptSuffix(), "_MASTERED");
REPLAY_ONLY(formatf(masteredReplaySoundName, "%s%s%s", cutEvent.TrackName, m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_REPLAY");)
formatf(editedSoundName, "%s%s%s", cutEvent.TrackName, m_CutsceneTrack.GetScriptSuffix(), "_EDITED");
formatf(trimmedSoundName, "%s%s%s", cutEvent.TrackName, m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_TRIMMED");
formatf(masteredOnlySoundName, "%s%s%s", cutEvent.TrackName, m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_ONLY");
REPLAY_ONLY(formatf(masteredOnlyReplaySoundName, "%s%s%s", cutEvent.TrackName, m_CutsceneTrack.GetScriptSuffix(), "_MASTERED_REPLAY_ONLY");)
}
else
{
formatf(masteredSoundName, "%s%s", cutEvent.TrackName, cutEvent.TrackName, type == AUD_CUTSCENE_TYPE_CUTSCENE? "_MASTERED":"_SYNC_MASTERED");
REPLAY_ONLY(formatf(masteredReplaySoundName, "%s%s", cutEvent.TrackName, cutEvent.TrackName, type == AUD_CUTSCENE_TYPE_CUTSCENE? "_MASTERED_REPLAY":"_SYNC_MASTERED_REPLAY");)
formatf(editedSoundName, "%s%s", cutEvent.TrackName, type == AUD_CUTSCENE_TYPE_CUTSCENE? "_EDITED": "_CUSTOM");
formatf(trimmedSoundName, "%s%s", cutEvent.TrackName, type == AUD_CUTSCENE_TYPE_CUTSCENE? "_MASTERED_TRIMMED" : "_SYNC_MASTERED_TRIMMED");
formatf(masteredOnlySoundName, "%s%s", cutEvent.TrackName, type == AUD_CUTSCENE_TYPE_CUTSCENE? "_MASTERED_ONLY" : "_SYNC_MASTERED_ONLY");
REPLAY_ONLY(formatf(masteredOnlyReplaySoundName, "%s%s", cutEvent.TrackName, type == AUD_CUTSCENE_TYPE_CUTSCENE? "_MASTERED_REPLAY_ONLY" : "_SYNC_MASTERED_REPLAY_ONLY");)
}
#endif
if(caVerifyf(m_CutsceneAudioType[slotIndex] == AUD_CUTSCENE_TYPE_NONE || type == m_CutsceneAudioType[slotIndex], "Trying to prepare a cutscene with type %d but a cutscene of type %d is already being prepared. Speak to Animation code", type, m_CutsceneAudioType[slotIndex]))
{
m_CutsceneAudioType[slotIndex] = type;
}
else
{
return AUD_PREPARE_FAILED;
}
if((m_SoundHashes[slotIndex] != soundHash) && (m_SoundHashes[slotIndex] != editedSoundHash) && (m_SoundHashes[slotIndex] != masteredSoundHash) && (m_SoundHashes[slotIndex] != trimmedSoundHash) && (m_SoundHashes[slotIndex] != masteredOnlySoundHash) REPLAY_ONLY( && (m_SoundHashes[slotIndex] != masteredReplaySoundHash) && (m_SoundHashes[slotIndex] != masteredOnlyReplaySoundHash) && (m_SoundHashes[slotIndex] != customReplaySoundHash)) )
{
//We've not yet created this sound.
if(m_Sounds[slotIndex])
{
BANK_ONLY(caAssertf(0, "Preparing cutscene/synched scene audio %s on a slot that already has a sound %s", cutEvent.TrackName, m_SoundNames[slotIndex]));
m_Sounds[slotIndex]->StopAndForget();
StopAndForgetSounds(m_StereoShadowSounds[slotIndex].Left, m_StereoShadowSounds[slotIndex].Right);
}
audSoundInitParams initParams;
initParams.StartOffset = cutEvent.StartOffset;
Assign(initParams.BucketId, audNorthAudioEngine::GetBucketIdForStreamingSounds());
initParams.RemoveHierarchy = false;
audEntity * playEntity = NULL;
if(type != AUD_CUTSCENE_TYPE_CUTSCENE)
{
int sceneIndex = m_SynchedSceneTrack.GetEventIndexFromHash(atFinalizeHash(cutEvent.ScenePartialHash));
if(m_SynchedSceneTrack.UseEntity(sceneIndex) && m_SynchedSceneTrack.GetPlayEntity(sceneIndex))
{
playEntity = m_SynchedSceneTrack.GetPlayEntity(sceneIndex)->GetAudioEntity();
initParams.TrackEntityPosition = true;
}
if(m_SynchedSceneTrack.UsePosition(sceneIndex))
{
initParams.Position = VEC3V_TO_VECTOR3(m_SynchedSceneTrack.GetPlayPosition(sceneIndex));
}
if(!playEntity && !m_SynchedSceneTrack.UsePosition(sceneIndex))
{
Sound::tSpeakerMask speakerMask;
speakerMask.Value = 0;
speakerMask.BitFields.FrontCenter = true;
initParams.SpeakerMask = speakerMask.Value;
}
}
if(!playEntity)
{
playEntity = (audEntity*)this;
}
if(type == AUD_CUTSCENE_TYPE_CUTSCENE)
{
if(!m_Sounds[slotIndex] && !PARAM_forcetrimmedcutscenes.Get())
{
#if GTA_REPLAY
if(CReplayMgr::IsReplayInControlOfWorld())
{
playEntity->CreateSound_PersistentReference(masteredOnlyReplaySoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = masteredOnlyReplaySoundHash;
strcpy(m_SoundNames[slotIndex], masteredOnlyReplaySoundName);
if(!PARAM_allcutsceneaudio.Get())
{
caAssertf(m_Sounds[slotIndex], "Couldn't get mastered_only_replay cutscene audio %s", masteredOnlyReplaySoundName);
}
}
#endif
if(!m_Sounds[slotIndex])
{
playEntity->CreateSound_PersistentReference(masteredOnlySoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = masteredOnlySoundHash;
strcpy(m_SoundNames[slotIndex], masteredOnlySoundName);
if(!PARAM_allcutsceneaudio.Get())
{
caAssertf(m_Sounds[slotIndex], "Couldn't get mastered_only cutscene audio %s", masteredOnlySoundName);
}
}
}
if(!m_Sounds[slotIndex] BANK_ONLY(&& g_UseMasterCutscenes))
{
playEntity->CreateSound_PersistentReference(masteredSoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = masteredSoundHash;
strcpy(m_SoundNames[slotIndex], masteredSoundName);
}
if(!PARAM_forcetrimmedcutscenes.Get() && PARAM_allcutsceneaudio.Get())
{
#if GTA_REPLAY
if(CReplayMgr::IsReplayInControlOfWorld())
{
playEntity->CreateSound_PersistentReference(masteredReplaySoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = masteredReplaySoundHash;
strcpy(m_SoundNames[slotIndex], masteredReplaySoundName);
if(!PARAM_allcutsceneaudio.Get())
{
caAssertf(m_Sounds[slotIndex], "Couldn't get mastered_replayy cutscene audio %s", masteredOnlyReplaySoundName);
}
}
#endif
if(!m_Sounds[slotIndex] BANK_ONLY(&& g_UseEditedCutscenes))
{
playEntity->CreateSound_PersistentReference(editedSoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = editedSoundHash;
strcpy(m_SoundNames[slotIndex], editedSoundName);
}
if(!m_Sounds[slotIndex])
{
playEntity->CreateSound_PersistentReference(soundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = soundHash;
strcpy(m_SoundNames[slotIndex], cutEvent.TrackName);
}
}
if(!m_Sounds[slotIndex] && PARAM_forcetrimmedcutscenes.Get())
{
playEntity->CreateSound_PersistentReference(trimmedSoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = trimmedSoundHash;
strcpy(m_SoundNames[slotIndex], trimmedSoundName);
if(/*sysBootManager::IsBootedFromDisc() || RSG_FINAL || */PARAM_forcetrimmedcutscenes.Get())
{
caAssertf(m_Sounds[slotIndex], "Couldn't get trimmed cutscene audio %s", trimmedSoundName);
}
if(m_Sounds[slotIndex]) //remove any audio offset that's snuck in
{
cutEvent.StartOffset-=cutEvent.AudioOffset;
cutEvent.AudioOffset = 0;
}
}
}
else
{
#if GTA_REPLAY
if(CReplayMgr::IsReplayInControlOfWorld())
{
playEntity->CreateSound_PersistentReference(customReplaySoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = customReplaySoundHash;
strcpy(m_SoundNames[slotIndex], customReplaySoundName);
if(!PARAM_allcutsceneaudio.Get())
{
caAssertf(m_Sounds[slotIndex], "Couldn't get custom_replay cutscene audio %s", masteredOnlyReplaySoundName);
}
if(!m_Sounds[slotIndex])
{
playEntity->CreateSound_PersistentReference(masteredOnlyReplaySoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = masteredOnlyReplaySoundHash;
strcpy(m_SoundNames[slotIndex], masteredOnlyReplaySoundName);
if(!PARAM_allcutsceneaudio.Get())
{
caAssertf(m_Sounds[slotIndex], "Couldn't get mastered_only_replay cutscene audio %s", masteredOnlyReplaySoundName);
}
}
}
#endif
if(!m_Sounds[slotIndex] BANK_ONLY(&& g_UseEditedCutscenes))
{
playEntity->CreateSound_PersistentReference(editedSoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = editedSoundHash;
strcpy(m_SoundNames[slotIndex], editedSoundName);
}
if(!m_Sounds[slotIndex])
{
playEntity->CreateSound_PersistentReference(masteredOnlySoundHash, &m_Sounds[slotIndex], &initParams);
m_SoundHashes[slotIndex] = masteredOnlySoundHash;
strcpy(m_SoundNames[slotIndex], masteredOnlySoundName);
caAssertf(m_Sounds[slotIndex], "Couldn't get mastered_only synched scene audio %s", masteredOnlySoundName);
}
}
#if __BANK
formatf(m_SceneNames[slotIndex], "%s_SCENE", cutEvent.SceneName);
#endif
}
//Default to prepared so we don't go into an infinite loop with missing audio tracks.
audPrepareState prepareState = AUD_PREPARED;
if(m_Sounds[slotIndex])
{
if(m_StreamSlots[slotIndex] == NULL)
{
audStreamClientSettings settings;
settings.priority = audStreamSlot::STREAM_PRIORITY_CUTSCENE;
settings.stopCallback = &OnStopCallback;
settings.hasStoppedCallback = &HasStoppedCallback;
settings.userData = slotIndex;
m_StreamSlots[slotIndex] = audStreamSlot::AllocateSlot(&settings);
m_StartPrepareTime[slotIndex] = fwTimer::GetTimeInMilliseconds();
caAssertf(m_StreamSlots[slotIndex], "Couldn't allocate streaming audio slot for cutscene");
if(!m_StreamSlots[slotIndex])
{
caErrorf("Couldn't allocate streaming audio slot for cutscene");
}
}
static const u32 k_MaxPrepareTime = 20000;
if(m_StreamSlots[slotIndex] && (m_StreamSlots[slotIndex]->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE))
{
prepareState = m_Sounds[slotIndex]->Prepare(m_StreamSlots[slotIndex]->GetWaveSlot(), true);
u32 prepareTime = fwTimer::GetTimeInMilliseconds() - m_StartPrepareTime[slotIndex];
u32 numReferences = audWaveSlot::GetSlotReferenceCount(m_StreamSlots[slotIndex]->GetWaveSlot()->GetSlotIndex());
if(numReferences > 0)
{
caDisplayf("Preparing cutscene audio on a waveslot with %u references. We've been preparing for %u ms, prepared state %u ", numReferences, prepareTime, prepareState);
if(prepareState != AUD_PREPARED && prepareTime > k_MaxPrepareTime)
{
caErrorf("Looks like our cutscene audio prepare took too long; bailing out");
#if __BANK
caErrorf("=========== Sound pool:");
audSound::GetStaticPool().DebugPrintSoundPool();
caErrorf("=========== Physical voices:");
audDriver::GetVoiceManager().PrintPhysicalVoices();
#if RSG_DURANGO
caErrorf("=========== Xma stats:");
audDecoderXboxOneXma::PrintDecoderStats();
caErrorf("=========== Xma contexts:");
audDecoderXboxOneXma::PrintUsedXmaContexts();
#endif //RSG_DURANGO
caErrorf("===========");
#endif //BANK
m_Sounds[slotIndex]->StopAndForget();
prepareState = AUD_PREPARE_FAILED;
}
}
}
else
{
prepareState = AUD_PREPARING; //we need to wait for a stream slot to become available.
}
if(prepareState == AUD_PREPARED && m_SyncIds[slotIndex] == audMixerSyncManager::InvalidId)
{
m_SyncIds[slotIndex] = audMixerSyncManager::Get()->Allocate();
if(m_SyncIds[slotIndex] != audMixerSyncManager::InvalidId)
{
caAssertf(!m_Scenes[slotIndex], "Cutscene audio Scene for slot %d is still active in Prpare", slotIndex);
m_ScenePartialHashes[slotIndex] = cutEvent.ScenePartialHash;
audMixerSyncManager::Get()->AddTriggerRef(m_SyncIds[slotIndex]);
m_Sounds[slotIndex]->SetRequestedSyncSlaveId(m_SyncIds[slotIndex]);
//snaErrorf("Setting slave sync id %u", m_SyncIds[slotIndex]);
//This was asserting but may already be fixed: try with it again once the big cutscene push is done.
//if(m_CutsceneTrack.GetCurrentPlayIndex() > -1)
//{
// //m_Sounds[m_CutsceneTrack.GetCurrentPlayIndex()]->SetRequestedSyncMasterId(m_SyncIds[slotIndex]);
// u32 wavePlayerId = ((audStreamingSound*)m_Sounds[m_CutsceneTrack.GetCurrentPlayIndex()])->GetFirstWavePlayerId();
// audPcmSourceInterface::SetParam(wavePlayerId, audPcmSource::Params::SyncMasterId, m_SyncIds[slotIndex]);
//
// u32 triggerTimeMs = cutEvent.TriggerTime + m_CutsceneTrack.GetEventFromIndex(0).StartOffset - m_CutsceneTrack.GetEventFromIndex(0).TriggerTime;
// u32 triggerTimeSamples = audDriverUtil::ConvertMsToSamples(triggerTimeMs, 32000);
// audPcmSourceInterface::SetParam(wavePlayerId, audWavePlayer::Params::SyncTriggerTimeSamples, triggerTimeSamples);
//}
//else
//{
// audMixerSyncManager::Get()->AddTriggerRef(m_SyncIds[slotIndex]);
//}
// This will prep the decoder/VRAM fetch so that we can kick off playback 'instantly'
// Voices won't actually start playing until we trigger the sync id
m_Sounds[slotIndex]->Play();
// Look for a multitrack reverb sound
Sound uncompressedSound;
const u32 reverbSoundName = atStringHash("_REV", m_ScenePartialHashes[slotIndex]);
const MultitrackSound *multitrackSound = g_AudioEngine.GetSoundManager().GetFactory().DecompressMetadata<MultitrackSound>(reverbSoundName, uncompressedSound);
if(multitrackSound)
{
caAssertf(multitrackSound->numSoundRefs == 2, "Cutscene reverb multitrack expected to have 2 sounds; this one has %u", multitrackSound->numSoundRefs);
audSoundInitParams initParams;
initParams.ShadowPcmSourceId = ((audStreamingSound*)m_Sounds[slotIndex])->GetFirstWavePlayerId();
CreateAndPlaySound_Persistent(multitrackSound->SoundRef[0].SoundId, &m_StereoShadowSounds[slotIndex].Left, &initParams);
CreateAndPlaySound_Persistent(multitrackSound->SoundRef[1].SoundId, &m_StereoShadowSounds[slotIndex].Right, &initParams);
caDisplayf("Created stereo shadow sounds for cutscene");
caAssert(m_StereoShadowSounds[slotIndex].Left);
caAssert(m_StereoShadowSounds[slotIndex].Right);
if(m_StereoShadowSounds[slotIndex].Left)
{
audEnvironmentGameMetric &metric = m_StereoShadowSounds[slotIndex].Left->GetRequestedSettings()->GetEnvironmentGameMetric();
metric.SetJustReverb(true);
metric.SetReverbSmall(0.f);
metric.SetReverbMedium(0.f);
metric.SetReverbLarge(0.f);
}
if(m_StereoShadowSounds[slotIndex].Right)
{
audEnvironmentGameMetric &metric = m_StereoShadowSounds[slotIndex].Right->GetRequestedSettings()->GetEnvironmentGameMetric();
metric.SetJustReverb(true);
metric.SetReverbSmall(0.f);
metric.SetReverbMedium(0.f);
metric.SetReverbLarge(0.f);
}
}
//We need to spin round one more time to make sure all our voices are ready to trigger
prepareState = AUD_PREPARING;
}
else
{
audWarningf("Failed to allocate mixer sync source for cutscene; will retry next frame");
prepareState = AUD_PREPARING;
}
}
else if (prepareState == AUD_PREPARED)
{
if((audMixerSyncManager::Get()->GetTriggerRefCount(m_SyncIds[slotIndex]) > 1))
{
prepareState = AUD_PREPARING;
}
//Check to see if we've taken too long and need to bail out
u32 prepareTime = fwTimer::GetTimeInMilliseconds() - m_StartPrepareTime[slotIndex];
caDisplayf("Waiting for cutscene audio sync. We've been preparing for %u ms, prepared state %u", prepareTime, prepareState);
if(prepareState != AUD_PREPARED && prepareTime > k_MaxPrepareTime)
{
caErrorf("Looks like our cutscene audio sync took too long; bailing out");
#if __BANK
caErrorf("=========== Sound pool:");
audSound::GetStaticPool().DebugPrintSoundPool();
caErrorf("=========== Physical voices:");
audDriver::GetVoiceManager().PrintPhysicalVoices();
#if RSG_DURANGO
caErrorf("=========== Xma stats:");
audDecoderXboxOneXma::PrintDecoderStats();
caErrorf("=========== Xma contexts:");
audDecoderXboxOneXma::PrintUsedXmaContexts();
#endif //RSG_DURANGO
caErrorf("===========");
#endif //BANK
m_Sounds[slotIndex]->StopAndForget();
prepareState = AUD_PREPARE_FAILED;
}
}
}
else
{
caWarningf("No audio track for cutscene: %s", cutEvent.TrackName);
}
if(prepareState != AUD_PREPARING)
{
m_IsPrepared[slotIndex] = true;
}
return prepareState;
}
void audCutsceneAudioEntity::Play(s32 slotIndex)
{
if(slotIndex == -1)
{
slotIndex = m_NextWaveSlotIndex;
}
#if __BANK
caDisplayf("Cutscene Audio Play: %s, type %d, slot %d, sound %p, time %d, FC: %u", m_SoundNames[slotIndex], m_CutsceneAudioType[slotIndex], slotIndex, m_Sounds[slotIndex], fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
if(m_Sounds[slotIndex])
{
if(caVerifyf(m_StreamSlots[slotIndex], "Stream slot is null when trying to Play cutscene"))
{
// The sound should already be playing, but the voices will be in a waiting-for-sync state
caAssertf(m_Sounds[slotIndex]->GetPlayState() == AUD_SOUND_PLAYING, "Playstate = %d", m_Sounds[slotIndex]->GetPlayState());
audMetadataObjectInfo info;
#if __BANK
caDisplayf("Trying to start cutscene dynamic mix scene %s", m_SceneNames[slotIndex]);
#endif
const u32 sceneHash = atStringHash("_SCENE", m_ScenePartialHashes[slotIndex]);
if(DYNAMICMIXMGR.GetMetadataManager().GetObjectInfo(sceneHash, info))
{
DYNAMICMIXER.StartScene(sceneHash, &m_Scenes[slotIndex]);
}
else if(m_CutsceneAudioType[slotIndex] != AUD_CUTSCENE_TYPE_CUTSCENE)
{
DYNAMICMIXER.StartScene(ATSTRINGHASH("SYNCHED_SCENE_DEFAULT_SCENE", 0xE69FFD02), &m_Scenes[slotIndex]);
}
else
{
DYNAMICMIXER.StartScene(ATSTRINGHASH("CUTSCENE_DEFAULT_SCENE", 0x3E96C108), &m_Scenes[slotIndex]);
}
if(m_CutsceneAudioType[slotIndex] == AUD_CUTSCENE_TYPE_CUTSCENE && m_Scenes[slotIndex] && m_Scenes[slotIndex]->GetSceneSettings() && AUD_GET_TRISTATE_VALUE(m_Scenes[slotIndex]->GetSceneSettings()->Flags, FLAG_ID_MIXERSCENE_CUSCENEAUDIOOVERRUNS) == AUD_TRISTATE_TRUE)
{
m_CutsceneTrack.SetCanOverrun(true);
}
if(m_SyncIds[slotIndex] != audMixerSyncManager::InvalidId)
{
audMixerSyncManager::Get()->Trigger(m_SyncIds[slotIndex], 0);
}
m_NeedToPreloadRadio = true;
m_PlayedThisFrame = true;
m_IsPrepared[slotIndex] = false;
//We are now playing a stream from this wave slot, so toggle the wave slot to be used to preload the next cutscene.
// - This enables overlapping loads with playback for split cutscenes.
m_NextWaveSlotIndex = (slotIndex + 1) % 2;
}
}
}
void audCutsceneAudioEntity::PreUpdateService(u32 UNUSED_PARAM(timeInMs))
{
g_DidCutsceneJumpThisFrame = false;
m_CutsceneTrack.Update();
m_SynchedSceneTrack.Update();
bool played0 = (m_Sounds[0] && m_Sounds[0]->GetPlayState() == AUD_SOUND_PLAYING) || m_PlayedThisFrame;
bool played1 = (m_Sounds[1] && m_Sounds[1]->GetPlayState() == AUD_SOUND_PLAYING) || m_PlayedThisFrame;
bool isPlaying = ((CutSceneManager::GetInstance()&&!CutSceneManager::GetInstance()->IsStreamedCutScene()) && (played0 || played1));
bool wasPlayingRecently = (fwTimer::GetTimeInMilliseconds() < m_LastStopTime + 500);
//Only mute non-cutscene audio when we're playing a non-ped-interaction cutscene, i.e a traditional screen-faded mocap cutscene.
if(isPlaying || wasPlayingRecently NA_RADIO_ENABLED_ONLY(|| audRadioStation::IsPlayingEndCredits()))
{
NA_RADIO_ENABLED_ONLY(g_RadioAudioEntity.MuteRetunes());
}
else
{
NA_RADIO_ENABLED_ONLY(g_RadioAudioEntity.UnmuteRetunes());
}
for(int i=0; i<2; i++)
{
if(!m_Sounds[i])
{
if(m_SyncIds[i] != audMixerSyncManager::InvalidId)
{
if(audMixerSyncManager::Get()->GetTriggerRefCount(m_SyncIds[i]))
{
audMixerSyncManager::Get()->Cancel(m_SyncIds[i]);
}
audMixerSyncManager::Get()->Release(m_SyncIds[i]);
m_SyncIds[i] = audMixerSyncManager::InvalidId;
}
if(m_Scenes[i])
{
m_Scenes[i]->Stop();
}
if(m_StreamSlots[i])
{
//We have finished with our stream slot, so free it.
m_StreamSlots[i]->Free();
m_StreamSlots[i] = NULL;
}
m_StartPrepareTime[i] = 0;
m_SoundNames[i][0] = '\0';
m_SoundHashes[i] = 0;
#if __BANK
m_SceneNames[i][0] = '\0';
#endif
m_CutsceneAudioType[i] = AUD_CUTSCENE_TYPE_NONE;
}
else
{
f32 fade = 1.f - camInterface::GetFadeLevel();
if(fade < 1.f && !g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::AllowCutsceneOverScreenFade))
{
m_Sounds[i]->SetRequestedVolume(audDriverUtil::ComputeDbVolumeFromLinear(fade));
m_WasFaded = true;
}
else if(m_WasFaded)
{
m_Sounds[i]->SetRequestedVolume(audDriverUtil::ComputeDbVolumeFromLinear(1.f));
m_WasFaded = false;
}
}
}
m_PlayedThisFrame = false;
}
void audCutsceneAudioEntity::PostUpdate()
{
m_SynchedSceneTrack.ClearUnusedEntities();
}
void audCutsceneAudioEntity::Stop(bool shouldStopAllStreams, s32 slotIndex, s32 release)
{
if(slotIndex == -1)
{
slotIndex = (m_NextWaveSlotIndex+1)%2;
}
#if __BANK
caDisplayf("Cutscene Audio Stop: %s, type %d, slot %d, shouldStopAllStreams %s, time %d, FC: %u", m_SoundNames[slotIndex], m_CutsceneAudioType[slotIndex], slotIndex, shouldStopAllStreams?"true":"false", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
u8 numStreamsToStop;
if(shouldStopAllStreams)
{
numStreamsToStop = 2;
}
else
{
numStreamsToStop = 1;
}
u32 waveSlotIndex = slotIndex;
for(u32 streamIndex=0; streamIndex<numStreamsToStop; streamIndex++)
{
m_IsPrepared[waveSlotIndex] = false;
if(m_Scenes[waveSlotIndex])
{
m_Scenes[waveSlotIndex]->Stop();
}
if(m_Sounds[waveSlotIndex])
{
m_LastStopTime = fwTimer::GetTimeInMilliseconds();
m_Sounds[waveSlotIndex]->SetReleaseTime(release);
m_Sounds[waveSlotIndex]->StopAndForget();
m_Sounds[waveSlotIndex] = NULL;
}
StopAndForgetSounds(m_StereoShadowSounds[waveSlotIndex].Left, m_StereoShadowSounds[waveSlotIndex].Right);
m_SoundNames[waveSlotIndex][0] = '\0';
m_SoundHashes[waveSlotIndex] = 0;
#if __BANK
m_SceneNames[waveSlotIndex][0] = '\0';
#endif
if(m_SyncIds[waveSlotIndex] != audMixerSyncManager::InvalidId)
{
if(audMixerSyncManager::Get()->GetTriggerRefCount(m_SyncIds[waveSlotIndex]))
{
audMixerSyncManager::Get()->Cancel(m_SyncIds[waveSlotIndex]);
}
audMixerSyncManager::Get()->Release(m_SyncIds[waveSlotIndex]);
m_SyncIds[waveSlotIndex] = audMixerSyncManager::InvalidId;
}
m_CutsceneAudioType[waveSlotIndex] = AUD_CUTSCENE_TYPE_NONE;
waveSlotIndex = (waveSlotIndex + 1) % 2;
}
for(u32 loop = 0; loop < 2; loop++)
{
if(m_StreamSlots[loop] && m_Sounds[loop] == NULL)
{
//We have finished with our stream slot, so free it.
m_StreamSlots[loop]->Free();
m_StreamSlots[loop] = NULL;
}
m_StartPrepareTime[loop] = 0;
}
}
void audCutsceneAudioEntity::Pause()
{
g_CutsceneAudioPaused = true;
}
void audCutsceneAudioEntity::UnPause()
{
g_CutsceneAudioPaused = false;
}
s32 g_CutsceneTimeOffset =0;
float g_CutsceneTimeScale = 1.f;
s32 audCutsceneAudioEntity::GetPlayTimeMs(s32 slotIndex)
{
if(slotIndex == -1)
{
slotIndex = (m_NextWaveSlotIndex+1)%2;
}
s32 playTimeMs = -1;
u8 playingWaveSlotIndex = (u8)slotIndex;
if(m_Sounds[playingWaveSlotIndex])
{
playTimeMs = ((audStreamingSound*)m_Sounds[playingWaveSlotIndex])->GetCurrentPlayTimeOfWave();
if(playTimeMs > 0)
{
playTimeMs -= ((audStreamingSound*)m_Sounds[playingWaveSlotIndex])->GetStartOffset();
}
}
else
{
audErrorf("Trying to get play time of a sound that is null");
return playTimeMs;
}
if(playTimeMs > ComputeSoundDuration())
{
return -1;
}
#if !__FINAL
playTimeMs = (u32)((float)playTimeMs * g_CutsceneTimeScale);
playTimeMs += g_CutsceneTimeOffset;
#endif
return playTimeMs;
}
s32 audCutsceneAudioEntity::GetPlayTimeIncludingOffsetMs(s32 slotIndex)
{
s32 playTimeMs = -1;
u8 playingWaveSlotIndex = (u8)slotIndex;
if(m_Sounds[playingWaveSlotIndex])
{
playTimeMs = ((audStreamingSound*)m_Sounds[playingWaveSlotIndex])->GetCurrentPlayTimeOfWave();
}
else
{
caErrorf("Trying to get play time of a sound that is null");
return playTimeMs;
}
return playTimeMs;
}
s32 audCutsceneAudioEntity::ComputeSoundDuration(s32 slotIndex)
{
if(slotIndex == -1)
{
slotIndex = (m_NextWaveSlotIndex+1)%2;
}
s32 duration = -1;
if(caVerifyf(m_Sounds[slotIndex], "Trying to get play time of a sound that is null"))
{
duration = ((audStreamingSound*)m_Sounds[slotIndex])->ComputeDurationMsIncludingStartOffsetAndPredelay(NULL);
}
return duration;
}
bool audCutsceneAudioEntity::OnStopCallback(u32 UNUSED_PARAM(userData))
{
//We are losing our stream slot, so stop the cutscene stream.
g_CutsceneAudioEntity.Stop(true);
return true;
}
bool audCutsceneAudioEntity::HasStoppedCallback(u32 userData)
{
bool hasStopped = true;
//Check if we are still loading or playing from our allocated wave slots.
//for(u8 waveSlotIndex=0; waveSlotIndex<2; waveSlotIndex++)
{
audStreamSlot *streamSlot = g_CutsceneAudioEntity.GetStreamSlot(userData);
if(streamSlot)
{
audWaveSlot *waveSlot = streamSlot->GetWaveSlot();
if(waveSlot && (waveSlot->GetIsLoading() || (waveSlot->GetReferenceCount() > 0)))
{
hasStopped = false;
}
}
}
return hasStopped;
}
void audCutsceneAudioEntity::InitCutsceneTrack(u32 skipTimeInMs)
{
m_CutsceneTrack.Init(skipTimeInMs);
}
bool audCutsceneAudioEntity::IsSyncSceneTrackPrepared(const char * audioName)
{
return m_SynchedSceneTrack.IsPrepared(audioName);
}
bool audCutsceneAudioEntity::IsCutsceneTrackPrepared()
{
return m_CutsceneTrack.IsPrepared();
}
bool audCutsceneAudioEntity::IsCutsceneTrackActive()
{
return m_CutsceneTrack.IsActive();
}
void audCutsceneAudioEntity::TriggerCutscene()
{
m_CutsceneTrack.Trigger();
}
void audCutsceneAudioEntity::TriggerSyncScene(u32 hash)
{
caDisplayf("Triggering synched scene with audio hash %u", hash);
m_SynchedSceneTrack.Trigger(hash);
}
u32 audCutsceneAudioEntity::GetSyncSceneDuration(u32 audioHash)
{
return m_SynchedSceneTrack.GetSceneAudioDuration(audioHash);
}
u32 audCutsceneAudioEntity::TriggerSyncScene()
{
caDisplayf("TriggeringSynchedScene with no audio hash");
u32 sceneHash = 0;
sceneHash = m_SynchedSceneTrack.GetNextSynchedSceneInQueue();
if(sceneHash)
{
m_SynchedSceneTrack.Trigger(sceneHash);
return sceneHash;
}
return 0;
}
void audCutsceneAudioEntity::HandleSeamlessPlay(s32 slotIndex)
{
#if __BANK
caDisplayf("Cutscene Handle seamless Play: %s, type %d, slot %d, sound %p, time %d, FC: %u", m_SoundNames[slotIndex], m_CutsceneAudioType[slotIndex], slotIndex, m_SoundNames[slotIndex], fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
// The sound should already be playing, but the voices will be in a waiting-for-sync state
caAssertf(m_Sounds[slotIndex]->GetPlayState() == AUD_SOUND_PLAYING, "Playstate = %d", m_Sounds[slotIndex]->GetPlayState());
audMetadataObjectInfo info;
#if __BANK
caDisplayf("Trying to start cutscene dynamic mix scene %s", m_SceneNames[slotIndex]);
#endif
const u32 sceneHash = atStringHash("_SCENE", m_ScenePartialHashes[slotIndex]);
if(DYNAMICMIXMGR.GetMetadataManager().GetObjectInfo(sceneHash, info))
{
DYNAMICMIXER.StartScene(sceneHash, &m_Scenes[slotIndex]);
}
else if(m_CutsceneAudioType[slotIndex] != AUD_CUTSCENE_TYPE_CUTSCENE)
{
DYNAMICMIXER.StartScene(ATSTRINGHASH("SYNCHED_SCENE_DEFAULT_SCENE", 0xE69FFD02), &m_Scenes[slotIndex]);
}
else
{
DYNAMICMIXER.StartScene(ATSTRINGHASH("CUTSCENE_DEFAULT_SCENE", 0x3E96C108), &m_Scenes[slotIndex]);
}
m_NeedToPreloadRadio = true;
m_PlayedThisFrame = true;
m_IsPrepared[slotIndex] = false;
//We are now playing a stream from this wave slot, so toggle the wave slot to be used to preload the next cutscene.
// - This enables overlapping loads with playback for split cutscenes.
m_NextWaveSlotIndex = (slotIndex + 1) % 2;
}
void audCutsceneAudioEntity::StopCutscene(bool isSkipping, u32 releaseTime)
{
if(isSkipping)
{
if(m_Scenes[GetNextWaveslotIndexForPrepare()] && AUD_GET_TRISTATE_VALUE(m_Scenes[GetNextWaveslotIndexForPrepare()]->GetSceneSettings()->Flags, FLAG_ID_MIXERSCENE_CUSCENEAUDIOOVERRUNS) == AUD_TRISTATE_TRUE)
{
m_Scenes[GetNextWaveslotIndexForPrepare()]->SetVariableValue(ATSTRINGHASH("apply", 0xE865CDE8), 0.f);
}
}
m_CutsceneTrack.Stop(isSkipping, releaseTime);
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CCutSceneStopAudioPacket>(
CCutSceneStopAudioPacket(isSkipping, releaseTime));
}
#endif // GTA_REPLAY
}
void audCutsceneAudioEntity::StopOverunAudio()
{
m_CutsceneTrack.SetCanOverrun(false);
StopCutscene(false, 10000);
}
void audCutsceneAudioEntity::StopSyncScene(u32 audioHash)
{
m_SynchedSceneTrack.Stop(audioHash);
}
void audCutsceneAudioEntity::StopSyncScene()
{
m_SynchedSceneTrack.Stop();
}
u32 audCutsceneAudioEntity::GetCutsceneTime()
{
return m_CutsceneTrack.GetTrackTime();
}
u32 audCutsceneAudioEntity::GetSyncSceneTime(u32 audioHash)
{
return m_SynchedSceneTrack.GetSceneTime(audioHash);
}
bool audCutsceneAudioEntity::SlotIsPlaying(u32 slotIndex)
{
if(m_Sounds[slotIndex])
{
return m_Sounds[slotIndex]->GetPlayState() == AUD_SOUND_PLAYING;
}
return false;
}
////// audCutTrack /////////////////////////////////////////////////////////////////////////
void audCutTrack::Reset()
{
#if __BANK
caDisplayf("Cutscene track reset, time %d, FC: %u", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
for(int i=0; i<kNumAudCutsceneEvents; i++)
{
m_EventList[i].Init();
}
m_IsControllingCutsceneAudio = false;
}
void audCutTrackCutScene::Reset()
{
audCutTrack::Reset();
m_State = UNINITIALIZED;
m_LoadEventIndex = 0;
m_PlayEventIndex = -1;
m_NumEvents = 0;
m_CutsceneLoadIndex = -1;
m_CutscenePlayIndex = -1;
m_ScriptSuffix[0] = '\0';
m_CanOverrun = false;
#if GTA_REPLAY
m_StartCutsceneTime = 0;
#endif
//We rely on these to keep track of audio time if audio stops so don't reset.
//m_TriggerTime = 0;
//m_LastEngineTime = 0;
//m_LastSoundTime = 0;
}
bool audCutTrackCutScene::IsPrepared()
{
if(m_State == UNINITIALIZED)
{
#if __BANK
caDebugf2("Cutscene audio was asked if it was prepared and it replied: false, time %d, FC: %u", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
return false;
}
#if __BANK
if(m_State >= PREPARED)
{
caDisplayf("Cutscene audio was asked if it was prepared and it replied: true, time %d, FC: %u", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
}
else
{
caDebugf2("Cutscene audio was asked if it was prepared and it replied: false, time %d, FC: %u", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
}
#endif
return m_State >= PREPARED;
}
void audCutTrackCutScene::Init(u32 skipTimeInMs)
{
if(m_State != audCutTrack::UNINITIALIZED)
{
return;
}
#if !__FINAL
m_Scrubbingoffset = skipTimeInMs;
#endif
CutSceneManager * manager = CutSceneManager::GetInstance();
const cutfCutsceneFile2* cutfile = static_cast<const CutSceneManager*>(CutSceneManager::GetInstance())->GetCutfFile();
//const cutfCutsceneFile2* cutfile = manager->GetCutfFile();
atArray<audCutSceneEvent> audioEventList;
const atArray<cutfEvent *> &eventList = cutfile->GetEventList();
atArray<cutfObject *> objectList;
cutfile->FindObjectsOfType(CUTSCENE_AUDIO_OBJECT_TYPE, objectList);
bool isConcatSkip = false;
m_LastCutsceneTime = 0;
atFixedArray<audIntermediateCutsceneEvent, 32> intermediateArray;
int sectionCount = 0;
cutfEvent * currentCutEvent = NULL;
while(sectionCount >= 0)
{
audIntermediateCutsceneEvent& thisIntEvent = intermediateArray.Append();
thisIntEvent.concatSection = sectionCount;
thisIntEvent.triggerTime = manager->IsConcatted()? manager->GetConcatSectionStartTime(sectionCount) : manager->GetStartTime();
for(int i=0; i < eventList.GetCount(); i++)
{
if(eventList[i]->GetEventId() == CUTSCENE_PLAY_AUDIO_EVENT && (!manager->IsConcatted() || sectionCount == manager->GetConcatSectionForTime(eventList[i]->GetTime())))
{
currentCutEvent = eventList[i];
break;
}
}
thisIntEvent.cutEvent = currentCutEvent;
sectionCount = manager->IsConcatted() ? manager->GetNextConcatSection(sectionCount) : -1;
}
#if __BANK
for(int i=0; i<intermediateArray.GetCount(); i++)
{
if(intermediateArray[i].cutEvent)
{
caDisplayf("Intermediate event: section %d, isValid=%s, event time %f, cut time %f", intermediateArray[i].concatSection, manager->IsConcatSectionValidForPlayBack(intermediateArray[i].concatSection)? "true":"false", intermediateArray[i].cutEvent ? intermediateArray[i].cutEvent->GetTime() : -1.f, intermediateArray[i].triggerTime);
}
}
#endif
u32 timeToskip = 0;
for(int i=0; i<intermediateArray.GetCount(); i++)
{
BANK_ONLY(int eventCount = 0;)
cutfEvent * event = intermediateArray[i].cutEvent;
if(event && event->GetEventId() == CUTSCENE_PLAY_AUDIO_EVENT)
{
BANK_ONLY(eventCount++;)
const cutfObjectIdEvent* pObjectIdEvent = static_cast<const cutfObjectIdEvent*>( event );
for(int j=0; j<objectList.GetCount(); j++)
{
cutfObject * object = objectList[j];
if(object->GetObjectId() == pObjectIdEvent->GetObjectId())
{
audCutSceneEvent audioEvent;
int concat = 0;
u32 duration = 0;
if(manager->IsConcatted())
{
concat = intermediateArray[i].concatSection;
duration = (u32)(manager->GetConcatSectionDuration(concat)*1000);
if(!manager->IsConcatSectionValidForPlayBack(concat))
{
#if __BANK
caDisplayf("Concat section %d at time %f player event %d is invalid - skipping", concat, event->GetTime(), eventCount);
#endif
if(!isConcatSkip)
{
isConcatSkip = true;
timeToskip = i;
}
break;
}
}
else
{
duration = (u32)(manager->GetTotalSeconds()*1000);
}
const cutfAudioObject* audioObj = static_cast<const cutfAudioObject*>(object);
char soundName[64];
formatf(soundName, "%s%s", g_CutsceneSoundNamePrefix, object->GetDisplayName().c_str());
strupr(soundName);
char * validationStr = NULL;
validationStr = strstr(soundName, "_EDITED");
if(validationStr)
{
caAssertf(0, "Cutscene audio (%s) should not be named _EDITED, please bug Default Animation Resource", soundName);
validationStr[0] = '\0';
}
validationStr = strstr(soundName, "_MASTERED");
if(validationStr)
{
caAssertf(0, "Cutscene audio (%s) should not be named _MASTERED, please bug Default Animation Resource", soundName);
validationStr[0] = '\0';
}
char sceneName[64];
char nonSeqName[64];
//Remove _SEQ for scenes if present
formatf(nonSeqName, "%s", object->GetDisplayName().c_str());
char * seqStr = strstr(nonSeqName, "_SEQ");
if(seqStr)
{
seqStr[0] = '\0';
}
formatf(sceneName, "%s", nonSeqName);
audioEvent.PartialHash = atPartialStringHash(soundName);
audioEvent.ScenePartialHash = atPartialStringHash(sceneName);
f32 audioOffsetTime = audioObj->GetOffset();
if((/*sysBootManager::IsBootedFromDisc() || RSG_FINAL ||*/ PARAM_forcetrimmedcutscenes.Get()))
{
audioOffsetTime = 0.f;
}
else
{
const u32 sceneHash = atStringHash("_SCENE", atPartialStringHash(sceneName));
MixerScene * mixScene = DYNAMICMIXMGR.GetObject<MixerScene>(sceneHash);
if(mixScene && AUD_GET_TRISTATE_VALUE(mixScene->Flags, FLAG_ID_MIXERSCENE_CUTSCENETRIMMEDASSET) == AUD_TRISTATE_TRUE)
{
#if __BANK
caDisplayf("Overriding start offset from MixerScene for trimmed asset: original offset %f", audioOffsetTime);
#endif
audioOffsetTime = 0.f;
}
}
audioEvent.AudioOffset = (u32)((audioOffsetTime)*1000);
audioEvent.StartOffset = (u32)((audioOffsetTime+intermediateArray[i].triggerTime)*1000) + 1; //+1ms to circumvent fp rounding error when we do a concat jump.
if(isConcatSkip)
{
audioEvent.TriggerTime = (u32)((intermediateArray[timeToskip].triggerTime)*1000); //timeToskip;
}
else
{
audioEvent.TriggerTime = (u32)(intermediateArray[i].triggerTime*1000);
}
timeToskip = 0;
audioEvent.IsConcatSkip = isConcatSkip;
isConcatSkip = false;
#if __BANK
caDisplayf("Cutfile event: trigger time %u, object offset %f, event time %f, event %d, concat %d", audioEvent.TriggerTime, audioObj->GetOffset(), event->GetTime(), eventCount, concat);
#endif
#if 1//__BANK
formatf(audioEvent.TrackName, object->GetDisplayName().c_str());
formatf(audioEvent.SceneName, sceneName);
#endif
audioEventList.Grow() = audioEvent;
}
}
}
}
audCutSceneEvent * prevEvent = NULL;
for(int i=0; i<audioEventList.GetCount(); i++)
{
audCutSceneEvent * event = &audioEventList[i];
if(prevEvent)
{
//caAssertf((prevEvent->TriggerTime + prevEvent->Duration) == (event->TriggerTime -1), "Non concat-skip discontinuity when generating cutscene timeline for %s, prev trigger %d, prev duration %d, trigger %d", manager->GetCutsceneName(), prevEvent->TriggerTime, prevEvent->Duration, event->TriggerTime);
if( event->TriggerTime == (u32)(-1000) || (!event->IsConcatSkip && event->PartialHash == prevEvent->PartialHash))
{
event->PartialHash = 0;
continue;
}
}
prevEvent = event;
}
u32 totalDuration = (u32)(manager->GetTotalSeconds()*1000.f);
for(int i = audioEventList.GetCount()-1; i>=0; i--)
{
if(audioEventList[i].PartialHash)
{
audioEventList[i].Duration = totalDuration - m_EventList[i].GetCutsceneTimeOffset();
totalDuration -= m_EventList[i].Duration;
}
}
for(int i=0; i<audioEventList.GetCount(); i++)
{
if(audioEventList[i].PartialHash)
{
if(skipTimeInMs > audioEventList[i].GetCutsceneTimeOffset())
{
if(skipTimeInMs > audioEventList[i].GetCutsceneTimeOffset() + audioEventList[i].Duration)
{
continue;
}
else
{
audioEventList[i].StartOffset += (skipTimeInMs - audioEventList[i].GetCutsceneTimeOffset());
audioEventList[i].TriggerTime += (skipTimeInMs - audioEventList[i].GetCutsceneTimeOffset());
skipTimeInMs = 0;
}
}
else
{
skipTimeInMs = 0;
}
#if __BANK
caDisplayf("Event %s, Start offset %d, trigger time %d, Duration %d is concat skip %s", audioEventList[i].TrackName, audioEventList[i].StartOffset, audioEventList[i].TriggerTime, audioEventList[i].Duration, audioEventList[i].IsConcatSkip?"true":"false");
#endif
#if 0 // __ASSERT
u32 soundDuration = g_CutsceneAudioEntity.GetDuration(audioEventList[i].SceneName) - audioEventList[i].AudioOffset;
u32 cutsceneDuration = (u32)(manager->GetTotalSeconds()*1000.f);
caAssertf(soundDuration < cutsceneDuration, "Mismatch in cutscene/audio asset duration for audio %s : %d, cutscene %s : %d, please check that the assets match up (puls any EDITED or MASTERED versions)", audioEventList[i].SceneName, soundDuration, manager->GetCutsceneName(), cutsceneDuration);
#endif
if(audioEventList[i].TriggerTime == 0)
{
audioEventList[i].IsConcatSkip = false;
}
m_EventList[m_NumEvents++] = audioEventList[i];
}
}
if(m_NumEvents)
{
m_LastSoundTime = m_EventList[0].GetCutsceneTimeOffset();
}
m_State = INITIALIZED;
}
void audCutTrackCutScene::Update()
{
switch(m_State)
{
case audCutTrack::UNINITIALIZED:
break;
case audCutTrack::INITIALIZED:
{
if(g_CutsceneAudioEntity.CutsceneAudioIsAvailable())
{
//TODO check synched scenes aren't active when support is added for them
if(g_CutsceneAudioEntity.SlotIsFree(g_CutsceneAudioEntity.GetWaveslotIndexForPrepare()))
{
m_CutsceneLoadIndex = g_CutsceneAudioEntity.GetWaveslotIndexForPrepare();
}
else if(g_CutsceneAudioEntity.SlotIsFree(g_CutsceneAudioEntity.GetNextWaveslotIndexForPrepare()))
{
m_CutsceneLoadIndex = g_CutsceneAudioEntity.GetNextWaveslotIndexForPrepare();
}
if(m_CutsceneLoadIndex >= 0)
{
#if __BANK
caDisplayf("Begin prepare with load index %d, time %d, FC: %u", m_CutsceneLoadIndex, fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
m_State = audCutTrack::PREPARING;
SetIsControllingCutsceneAudio(true);
}
}
}
break;
case audCutTrack::PREPARING:
{
if(g_CutsceneAudioEntity.Prepare(m_EventList[m_LoadEventIndex], AUD_CUTSCENE_TYPE_CUTSCENE, m_CutsceneLoadIndex)!= AUD_PREPARING)
{
#if __BANK
caDisplayf("Prepared and activating cutscene track at load index %d, time %d, FC: %u", m_LoadEventIndex, fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
m_State = audCutTrack::PREPARED;
}
}
break;
case audCutTrack::PREPARED:
break;
case audCutTrack::ACTIVE:
{
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord() && CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->GetSceneHashString()->IsNotNull())
{
CReplayMgr::RecordFx<CCutSceneAudioPacket>(
CCutSceneAudioPacket(GetTrackTime(), m_StartCutsceneTime));
}
#endif // GTA_REPLAY
if(m_CutsceneLoadIndex != -1)
{
if(!g_CutsceneAudioEntity.IsPrepared(m_CutsceneLoadIndex))
{
g_CutsceneAudioEntity.Prepare(m_EventList[m_LoadEventIndex], AUD_CUTSCENE_TYPE_CUTSCENE, m_CutsceneLoadIndex);
}
/* if(!g_CutsceneAudioEntity.SlotIsFree(m_CutsceneLoadIndex) && g_CutsceneAudioEntity.IsPrepared(m_CutsceneLoadIndex) && g_CutsceneAudioEntity.GetPlayTimeMs(m_CutsceneLoadIndex) > 0)
{
g_CutsceneAudioEntity.HandleSeamlessPlay(m_CutsceneLoadIndex);
if(!g_CutsceneAudioEntity.SlotIsFree(m_CutscenePlayIndex))
{
g_CutsceneAudioEntity.Stop(false, m_CutscenePlayIndex);
}
HandlePlay();
}*/
/*else if(m_CutscenePlayIndex < 0 || g_CutsceneAudioEntity.SlotIsFree(m_CutscenePlayIndex)) //If other sound stops or trigger is missed lets just play the next sound
{
if(g_CutsceneAudioEntity.IsPrepared(m_CutsceneLoadIndex) && )
{
g_CutsceneAudioEntity.Play(m_CutsceneLoadIndex);
HandlePlay();
}
}*/
else if (GetTrackTime(true) >= m_EventList[m_LoadEventIndex].TriggerTime)
{
if (g_GameTimerCutscenes || PARAM_gametimercutscenes.Get())
{
HandlePlay();
}
else if(g_CutsceneAudioEntity.IsPrepared(m_CutsceneLoadIndex))
{
g_CutsceneAudioEntity.Stop(false, m_CutscenePlayIndex, 10);
if(g_CutsceneAudioEntity.SlotIsPlaying(m_CutsceneLoadIndex))
{
g_CutsceneAudioEntity.Play(m_CutsceneLoadIndex);
}
HandlePlay();
}
else
{
caErrorf("Want to do a concat jump but audio isn't prepared yet");
}
}
}
}
break;
case audCutTrack::ENDING:
{
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord() && CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->GetSceneHashString()->IsNotNull())
{
CReplayMgr::RecordFx<CCutSceneAudioPacket>(
CCutSceneAudioPacket(GetTrackTime(), m_StartCutsceneTime));
}
#endif // GTA_REPLAY
SetIsControllingCutsceneAudio(false);
if(m_CutscenePlayIndex <0 || g_CutsceneAudioEntity.SlotIsFree(m_CutscenePlayIndex))
{
Reset();
}
}
default:
break;
}
}
void audCutTrackCutScene::Stop()
{
if(m_CutsceneLoadIndex >= 0)
{
g_CutsceneAudioEntity.Stop(false, m_CutsceneLoadIndex);
}
if(m_CutscenePlayIndex >= 0)
{
g_CutsceneAudioEntity.Stop(false, m_CutscenePlayIndex);
}
Reset();
}
void audCutTrackCutScene::Stop(bool isSkipping, u32 release)
{
#if __BANK
caDisplayf("Cutscene track stop called at time %d, FC: %u, can overrun = %s, is skipping = %s", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount(), FMT_BOOL(m_CanOverrun), FMT_BOOL(isSkipping));
#endif
if(g_CutsceneAudioEntity.GetMixScene(g_CutsceneAudioEntity.GetNextWaveslotIndexForPrepare()) && AUD_GET_TRISTATE_VALUE(g_CutsceneAudioEntity.GetMixScene(g_CutsceneAudioEntity.GetNextWaveslotIndexForPrepare())->GetSceneSettings()->Flags, FLAG_ID_MIXERSCENE_CUTSCENEQUICKRELEASE) == AUD_TRISTATE_TRUE)
{
release = 10;
}
if(!m_CanOverrun || isSkipping)
{
if(m_CutsceneLoadIndex >= 0)
{
g_CutsceneAudioEntity.Stop(false, m_CutsceneLoadIndex, release);
}
if(m_CutscenePlayIndex >= 0)
{
g_CutsceneAudioEntity.Stop(false, m_CutscenePlayIndex, release);
}
Reset();
}
else // m_CanOverrun && !isSkipping
{
if(m_CutsceneLoadIndex >= 0)
{
if(g_CutsceneAudioEntity.GetMixScene(m_CutsceneLoadIndex))
{
g_CutsceneAudioEntity.GetMixScene(m_CutsceneLoadIndex)->Stop();
}
}
if(m_CutscenePlayIndex >= 0)
{
if(g_CutsceneAudioEntity.GetMixScene(m_CutscenePlayIndex))
{
g_CutsceneAudioEntity.GetMixScene(m_CutscenePlayIndex)->Stop();
}
}
}
}
void audCutTrackCutScene::HandlePlay()
{
if(g_CutsceneAudioEntity.SlotIsPlaying(m_CutsceneLoadIndex))
{
m_CutscenePlayIndex = m_CutsceneLoadIndex;
}
else
{
g_CutsceneAudioEntity.Stop(false, m_CutsceneLoadIndex);
m_CutscenePlayIndex = -1;
}
m_PlayEventIndex = m_LoadEventIndex;
m_LoadEventIndex++;
m_LastSoundTime = m_EventList[m_PlayEventIndex].GetCutsceneTimeOffset();
m_LastEngineTime = g_CutsceneAudioEntity.GetEngineTime();
if(m_EventList[m_PlayEventIndex].IsConcatSkip)
{
g_DidCutsceneJumpThisFrame = true;
}
if(m_LoadEventIndex < m_NumEvents)
{
m_CutsceneLoadIndex = (m_CutsceneLoadIndex+1)&1;
}
else
{
m_CutsceneLoadIndex = -1;
m_State = audCutTrack::ENDING;
}
}
void audCutTrackCutScene::Trigger()
{
if(m_State == audCutTrack::PREPARED)
{
CPed* pPlayer = CGameWorld::FindLocalPlayer();
if(pPlayer)
{
pPlayer->GetFootstepHelper().ResetFlags();
}
g_CutsceneAudioEntity.Play(m_CutsceneLoadIndex);
m_TriggerTime = g_CutsceneAudioEntity.GetEngineTime();
m_LastEngineTime = m_TriggerTime;
m_State = audCutTrack::ACTIVE;
HandlePlay();
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord() && CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->GetSceneHashString()->IsNotNull())
{
m_StartCutsceneTime = GetTrackTime();
CReplayMgr::RecordFx<CCutSceneAudioPacket>(
CCutSceneAudioPacket(GetTrackTime(), m_StartCutsceneTime));
}
#endif // GTA_REPLAY
}
}
u32 audCutTrackCutScene::GetTrackTime(bool isUpdate)
{
if(m_NumEvents > 0)
{
if(m_State <= PREPARED && m_State > UNINITIALIZED)
{
m_LastCutsceneTime = m_EventList[0].GetCutsceneTimeOffset();
return m_LastCutsceneTime;
}
u32 calcSoundTime = 0, catchupTime = 0;
u32 engineTime = g_CutsceneAudioEntity.GetEngineTime() - m_LastEngineTime + m_LastSoundTime;
u32 timeToUse = engineTime;
if(m_CutscenePlayIndex > -1)
{
s32 soundTime = g_CutsceneAudioEntity.GetPlayTimeIncludingOffsetMs(m_CutscenePlayIndex);
if((g_CutsceneAudioEntity.GetPlayTimeMs(m_CutscenePlayIndex)) > 0)
{
calcSoundTime = soundTime - m_EventList[m_PlayEventIndex].AudioOffset;
timeToUse = calcSoundTime;
m_LastSoundTime = calcSoundTime;
m_LastEngineTime = g_CutsceneAudioEntity.GetEngineTime();
if(!isUpdate && m_State == ACTIVE && calcSoundTime >= m_EventList[m_LoadEventIndex].TriggerTime)
{
if(g_CutsceneAudioEntity.IsPrepared(m_CutsceneLoadIndex))
{
g_CutsceneAudioEntity.Stop(false, m_CutscenePlayIndex, 10);
if(g_CutsceneAudioEntity.SlotIsPlaying(m_CutsceneLoadIndex))
{
g_CutsceneAudioEntity.Play(m_CutsceneLoadIndex);
}
HandlePlay();
timeToUse = m_LastSoundTime;
}
else
{
caErrorf("Want to do a concat jump but audio isn't prepared yet");
}
}
}
else
{
catchupTime = (u32)((g_CutsceneAudioEntity.GetEngineTime() - m_LastEngineTime )*g_CutsceneAudioCatchupFactor) + m_LastSoundTime;
timeToUse = catchupTime;
}
}
#if __BANK
caDebugf3("Cutscene time %d, m_State: %d, calcSoundTime: %d, catchupTime %d, engineTime %d, game time %d", timeToUse, m_State, calcSoundTime, catchupTime, engineTime, fwTimer::GetTimeInMilliseconds());
#endif
//Last line of defence against time going backwards...
if(timeToUse < m_LastCutsceneTime)
{
caErrorf("Cutscene time has gone backwards; last time (%u), current time (%u), FC:%u", m_LastSoundTime, timeToUse, fwTimer::GetFrameCount());
timeToUse = m_LastCutsceneTime + (u32)((g_CutsceneAudioEntity.GetEngineTime() - m_LastEngineTime )*g_CutsceneAudioCatchupFactor);
}
m_LastCutsceneTime = timeToUse;
return timeToUse;
}
else
{
if(m_State <= PREPARED && m_State > UNINITIALIZED)
{
return 0
#if !__FINAL
+ m_Scrubbingoffset
#endif
;
}
s32 offset = 0;
#if !__FINAL
if(m_Scrubbingoffset)
{
offset = m_Scrubbingoffset - m_TriggerTime;
}
else
#endif
{
offset = m_LastSoundTime - m_LastEngineTime;
}
#if __BANK
caDebugf3("Fake cutscene time %d, m_State: %d", g_CutsceneAudioEntity.GetEngineTime() + offset, m_State);
#endif
u32 timeToUse = g_CutsceneAudioEntity.GetEngineTime() + offset;
//Last line of defence against time going backwards...
if(timeToUse < m_LastCutsceneTime)
{
caErrorf("Cutscene time has gone backwards; last time (%u), current time (%u)", m_LastSoundTime, timeToUse);
timeToUse = m_LastCutsceneTime + (u32)((g_CutsceneAudioEntity.GetEngineTime() - m_LastEngineTime )*g_CutsceneAudioCatchupFactor);
}
m_LastCutsceneTime = timeToUse;
return timeToUse;
}
}
void audCutTrackCutScene::SkipToTime(u32 timInMs)
{
Stop();
Init(timInMs);
}
bool audCutTrackSynchedScene::IsPrepared()
{
caErrorf("Call audCutTrackSynchedScene::IsPrepared(u32 hash) instead");
return false;
}
void audCutTrackSynchedScene::Trigger()
{
u32 triggerTime = ~0U;
int indexToTrigger = -1;
for(int i=0; i<kNumAudSynchedSceneEvents; i++)
{
if(m_EventStates[i] == PREPARED && m_EventList[i].TriggerTime < triggerTime)
{
triggerTime = m_EventList[i].TriggerTime;
indexToTrigger = i;
}
}
if(indexToTrigger >= 0)
{
Trigger(indexToTrigger);
}
}
void audCutTrackSynchedScene::Stop()
{
for(int index = 0; index < kNumAudSynchedSceneEvents; index++)
{
if(m_CutsceneIndexes[index] >= 0)
{
g_CutsceneAudioEntity.Stop(false, m_CutsceneIndexes[index]);
}
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CSynchSceneStopAudioPacket>(
CSynchSceneStopAudioPacket(0));
}
#endif // GTA_REPLAY
}
//Reset all the synch scene, this makes sure SetIsControllingCutsceneAudio is cleaned up.
Reset();
}
void audCutTrackSynchedScene::Stop(u32 hash)
{
int index = GetEventIndexFromHash(hash);
if(index >= 0)
{
if(m_CutsceneIndexes[index] >= 0)
{
g_CutsceneAudioEntity.Stop(false, m_CutsceneIndexes[index]);
}
Reset(index);
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CSynchSceneStopAudioPacket>(
CSynchSceneStopAudioPacket(hash));
}
#endif // GTA_REPLAY
}
}
bool audCutTrackSynchedScene::IsPrepared(const char *audioName)
{
int index = GetEventIndexFromHash(atStringHash(audioName));
if(index >= 0 && m_CutsceneIndexes[index] >=0)
{
return m_EventStates[index] >= audCutTrack::PREPARED;
}
else
{
Init(audioName);
return false;
}
}
void audCutTrackSynchedScene::Trigger(u32 nameHash)
{
int index = GetEventIndexFromHash(nameHash);
if(index >= 0)
{
if(m_EventStates[index] == PREPARED)
{
g_CutsceneAudioEntity.Play(m_CutsceneIndexes[index]);
m_TriggerTimes[index] = g_CutsceneAudioEntity.GetEngineTime();
m_EventStates[index] = audCutTrack::ENDING; //Synched scenes only have one event but multiple scenes can be prepared and triggered
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CSynchSceneAudioPacket>(
CSynchSceneAudioPacket(nameHash, m_EventList[index].TrackName, GetSceneTime(atFinalizeHash(m_EventList[index].ScenePartialHash)), m_UseScenePosistion[index], m_UseSceneEntity[index], VEC3V_TO_VECTOR3(GetPlayPosition(index))));
}
#endif // GTA_REPLAY
if(!g_CutsceneAudioEntity.SlotIsPlaying(m_CutsceneIndexes[index]))
{
g_CutsceneAudioEntity.Stop(false, m_CutsceneIndexes[index]);
m_CutsceneIndexes[index] = -1;
}
}
}
}
int audCutTrackSynchedScene::GetEventIndexFromHash(u32 nameHash)
{
for(int i=0; i< kNumAudSynchedSceneEvents; i++)
{
if(atFinalizeHash(m_EventList[i].ScenePartialHash) == nameHash)
{
return i;
}
}
return -1;
}
bool audCutTrackSynchedScene::HasSyncSceneBeenInitialized(const char* sceneName)
{
int sceneIndex = GetEventIndexFromHash(atStringHash(sceneName));
if(sceneIndex < 0)
{
return false;
}
if(m_EventStates[sceneIndex] >= INITIALIZED && m_EventStates[sceneIndex] <= ENDING )
{
return true;
}
return false;
}
bool audCutTrackSynchedScene::HasSyncSceneBeenTriggered(const char* sceneName)
{
int sceneIndex = GetEventIndexFromHash(atStringHash(sceneName));
if(sceneIndex < 0)
{
return false;
}
if(m_EventStates[sceneIndex] == ENDING )
{
return true;
}
return false;
}
void audCutTrackSynchedScene::Init(const char* sceneName, u32 skipMs, u32 parentHash)
{
int sceneIndex = -1;
if(GetEventIndexFromHash(atStringHash(sceneName)) >= 0)
{
return;
}
for(int i=0; i<kNumAudSynchedSceneEvents; i++)
{
if(m_EventStates[i] == UNINITIALIZED)
{
sceneIndex = i;
break;
}
}
if(sceneIndex<0)
{
caErrorf("Trying to init synched scene audio %s but there's no free slots", sceneName);
return;
}
if(parentHash)
{
int parentIndex = GetEventIndexFromHash(parentHash);
if(parentIndex >= 0)
{
m_SynchSceneToTrigger[parentIndex] = sceneIndex;
}
}
m_EventList[sceneIndex].StartOffset = skipMs;
char soundName[64];
formatf(soundName, "%s%s", g_CutsceneSoundNamePrefix, sceneName);
strupr(soundName);
char * validationStr = NULL;
validationStr = strstr(soundName, "_CUSTOM");
if(validationStr)
{
caAssertf(0, "Cutscene audio (%s) should not be named _CUSTOM, please bug Default Animation Resource", soundName);
validationStr[0] = '\0';
}
char mixSceneName[64];
char nonSeqName[64];
//Remove _SEQ for scenes if present
formatf(nonSeqName, "%s", sceneName);
char * seqStr = strstr(nonSeqName, "_SEQ");
if(seqStr)
{
seqStr[0] = '\0';
}
formatf(mixSceneName, "%s", nonSeqName);
m_EventList[sceneIndex].PartialHash = atPartialStringHash(soundName);
m_EventList[sceneIndex].ScenePartialHash = atPartialStringHash(mixSceneName);
m_EventList[sceneIndex].TriggerTime = g_CutsceneAudioEntity.GetEngineTime();
#if __BANK
caDisplayf("Synched scene event %s: init time %u, FC: %u", sceneName, fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
#if 1//__BANK
formatf(m_EventList[sceneIndex].TrackName, sceneName);
formatf(m_EventList[sceneIndex].SceneName, mixSceneName);
#endif
m_EventStates[sceneIndex] = INITIALIZED;
}
void audCutTrackSynchedScene::Update()
{
for(int i=0; i<kNumAudSynchedSceneEvents; i++)
{
switch(m_EventStates[i])
{
case audCutTrack::UNINITIALIZED:
break;
case audCutTrack::INITIALIZED:
{
if(g_CutsceneAudioEntity.CutsceneAudioIsAvailable())
{
//TODO check synched scenes aren't active when support is added for them
if(g_CutsceneAudioEntity.SlotIsFree(g_CutsceneAudioEntity.GetWaveslotIndexForPrepare()))
{
m_CutsceneIndexes[i] = g_CutsceneAudioEntity.GetWaveslotIndexForPrepare();
}
else if(g_CutsceneAudioEntity.SlotIsFree(g_CutsceneAudioEntity.GetNextWaveslotIndexForPrepare()))
{
m_CutsceneIndexes[i] = g_CutsceneAudioEntity.GetNextWaveslotIndexForPrepare();
}
if(m_CutsceneIndexes[i] >= 0)
{
#if __BANK
caDisplayf("Begin SS prepare with load index %d, time %d, FC:%u", m_CutsceneIndexes[i], fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
m_EventStates[i] = audCutTrack::PREPARING;
SetIsControllingCutsceneAudio(true);
}
}
}
break;
case audCutTrack::PREPARING:
{
if(g_CutsceneAudioEntity.Prepare(m_EventList[i], AUD_CUTSCENE_TYPE_SYNCHED_SCENE, m_CutsceneIndexes[i]) != AUD_PREPARING)
{
#if __BANK
caDisplayf("Prepared and activating synched scene track at load index %d, sound %s (%p), time %d, FC: %u", i, g_CutsceneAudioEntity.GetSoundName(m_CutsceneIndexes[i]), g_CutsceneAudioEntity.GetSound(m_CutsceneIndexes[i]), fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
#endif
m_EventStates[i] = audCutTrack::PREPARED;
SetIsControllingCutsceneAudio(false);
}
}
break;
case audCutTrack::PREPARED:
break;
case audCutTrack::ACTIVE:
{
caErrorf("Synched scene audio %s is ACTIVE but should be ENDING", m_EventList[i].TrackName);
}
break;
case audCutTrack::ENDING:
{
#if GTA_REPLAY
if(CReplayMgr::ShouldRecord())
{
CReplayMgr::RecordFx<CSynchSceneAudioPacket>(
CSynchSceneAudioPacket(atFinalizeHash(m_EventList[i].ScenePartialHash), m_EventList[i].TrackName, GetSceneTime(atFinalizeHash(m_EventList[i].ScenePartialHash)), m_UseScenePosistion[i], m_UseSceneEntity[i], VEC3V_TO_VECTOR3(GetPlayPosition(i))), GetPlayEntity(i));
}
#endif // GTA_REPLAY
if(m_CutsceneIndexes[i] <0 || g_CutsceneAudioEntity.SlotIsFree(m_CutsceneIndexes[i]))
{
if(m_SynchSceneToTrigger[i] > -1)
{
g_CutsceneAudioEntity.TriggerSyncScene(atFinalizeHash(m_EventList[m_SynchSceneToTrigger[i]].ScenePartialHash));
}
else if(m_TriggerCutscene)
{
g_CutsceneAudioEntity.TriggerCutscene();
}
m_CutsceneIndexes[i] = -1;
//Reset(i);
}
}
default:
break;
}
}
}
void audCutTrackSynchedScene::SkipToTime(u32 audioHash, u32 skipMs)
{
int eventIndex = GetEventIndexFromHash(audioHash);
if(eventIndex >=0)
{
char sceneName[128] = {0};
formatf(sceneName, "%s", m_EventList[eventIndex].SceneName);
Stop(audioHash);
Init(sceneName, skipMs);
}
}
void audCutTrackSynchedScene::Reset()
{
audCutTrack::Reset();
for(int i=0; i<kNumAudSynchedSceneEvents; i++)
{
Reset(i);
}
}
void audCutTrackSynchedScene::Reset(int index)
{
m_EventList[index].Init();
m_EventStates[index] = UNINITIALIZED;
m_CutsceneIndexes[index] = -1;
m_TriggerTimes[index] = 0;
m_SynchSceneToTrigger[index] = -1;
m_TriggerCutscene = false;
m_LastSoundTimes[index] = 0;
m_LastEngineTimes[index] = 0;
m_UseScenePosistion[index] = false;
m_UseSceneEntity[index] = false;
}
void audCutTrackSynchedScene::ClearUnusedEntities()
{
for(int i=0; i < kNumAudSynchedSceneEvents; i++)
{
if(!m_UseSceneEntity[i] && m_SceneEntity[i])
{
m_SceneEntity[i] = NULL;
}
}
}
u32 audCutTrackSynchedScene::GetNextSynchedSceneInQueue()
{
u32 triggerTime = ~0U;
int index = -1;
for(int i=0; i<kNumAudSynchedSceneEvents; i++)
{
if(m_EventStates[i] == PREPARED && m_EventList[i].TriggerTime < triggerTime)
{
index = i;
triggerTime = m_EventList[i].TriggerTime;
}
}
if(index < 0)
{
caErrorf("No prepared synched scene audio available");
return 0;
}
return atFinalizeHash(m_EventList[index].ScenePartialHash);
}
u32 audCutTrackSynchedScene::GetSceneTime(u32 audioHash)
{
int index = GetEventIndexFromHash(audioHash);
if(index >= 0)
{
if(m_EventStates[index] <= audCutTrack::PREPARED)
{
return 0;
}
u32 calcSoundTime = 0;
u32 engineTime = g_CutsceneAudioEntity.GetEngineTime() - m_TriggerTimes[index] - m_LastEngineTimes[index] + m_LastSoundTimes[index];
u32 timeToUse = engineTime;
if(m_CutsceneIndexes[index] > -1)
{
s32 soundTime = g_CutsceneAudioEntity.GetPlayTimeMs(m_CutsceneIndexes[index]) + m_EventList[index].StartOffset;
if(soundTime > 0)
{
calcSoundTime = soundTime;
timeToUse = calcSoundTime;
m_LastSoundTimes[index] = calcSoundTime;
m_LastEngineTimes[index] = g_CutsceneAudioEntity.GetEngineTime();
}
}
#if __BANK
caDebugf3("sound time %u, engine time %u, timeToUse %u, frameTime %u", calcSoundTime, engineTime, timeToUse, fwTimer::GetTimeInMilliseconds());
#endif
return timeToUse;
}
#if __BANK
caDebugf3("engine time %u, ", g_CutsceneAudioEntity.GetEngineTime());
#endif
return g_CutsceneAudioEntity.GetEngineTime();
}
u32 audCutTrackSynchedScene::GetSceneAudioDuration(u32 hash)
{
int index = GetEventIndexFromHash(hash);
if(index >= 0)
{
const StreamingSound *streamingSound = SOUNDFACTORY.DecompressMetadata<StreamingSound>(atStringHash("_CUSTOM", m_EventList[index].PartialHash));
if (streamingSound)
{
// Compute our actual start offset
return streamingSound->Duration;
}
streamingSound = SOUNDFACTORY.DecompressMetadata<StreamingSound>(atFinalizeHash(m_EventList[index].PartialHash));
if (streamingSound)
{
// Compute our actual start offset
return streamingSound->Duration;
}
}
return 0;
}
void audCutTrackSynchedScene::ActivateLooping(u32 hash)
{
int index = GetEventIndexFromHash(hash);
if(index > -1)
{
}
}
void audCutTrackSynchedScene::SetEntity(u32 hash, CEntity * entity)
{
int index = GetEventIndexFromHash(hash);
if(index > -1)
{
m_SceneEntity[index] = entity;
m_UseSceneEntity[index] = true;
}
}
void audCutTrackSynchedScene::SetPosition(u32 hash, Vec3V_In position)
{
int index = GetEventIndexFromHash(hash);
if(index > -1)
{
m_ScenePosition[index] = position;
m_UseScenePosistion[index] = true;
}
}