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

340 lines
12 KiB
C++

//
// audio/audioentity.cpp
//
// Copyright (C) 1999-2006 Rockstar Games. All Rights Reserved.
//
#include "audioentity.h"
#include "northaudioengine.h"
#include "audio/environment/environmentgroup.h"
#include "audioengine/engine.h"
#include "control/replay/replay.h"
#include "control/replay/ReplaySettings.h"
#include "control/replay/Audio/SoundPacket.h"
#include "system/bit.h"
AUDIO_OPTIMISATIONS()
atFixedArray<naDeferredSound, naAudioEntity::sm_MaxNumDeferredSounds> naAudioEntity::sm_DeferredSoundList;
u16 naAudioEntity::sm_BaseAnimLoopTimeout = 100;
sysCriticalSectionToken naAudioEntity::sm_EnvGroupCritSec;
namespace rage
{
extern bool g_WarnOnMissingSounds;
}
naAudioEntity::naAudioEntity() : m_EnvironmentGroup(NULL), m_GroupRequests(0)
{
}
naAudioEntity::~naAudioEntity()
{
naAssertf(!sysThreadType::IsUpdateThread() || !audNorthAudioEngine::IsAudioUpdateCurrentlyRunning(), "Deleted an audio entity from the main thread while the audio update thread is running.");
}
void naAudioEntity::Init()
{
naAssertf(!sysThreadType::IsUpdateThread() || !audNorthAudioEngine::IsAudioUpdateCurrentlyRunning(), "Initialised an audio entity from the main thread while the audio update thread is running.");
fwAudioEntity::Init();
m_AnimLoopTimeout = sm_BaseAnimLoopTimeout;
}
void naAudioEntity::Shutdown()
{
naAssertf(!sysThreadType::IsUpdateThread() || !audNorthAudioEngine::IsAudioUpdateCurrentlyRunning(), "Shutting down an audio entity from the main thread while the audio update thread is running.");
fwAudioEntity::Shutdown();
}
void naAudioEntity::InitClass()
{
sm_DeferredSoundList.Reset();
}
void naAudioEntity::CreateEnvironmentGroup(const char* debugIdentifier,u32 type )
{
if(!m_EnvironmentGroup)
{
m_EnvironmentGroup = naEnvironmentGroup::Allocate(debugIdentifier);
}
AddEnvironmentGroupRequest(type);
}
void naAudioEntity::RemoveEnvironmentGroup(u32 type)
{
RemoveEnvironmentGroupRequest(type);
if(m_EnvironmentGroup)
{
if(!m_GroupRequests || type==naAudioEntity::SHUTDOWN)
{
m_EnvironmentGroup->SetEntity(NULL);
m_EnvironmentGroup = NULL;
m_GroupRequests = 0;
}
}
}
naEnvironmentGroup* naAudioEntity::GetEnvironmentGroup(bool UNUSED_PARAM(create)) const
{
return m_EnvironmentGroup;
}
naEnvironmentGroup* naAudioEntity::GetEnvironmentGroup(bool UNUSED_PARAM(create))
{
return m_EnvironmentGroup;
}
void naAudioEntity::AddEnvironmentGroupRequest(u32 type)
{
SYS_CS_SYNC(sm_EnvGroupCritSec);
naAssertf(!sysThreadType::IsUpdateThread() || !audNorthAudioEngine::IsAudioUpdateCurrentlyRunning(), "Can't add env group request from the main thread while the audio update thread is running.");
if(m_EnvironmentGroup && (m_GroupRequests & type) == 0)
{
m_EnvironmentGroup->AddSoundReference();
}
m_GroupRequests |= type;
}
void naAudioEntity::RemoveEnvironmentGroupRequest(u32 type)
{
SYS_CS_SYNC(sm_EnvGroupCritSec);
naAssertf(!sysThreadType::IsUpdateThread() || !audNorthAudioEngine::IsAudioUpdateCurrentlyRunning(), "Can't remove env group request from the main thread while the audio update thread is running.");
if(m_EnvironmentGroup)
{
if(type == naAudioEntity::SHUTDOWN)
{
// Need to remove all request refs
const u32 numRefs = CountOnBits(m_GroupRequests);
for(u32 i = 0; i < numRefs; i++)
{
m_EnvironmentGroup->RemoveSoundReference();
}
}
else if(m_GroupRequests & type)
{
m_EnvironmentGroup->RemoveSoundReference();
}
}
m_GroupRequests &= ~type;
}
void naAudioEntity::ProcessAnimEvents()
{
for(int i=0; i< AUDENTITY_NUM_ANIM_EVENTS; i++)
{
if(m_AnimEvents[i])
{
audSoundInitParams initParams;
initParams.TrackEntityPosition = true;
initParams.AllowLoad = false;
initParams.EnvironmentGroup = m_EnvironmentGroup;
CreateAndPlaySound(m_AnimEvents[i], &initParams);
m_AnimEvents[i] = 0;
REPLAY_ONLY(CReplayMgr::ReplayRecordSound(m_AnimEvents[i], &initParams, GetOwningEntity()));
}
}
if(m_AnimLoopWasStarted && !m_AnimLoopWasStopped)
{
audSoundInitParams initParams;
initParams.TrackEntityPosition = true;
initParams.AllowLoad = false;
initParams.EnvironmentGroup = m_EnvironmentGroup;
CreateAndPlaySound_Persistent(m_AnimLoopHash, &m_AnimLoop, &initParams);
REPLAY_ONLY(CReplayMgr::ReplayRecordSoundPersistant(m_AnimLoopHash, &initParams, m_AnimLoop, GetOwningEntity(), eNoGlobalSoundEntity);)
}
else if(m_AnimLoop && (m_AnimLoopWasStopped || g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0) > (m_AnimLoopTriggerTime + m_AnimLoopTimeout)))
{
m_AnimLoop->StopAndForget();
m_AnimLoopHash = 0;
}
m_AnimLoopWasStarted = false;
m_AnimLoopWasStopped = false;
}
void naAudioEntity::HandleLoopingAnimEvent(const audAnimEvent & event)
{
if(event.isStart)
{
if(!m_AnimLoop || event.hash == m_AnimLoopHash)
{
if(!m_AnimLoop)
{
m_AnimLoopWasStarted = true;
m_AnimLoopHash = event.hash;
}
m_AnimLoopTriggerTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
}
else
{
BANK_ONLY(audWarningf("Trying to start anim looping event with sound %s but we're already playing sound %s. Only one allowed at a time", g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(event.hash), g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_AnimLoopHash));)
}
}
else
{
if(m_AnimLoop && naVerifyf(event.hash == m_AnimLoopHash, "Trying to stop anim looping event with sound %s but we're playing sound %s. Only one allowed at a time", g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(event.hash), g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectName(m_AnimLoopHash)))
{
m_AnimLoopWasStopped = true;
}
}
}
bool naAudioEntity::CreateDeferredSound(const char *soundName, const CEntity* entity, const audSoundInitParams *initParams /* = NULL */,
const bool useEnvGroup /* = false */, const bool useTracker /* = false */, const bool invalidateEntity /* = false */, const bool persistAcrossClears /* = false */)
{
const bool success = CreateDeferredSound(atStringHash(soundName), entity, initParams, useEnvGroup, useTracker, invalidateEntity, persistAcrossClears);
#if __BANK
if(g_WarnOnMissingSounds && !success)
{
audWarningf("Missing sound %s", soundName);
}
#endif
return success;
}
bool naAudioEntity::CreateDeferredSound(const u32 soundHash, const CEntity* entity, const audSoundInitParams *initParams /* = NULL */,
const bool useEnvGroup /* = false */, const bool useTracker /* = false */, const bool invalidateEntity /* = false */, const bool persistAcrossClears /* = false */)
{
return CreateDeferredSound(SOUNDFACTORY.GetMetadataManager().GetObjectMetadataRefFromHash(soundHash), entity, initParams, useEnvGroup, useTracker, invalidateEntity, persistAcrossClears);
}
bool naAudioEntity::CreateDeferredSound(const audMetadataRef metadataRef, const CEntity* entity, const audSoundInitParams *initParams /* = NULL */,
const bool useEnvGroup /* = false */, const bool useTracker /* = false */, const bool invalidateEntity /* = false */, const bool persistAcrossClears /* = false */)
{
#if __ASSERT
if(initParams)
{
Assert(initParams->VolumeCurveScale != 0.f);
Assertf(initParams->Volume >= g_SilenceVolume*3.f, "Invalid requested volume: %f", initParams->Volume);
Assertf(initParams->Volume <= 24.f,"Invalid requested volume: %f", initParams->Volume);
Assertf(!initParams->EnvironmentGroup, "Cannot set environmentGroup on a deferred sound, it will be created from the CEntity");
Assertf(!initParams->Tracker, "Cannot set tracker on a deferred sound, it will be created from the CEntity");
if(useEnvGroup)
{
Assertf(entity, "Can't create an environmentGroup in a deferred sound without a CEntity");
}
if(useTracker)
{
Assertf(entity, "Can't create an audTracker in a deferred sound without a CEntity");
}
}
#endif
if(metadataRef != g_NullSoundRef && metadataRef.IsValid() &&
naVerifyf(!sm_DeferredSoundList.IsFull(), "Creating more than %d deferred sounds in 1 frame - sound will not be played", sm_MaxNumDeferredSounds))
{
#if __BANK
audDisplayf("Triggering deferred sound %s (%u) from entity %s", SOUNDFACTORY.GetMetadataManager().GetObjectName(metadataRef), metadataRef.Get(), entity ? entity->GetModelName() : "(null)");
#endif
naDeferredSound& deferredSound = sm_DeferredSoundList.Append();
deferredSound.metadataRef = metadataRef;
deferredSound.audioEntity = this;
deferredSound.entity = entity;
deferredSound.useEnvGroup = useEnvGroup;
deferredSound.useTracker = useTracker;
deferredSound.invalidateEntity = invalidateEntity;
deferredSound.persistAcrossClears = persistAcrossClears;
if(initParams)
{
deferredSound.initParams = *initParams;
}
else
{
audSoundInitParams defaultInitParams;
deferredSound.initParams = defaultInitParams;
}
return true;
}
return false;
}
void naAudioEntity::ProcessDeferredSounds()
{
const s32 timeStepMs = static_cast<s32>(fwTimer::GetTimeStepInMilliseconds());
for(s32 i = sm_DeferredSoundList.GetCount() - 1; i >= 0; i--)
{
const bool needCEntity = sm_DeferredSoundList[i].useEnvGroup || sm_DeferredSoundList[i].useTracker;
const bool isAudEntityDeleted = sm_DeferredSoundList[i].audioEntity == NULL || sm_DeletedEntities.Find(sm_DeferredSoundList[i].audioEntity) != -1;
#if __ASSERT
if(isAudEntityDeleted && needCEntity && sm_DeferredSoundList[i].entity)
{
bool entityPendingDeletion = sm_DeferredSoundList[i].entity->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD);
if(sm_DeferredSoundList[i].entity->GetIsTypeVehicle())
{
const CVehicle* vehicle = static_cast<const CVehicle*>(sm_DeferredSoundList[i].entity.Get());
entityPendingDeletion |= vehicle->GetIsInReusePool();
}
naAssertf(!entityPendingDeletion, "audEntity has been deleted or is in deleted list, but CEntity: %s has not in ProcessDeferredSounds. Sound metadataRef: %s",
sm_DeferredSoundList[i].entity ? sm_DeferredSoundList[i].entity->GetModelName() : "NULL entity",
audNorthAudioEngine::GetMetadataManager().GetObjectName(sm_DeferredSoundList[i].metadataRef));
}
#endif
if(!isAudEntityDeleted && (!needCEntity || (needCEntity && sm_DeferredSoundList[i].entity)))
{
// Evaluate the first part of a long predelay at the game level, before we create any sounds
if(sm_DeferredSoundList[i].initParams.Predelay > 100)
{
sm_DeferredSoundList[i].initParams.Predelay -= Min(timeStepMs, sm_DeferredSoundList[i].initParams.Predelay);
}
else
{
if(sm_DeferredSoundList[i].useEnvGroup)
{
sm_DeferredSoundList[i].initParams.EnvironmentGroup = sm_DeferredSoundList[i].entity->GetAudioEnvironmentGroup();
}
if(sm_DeferredSoundList[i].useTracker)
{
sm_DeferredSoundList[i].initParams.Tracker = sm_DeferredSoundList[i].entity->GetPlaceableTracker();
}
audSound *sound;
sm_DeferredSoundList[i].audioEntity->CreateSound_LocalReference(sm_DeferredSoundList[i].metadataRef, &sound, &sm_DeferredSoundList[i].initParams);
if(sound)
{
sound->PrepareAndPlay(const_cast<audWaveSlot*>(sm_DeferredSoundList[i].initParams.WaveSlot),
sm_DeferredSoundList[i].initParams.AllowLoad,
sm_DeferredSoundList[i].initParams.PrepareTimeLimit);
if(sm_DeferredSoundList[i].invalidateEntity)
{
sound->InvalidateEntity();
}
if(sm_DeferredSoundList[i].persistAcrossClears)
{
sound->GetRequestedSettings()->SetShouldPersistOverClears(true);
}
}
sm_DeferredSoundList.DeleteFast(i);
}
}
else
{
sm_DeferredSoundList.DeleteFast(i);
}
}
}
#if GTA_REPLAY
void naAudioEntity::ClearDeferredSoundsForReplay()
{
for(s32 i = sm_DeferredSoundList.GetCount() - 1; i >= 0; i--)
{
if(!sm_DeferredSoundList[i].persistAcrossClears)
{
sm_DeferredSoundList.Delete(i);
}
}
}
#endif //GTA_REPLAY