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

1537 lines
44 KiB
C++

//
// audio/radioslot.cpp
//
// Copyright (C) 1999-2007 Rockstar Games. All Rights Reserved.
//
#include "audiodefines.h"
#if NA_RADIO_ENABLED
#include "radioslot.h"
#include "streamslot.h"
#include "grcore/debugdraw.h"
#include "radioaudioentity.h"
#include "radiostation.h"
#include "northaudioengine.h"
#include "audio/environment/environment.h"
#include "audioengine/engine.h"
#include "vehicles/vehicle.h"
#include "Cutscene/CutSceneManagerNew.h"
#include "debugaudio.h"
#include "audiohardware/driverdefs.h"
AUDIO_MUSIC_OPTIMISATIONS()
enum audRadioSlotStates
{
RADIO_SLOT_IDLE,
RADIO_SLOT_STARTING,
RADIO_SLOT_PLAYING,
RADIO_SLOT_STOPPING,
RADIO_SLOT_RETUNING,
RADIO_SLOT_RETUNING_STOPPING_OLD,
RADIO_SLOT_RETUNING_STARTING_NEW
};
audRadioStation *audRadioSlot::sm_RetuneStation = NULL;
audRadioSlot *audRadioSlot::sm_RadioSlots[g_MaxRadioSlots];
bank_float g_PlayerPositionedRadioLinVol = 4.f;
bank_float g_PlayerPositionedRadioSoftTopLinVol = 6.f;
f32 g_PositionedPlayerVehicleRadioReverbSmall = 1.0f;
f32 g_PositionedPlayerVehicleRadioReverbMedium = 0.37f;
f32 g_PositionedPlayerVehicleRadioReverbLarge = 0.0f;
bool g_PositionedPlayerVehicleRadioEnabled = false;
bank_float g_CutsceneRadioLeakage = 0.5f;
BANK_ONLY(extern bool g_DebugSimulateStreamSlotStarvation;)
u32 audRadioSlot::sm_SoundBucketId = ~0U;
void audRadioSlot::InitClass(void)
{
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
sm_RadioSlots[slotIndex] = rage_new audRadioSlot(slotIndex);
}
sm_SoundBucketId = audSound::ReserveBucket();
}
void audRadioSlot::ShutdownClass(void)
{
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
if(sm_RadioSlots[slotIndex])
{
delete sm_RadioSlots[slotIndex];
sm_RadioSlots[slotIndex] = NULL;
}
}
}
audRadioSlot *audRadioSlot::GetSlot(u32 index)
{
naCErrorf(index < g_MaxRadioSlots, "Index passed into GetSlot is out of bounds");
return ((index < g_MaxRadioSlots) ? sm_RadioSlots[index] : NULL);
}
audRadioSlot *audRadioSlot::FindSlotByStation(const audRadioStation *station)
{
audRadioSlot *slot;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
slot = sm_RadioSlots[slotIndex];
if(slot && (slot->GetStation() == station))
{
return slot;
}
}
return NULL;
}
audRadioSlot *audRadioSlot::FindSlotByEmitter(audRadioEmitter *emitter)
{
audRadioSlot *slot;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
slot = sm_RadioSlots[slotIndex];
if(slot && (slot->HasEmitter(emitter)))
{
return slot;
}
}
return NULL;
}
audRadioSlot *audRadioSlot::FindFreeSlot(u32 priority)
{
audRadioSlot *slot;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
slot = sm_RadioSlots[slotIndex];
if(slot && slot->IsFree(priority == audStreamSlot::STREAM_PRIORITY_PLAYER_RADIO))
{
//We have a free radio slot, so now lets see if we have a free stream slot.
if(slot->HasStreamSlot(0) || slot->HasStreamSlot(1))
{
return slot;
}
else if(slot->AllocateStreamSlot(0, priority))
{
return slot;
}
}
}
return NULL;
}
audRadioSlot *audRadioSlot::FindSlotToOverride(u32 priority)
{
//Find the slot with the lowest priority.
u32 lowestPriority = priority;
audRadioSlot *lowestPrioritySlot = NULL;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
audRadioSlot *slot = sm_RadioSlots[slotIndex];
if(slot &&
(slot->GetState() == RADIO_SLOT_IDLE || slot->GetState() == RADIO_SLOT_PLAYING || slot->GetState() == RADIO_SLOT_RETUNING))
{
u32 slotPriority = slot->GetPriority();
if(slotPriority < lowestPriority)
{
lowestPriority = slotPriority;
lowestPrioritySlot = slot;
}
}
}
if(lowestPrioritySlot)
{
//We have found a slot we can override, so now lets make sure we have a stream slot.
if(lowestPrioritySlot->HasStreamSlot(0) || lowestPrioritySlot->HasStreamSlot(1))
{
return lowestPrioritySlot;
}
else if(lowestPrioritySlot->AllocateStreamSlot(0, priority))
{
return lowestPrioritySlot;
}
}
return NULL;
}
audRadioSlot *audRadioSlot::FindSlotWithLeastEmitters(void)
{
u32 emitterIndex;
u32 numEmittersPerSlot;
u32 minEmittersPerSlot = g_NumRadioSlotEmitters + 1;
audRadioSlot *minEmittersSlot = NULL;
audRadioSlot *slot;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
slot = sm_RadioSlots[slotIndex];
numEmittersPerSlot = 0;
for(emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(slot && (slot->GetEmitter(emitterIndex) != NULL))
{
numEmittersPerSlot++;
}
}
if(numEmittersPerSlot < minEmittersPerSlot)
{
minEmittersPerSlot = numEmittersPerSlot;
minEmittersSlot = slot;
}
}
return minEmittersSlot;
}
void audRadioSlot::UpdateSlots(u32 timeInMs)
{
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
sm_RadioSlots[slotIndex]->Update(timeInMs);
}
}
void audRadioSlot::SkipForwardSlots(u32 timeToSkipMs)
{
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
sm_RadioSlots[slotIndex]->SkipForward(timeToSkipMs);
}
}
void audRadioSlot::SetPositionedPlayerVehicleRadioEmitterEnabled(bool enabled)
{
g_PositionedPlayerVehicleRadioEnabled = enabled;
}
s32 audRadioSlot::GetSlotIndex(audRadioSlot* slot)
{
for (u32 i = 0; i < g_MaxRadioSlots; i++)
{
if (sm_RadioSlots[i] == slot)
{
return i;
}
}
return -1;
}
bool audRadioSlot::RequestRadioStationForEmitter(const audRadioStation *station, audRadioEmitter *emitter, u32 priority, s32 requestedPCMChannel)
{
BANK_ONLY(const bool isClubEmitter = emitter && emitter->IsClubEmitter());
BANK_ONLY(if (isClubEmitter) { audDisplayf("[CLUB EMITTERS] Club radio emitter requesting slot for station %s", station ? station->GetName() : "NULL"); })
audRadioSlot *slot = FindSlotByEmitter(emitter);
if(slot)
{
//We are already playing radio for this emitter.
if(slot->GetStation() == station || (slot->IsRetuning() && slot->GetStationPendingRetune() == station))
{
//We are already playing the correct station.
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Emitter already playing station %s from slot %d", station ? station->GetName() : "NULL", GetSlotIndex(slot));})
return true;
}
else
{
//We are playing a different station for this emitter, so remove it and allow it to be allocated the correct station below.
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Emitter already playing from slot %d, but using different station (%s)", GetSlotIndex(slot), slot->GetStation() ? slot->GetStation()->GetName() : "NULL");})
slot->RemoveEmitter(emitter, false);
}
}
bool isPlayerRadio = (priority == audStreamSlot::STREAM_PRIORITY_PLAYER_RADIO);
//Check if we're already playing this station.
slot = FindSlotByStation(station);
if(slot)
{
//Only add new emitters to radio slots that are playing (or about to play.)
u32 state = slot->GetState();
if((state == RADIO_SLOT_STARTING) || (state == RADIO_SLOT_PLAYING))
{
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Slot %d already playing station - adding...", GetSlotIndex(slot));})
if(slot->AddEmitter(emitter, isPlayerRadio, requestedPCMChannel, station))
{
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Successfully added emitter to slot %d", GetSlotIndex(slot));})
return true;
}
}
//We're already playing the requested station, but we couldn't add this emitter.
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Failed to add emitter to slot %d", GetSlotIndex(slot));})
return false;
}
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] No slot found - searching for free slot...");})
//Check if we have a free radio slot (that's been free for a while.)
slot = FindFreeSlot(priority);
if(slot)
{
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Found free slot (%d), starting radio playback", GetSlotIndex(slot));})
//We're not already playing the requested station, but we have a free slot.
slot->AddEmitter(emitter, false, requestedPCMChannel, station);
slot->StartRadioPlayback(station);
return true;
}
else
{
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] No free slots, searching for slot to override");})
slot = FindSlotToOverride(priority);
if(slot)
{
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Found slot to override (%d)", GetSlotIndex(slot));})
//Add this emitter to the slot and switch off the radios of the existing emitters.
if(slot->OverrideEmitters(emitter, const_cast<audRadioStation*>(station), requestedPCMChannel))
{
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Successfully overrode emitters in slot %d", GetSlotIndex(slot));})
return true;
}
}
/*else if(priority == audStreamSlot::STREAM_PRIORITY_ENTITY_EMITTER_NEAR)
{
//We're not already playing the requested station, and we don't have any free slots for this ambient vehicle emitter.
//Try and allocate an emitter on a slot playing another station.
// - Starting on a random slot, so we don't fill them in order.
u32 startOffset = static_cast<u32>(audEngineUtil::GetRandomNumberInRange(0, g_MaxRadioSlots - 1));
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
slot = GetSlot((slotIndex + startOffset) % g_MaxRadioSlots);
//Only add new emitters to radio slots that are playing (or about to play.)
u32 state = slot->GetState();
if((state == RADIO_SLOT_STARTING) || (state == RADIO_SLOT_PLAYING))
{
if(slot->AddEmitter(emitter, isPlayerRadio))
{
return slot->GetStationIndex();
}
}
}
}*/
}
BANK_ONLY(if (isClubEmitter) {audDisplayf("[CLUB EMITTERS] Radio station request failed!");})
//We couldn't add this emitter.
return false;
}
bool audRadioSlot::IsStationActive(const audRadioStation *station)
{
return (FindSlotByStation(station) != NULL);
}
void audRadioSlot::FindAndStopEmitter(audRadioEmitter *emitter, bool clearStation)
{
/*
//Find this emitter in our radio slots and NULL it.
audRadioSlot *slot = FindSlotByEmitter(emitter);
if(slot)
{
slot->RemoveEmitter(emitter);
}
*/
u32 count = 0;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
audRadioSlot *slot = sm_RadioSlots[slotIndex];
if(slot && (slot->HasEmitter(emitter)))
{
slot->RemoveEmitter(emitter, clearStation);
count++;
}
}
naAssertf(count <= 1, "Emitter registered with %u slots", count);
}
void audRadioSlot::SwapRadioEmitter(audRadioEmitter *oldEmitter, audRadioEmitter *newEmitter)
{
u32 count = 0;
for(u32 slotIndex=0; slotIndex<g_MaxRadioSlots; slotIndex++)
{
audRadioSlot *slot = sm_RadioSlots[slotIndex];
if(slot && (slot->HasEmitter(oldEmitter)))
{
slot->RemoveEmitter(oldEmitter, false);
slot->AddEmitter(newEmitter);
count++;
}
}
naAssertf(count <= 1, "Emitter registered with %u slots", count);
}
bool audRadioSlot::IsEmitterRetuning(audRadioEmitter *emitter)
{
audRadioSlot *slot = FindSlotByEmitter(emitter);
if(slot)
{
return slot->IsRetuning();
}
return false;
}
void audRadioSlot::FindAndRetuneEmitter(audRadioEmitter *emitter)
{
audRadioSlot *slot = FindSlotByEmitter(emitter);
if(slot)
{
slot->RetuneEmitter(emitter);
}
else
{
//The radio is currently off for this emitter, so we need to add it to one of the radio slots.
//Add the emitter to the slot with the least existing emitters and switch off the radios of those emitters.
slot = FindSlotWithLeastEmitters();
if(slot)
{
//Make sure we have a stream slot.
if(!slot->HasStreamSlot(0) && !slot->HasStreamSlot(1))
{
slot->AllocateStreamSlot(0, audStreamSlot::STREAM_PRIORITY_PLAYER_RADIO);
}
if(slot->HasStreamSlot(0) || slot->HasStreamSlot(1))
{
slot->OverrideEmitters(emitter);
}
}
}
}
#if !__FINAL
void audRadioSlot::FindAndSkipForwardEmitter(audRadioEmitter *emitter, u32 timeToSkipMs)
{
audRadioSlot *slot = FindSlotByEmitter(emitter);
if(slot)
{
slot->SkipForward(timeToSkipMs);
}
}
void audRadioSlot::FindAndSkipForwardEmitterToTransition(audRadioEmitter *emitter, u32 timeToSkipMs)
{
audRadioSlot *slot = FindSlotByEmitter(emitter);
if (slot)
{
slot->SkipForwardToTransition(timeToSkipMs);
}
}
#endif // !__FINAL
bool audRadioSlot::OnStopCallback(u32 userData)
{
audRadioSlot *slot = audRadioSlot::GetSlot(userData);
if(slot)
{
//We are losing our stream slot, so stop the radio slot.
slot->Stop();
}
return true;
}
bool audRadioSlot::HasStoppedCallback(u32 userData)
{
bool hasStopped = true;
audRadioSlot *slot = audRadioSlot::GetSlot(userData);
if(slot)
{
//Check if we have stopped streaming from this radio slot yet
// NOTE: check for both starting states, since they block until their stream slot is active - this generates a dead lock
hasStopped = (slot->GetState() == RADIO_SLOT_IDLE || slot->GetState() == RADIO_SLOT_RETUNING_STARTING_NEW || slot->GetState() == RADIO_SLOT_STARTING);
}
return hasStopped;
}
audRadioSlot::audRadioSlot(u32 slotIndex)
{
Assign(m_SlotIndex, slotIndex);
m_StreamSlots[0] = NULL;
m_StreamSlots[1] = NULL;
m_State = RADIO_SLOT_IDLE;
m_Priority = audStreamSlot::STREAM_PRIORITY_IDLE;
m_IdleStartTimeMs = 0;
m_TimeToSkipForwardMs = 0;
m_Station = NULL;
m_RetuningEmitterIndex = g_NullRadioSlotEmitter;
m_RetuningStation = NULL;
m_ShouldSkipForward = false;
m_WasUsingPositionedPlayerVehicleRadioReverb = false;
m_CurrentStationIndex = 0;
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
ClearEmitter(emitterIndex);
}
}
audRadioSlot::~audRadioSlot()
{
}
bool audRadioSlot::IsFree(bool UNUSED_PARAM(isRequestingPlayerRadio))
{
//if(isRequestingPlayerRadio)
{
//Don't bother checking free time for player requests.
return (m_State == RADIO_SLOT_IDLE);
}
/*else
{
return ((m_State == RADIO_SLOT_IDLE) && (g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(2) >
(m_IdleStartTimeMs + g_MinRadioSlotIdleTimeMs)));
}*/
}
bool audRadioSlot::IsRetuning(void)
{
return ((m_State == RADIO_SLOT_RETUNING) || (m_State == RADIO_SLOT_RETUNING_STOPPING_OLD) || (m_State == RADIO_SLOT_RETUNING_STARTING_NEW));
}
bool audRadioSlot::AllocateStreamSlot(u32 index, u32 priority)
{
audStreamClientSettings settings;
// If we haven't specified a priority, use the old one, otherwise update the recorded priority
if(priority == audStreamSlot::NUM_STREAM_PRIORITIES)
{
priority = m_Priority;
}
else
{
m_Priority = priority;
}
Assign(settings.priority, priority);
settings.stopCallback = &OnStopCallback;
settings.hasStoppedCallback = &HasStoppedCallback;
settings.userData = m_SlotIndex;
if(!m_StreamSlots[index])
{
m_StreamSlots[index] = audStreamSlot::AllocateSlot(&settings);
}
return (m_StreamSlots[index] != NULL);
}
void audRadioSlot::Update(u32 timeInMs)
{
bool hasEmitters = false;
CVehicle *playerVehicle = NULL;
const audRadioStation *playerRadioStation = g_RadioAudioEntity.GetPlayerRadioStation();
CalculatePriority();
switch(m_State)
{
case RADIO_SLOT_STARTING:
//Check if our radio station is streaming physically yet.
if(m_Station)
{
//Start this station muted to prevent audibility prior to setting up correct emitter volumes.
m_Station->MuteEmitters();
naAssertf(m_StreamSlots[0] || m_StreamSlots[1], "Invalid stream slot in RADIO_SLOT_STARTING state during Update");
// If we've just got the one slot, then duplicate it so that we use it regardless of which track is playing
if(!m_StreamSlots[0] && m_StreamSlots[1])
{
m_StreamSlots[0] = m_StreamSlots[1];
}
else if(!m_StreamSlots[1] && m_StreamSlots[0])
{
m_StreamSlots[1] = m_StreamSlots[0];
}
if(m_Station->IsStreamingPhysically())
{
m_State = RADIO_SLOT_PLAYING;
}
else if((m_StreamSlots[0] && (m_StreamSlots[0]->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE)) &&
(m_StreamSlots[1] && (m_StreamSlots[1]->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE)))
{
m_Station->SetPhysicalStreamingState(true, m_StreamSlots.GetElements(), (u8)sm_SoundBucketId);
}
}
break;
case RADIO_SLOT_PLAYING:
if(m_Station)
{
if(m_Station->IsStreamingPhysically())
{
if(m_Station->IsRequestingOverlappedTrackPrepare())
{
#if __BANK
// Simulate not being able to allocate additional stream slots. The playback should starve, stop, then restart, just
// without the crossfade between the two track
if(!g_DebugSimulateStreamSlotStarvation)
#endif
{
// We need two unique stream slots if we're doing a crossfade
if(m_StreamSlots[0] == m_StreamSlots[1] && !m_Station->GetNextTrack().IsPlaying())
{
m_StreamSlots[m_Station->GetNextTrackIndex()] = NULL;
}
AllocateStreamSlot(0);
AllocateStreamSlot(1);
}
}
else
{
// We don't currently need the slot for the next track, so get rid. Slot manager default HasStopped function
// will verify that reference counts etc. are at zero
if(m_Station->GetNextTrack().GetWaveSlot() == NULL)
{
u32 currentTrackIndex = m_Station->GetActiveTrackIndex();
u32 nextTrackIndex = m_Station->GetNextTrackIndex();
if(m_StreamSlots[nextTrackIndex])
{
if(m_StreamSlots[nextTrackIndex] != m_StreamSlots[currentTrackIndex])
{
m_StreamSlots[nextTrackIndex]->ClearActiveSettings();
m_StreamSlots[nextTrackIndex]->StopAndFree();
m_StreamSlots[nextTrackIndex] = NULL;
}
else
{
m_StreamSlots[nextTrackIndex] = NULL;
}
}
}
}
m_Station->SetStreamSlots(m_StreamSlots.GetElements());
}
else
{
// catch the strange case where we're marked as playing but the station is still virtual - we need to jump back to starting
// to kick off the phys streaming
m_State = RADIO_SLOT_STARTING;
break;
}
}
/*if(m_Station && m_Station->IsStreamingPhysically() && m_Station->IsLocked())
{
// if this is no longer a valid station then stop playing it physically
m_Station->SetPhysicalStreamingState(false);
m_State = RADIO_SLOT_STOPPING;
break;
}*/
//Check if we have any emitters and if one of them is the player's vehicle.
playerVehicle = g_RadioAudioEntity.GetLastPlayerVehicle();
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex] != NULL)
{
hasEmitters = true;
//Make sure each emitter is assigned the active station (handling the player vehicle as a special-case.)
m_Emitters[emitterIndex]->SetRadioStation(m_Station);
if(m_Emitters[emitterIndex]->Equals(playerVehicle))
{
playerRadioStation = m_Station;
}
}
}
//If the player is fully in a vehicle and this is not the player's station we have to stop this slot so we
//don't take up too much streaming bandwidth.
if(hasEmitters &&
(m_Station == playerRadioStation || playerRadioStation == NULL || !g_RadioAudioEntity.StopEmittersForPlayerVehicle()))
{
#if !__WIN32PC
naAssertf(m_StreamSlots[0] || m_StreamSlots[1], "No valid stream slots in radioslot update");
#endif
UpdateEmitters();
//TODO: Check if this station can realistically be heard before updating its history.
if(m_Station)
{
m_Station->AddActiveTrackToHistory();
}
}
else
{
//This slot is now free, so stop it.
Stop();
}
break;
case RADIO_SLOT_STOPPING:
if(m_Station == NULL)
{
//The radio is currently switched off, so there's no station to stop physically streaming.
m_IdleStartTimeMs = timeInMs;
m_State = RADIO_SLOT_IDLE;
FreeStreamSlot();
}
else if(m_Station)
{
//Check if our radio station is streaming virtually yet.
if(m_Station->IsStreamingVirtually())
{
m_Station = NULL;
m_IdleStartTimeMs = timeInMs;
m_State = RADIO_SLOT_IDLE;
FreeStreamSlot();
}
else
{
m_Station->SetPhysicalStreamingState(false);
}
}
else
{
naErrorf("In Update; case RADIO_SLOT_STOPPING, Radio station is not off but doesn't have a Valid audRadioStation");
}
break;
case RADIO_SLOT_RETUNING:
if(!ActionRadioRetune(timeInMs))
{
//This station is retuning, so mute all radio sounds.
if(m_Station)
{
m_Station->MuteEmitters();
}
}
break;
case RADIO_SLOT_RETUNING_STOPPING_OLD:
if(!m_Station)
{
//The radio is currently switched off, so there's no station to stop physically streaming.
m_Station = const_cast<audRadioStation*>(m_RetuningStation);
if(m_Emitters[m_RetuningEmitterIndex])
{
m_Emitters[m_RetuningEmitterIndex]->SetRadioStation(m_RetuningStation);
}
m_RetuningEmitterIndex = g_NullRadioSlotEmitter;
m_RetuningStation = NULL;
m_State = RADIO_SLOT_RETUNING_STARTING_NEW;
}
else if(m_Station)
{
//Check if our radio station is streaming virtually yet.
if(m_Station->IsStreamingVirtually())
{
m_Station = const_cast<audRadioStation*>(m_RetuningStation);
if(m_Emitters[m_RetuningEmitterIndex])
{
m_Emitters[m_RetuningEmitterIndex]->SetRadioStation(m_RetuningStation);
}
m_RetuningEmitterIndex = g_NullRadioSlotEmitter;
m_RetuningStation = NULL;
if(m_ShouldSkipForward)
{
//Action the skip forward request by advancing the virtual play time of the new radio
//station's active track.
if(m_Station)
{
m_Station->SkipForward(m_TimeToSkipForwardMs);
}
m_ShouldSkipForward = false;
}
m_State = RADIO_SLOT_RETUNING_STARTING_NEW;
}
else
{
naAssertf(m_StreamSlots[0] || m_StreamSlots[1], "In Update; case RADIO_SLOT_RETUNING_STOPPING_OLD, station is not streaming virtually but has no valid stream slot");
m_Station->SetPhysicalStreamingState(false);
}
}
break;
case RADIO_SLOT_RETUNING_STARTING_NEW:
//Check if our radio station is streaming physically yet.
if(!m_StreamSlots[0] && !m_StreamSlots[1])
{
naWarningf("RADIO_SLOT_RETUNING_STARTING_NEW with NULL m_StreamSlot: %p, %u", this, m_SlotIndex);
m_State = RADIO_SLOT_STOPPING;
}
else if(m_Station)
{
if(m_Station->IsStreamingPhysically())
{
m_State = RADIO_SLOT_PLAYING;
}
else
{
// If we've just got the one slot, then duplicate it so that we use it regardless of which track is playing
if(!m_StreamSlots[0] && m_StreamSlots[1])
{
m_StreamSlots[0] = m_StreamSlots[1];
}
else if(!m_StreamSlots[1] && m_StreamSlots[0])
{
m_StreamSlots[1] = m_StreamSlots[0];
}
if(HasStreamSlots())
{
if(m_StreamSlots[0]->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE &&
m_StreamSlots[1]->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE)
{
m_Station->SetPhysicalStreamingState(true, m_StreamSlots.GetElements(), (u8)sm_SoundBucketId);
}
}
}
}
else
{
m_State = RADIO_SLOT_PLAYING;
}
break;
case RADIO_SLOT_IDLE:
default:
break;
}
}
void audRadioSlot::StartRadioPlayback(const audRadioStation *station)
{
m_Station = const_cast<audRadioStation*>(station);
m_State = RADIO_SLOT_STARTING;
m_CurrentStationIndex = station->GetStationIndex();
}
bool audRadioSlot::ActionRadioRetune(u32 timeInMs)
{
//Ensure that we don't retune too quickly.
if(!g_RadioAudioEntity.IsTimeToRetune(timeInMs))
{
return false; //Still retuning.
}
const audRadioStation *newStation = m_RetuningStation ? m_RetuningStation : g_RadioAudioEntity.GetPlayerRadioStationPendingRetune();
//Check if we're already playing this station in a slot.
audRadioSlot *otherRadioSlot = audRadioSlot::FindSlotByStation(newStation);
if(otherRadioSlot)
{
if(otherRadioSlot != this)
{
//We're already playing this station in another radio slot, so move the vehicle over.
audRadioEmitter *emitter = m_Emitters[m_RetuningEmitterIndex];
if(emitter)
{
otherRadioSlot->AddEmitter(emitter, true);
emitter->SetRadioStation(newStation);
//Remove the original emitter entry.
m_Emitters[m_RetuningEmitterIndex] = NULL;
}
}
//else if(otherRadioSlot == this)
// - We're already playing this station in the current host slot, so the retune is complete.
g_RadioAudioEntity.ClearPendingPlayerRadioStationRetunes();
//Return to playing state.
m_State = RADIO_SLOT_PLAYING;
m_RetuningEmitterIndex = g_NullRadioSlotEmitter;
return true; //Done retuning.
}
//We're not already playing this station, so...
//Retune this slot to the requested station, switching off all other emitters using this slot, to avoid retuning
//them too.
m_RetuningStation = newStation;
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if((emitterIndex != m_RetuningEmitterIndex) && m_Emitters[emitterIndex])
{
//Switch the radio off for this other emitter.
m_Emitters[emitterIndex]->SetRadioStation(NULL);
ClearEmitter(emitterIndex);
}
}
g_RadioAudioEntity.ClearPendingPlayerRadioStationRetunes();
m_State = RADIO_SLOT_RETUNING_STOPPING_OLD;
return false; //Still retuning.
}
bool audRadioSlot::AddEmitter(audRadioEmitter *emitter, bool shouldReplaceExisting, s32 requestedPCMChannel, const audRadioStation* station)
{
if(!naVerifyf(emitter != NULL, "NULL radio emitter being added to slot"))
{
return false;
}
//Check if we already have this emitter.
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex] && m_Emitters[emitterIndex]->Equals(emitter))
{
return true;
}
}
//Check if we have a free emitter.
u32 largestDistanceEmitterIndex = 0;
f32 largestEmitterDistance = 0.0f;
f32 distance;
Vector3 distanceVector;
bool foundValidReplacementCandidate = false;
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
const u32 numStationChannels = station ? station->GetNumChannels() : 2;
if (requestedPCMChannel >= 0 && (emitterIndex % numStationChannels) != (u32)requestedPCMChannel)
{
continue;
}
if(m_Emitters[emitterIndex] == NULL)
{
//Use this free emitter.
m_Emitters[emitterIndex] = emitter;
return true;
}
else if(shouldReplaceExisting && m_Emitters[emitterIndex]->IsReplaceable())
{
//Check if this emitter is the farthest from the listener.
Vector3 position;
m_Emitters[emitterIndex]->GetPosition(position);
distanceVector = VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - position;
distance = distanceVector.Mag();
if(distance > largestEmitterDistance)
{
largestEmitterDistance = distance;
largestDistanceEmitterIndex = emitterIndex;
foundValidReplacementCandidate = true;
}
}
}
if(shouldReplaceExisting && foundValidReplacementCandidate)
{
//Replace the most distant emitter.
m_Emitters[largestDistanceEmitterIndex]->SetRadioStation(NULL);
m_Emitters[largestDistanceEmitterIndex] = emitter;
return true;
}
return false;
}
void audRadioSlot::RemoveEmitter(audRadioEmitter *emitter, bool clearRadioStation)
{
if(naVerifyf(emitter, "Null audRadioEmitter passed into RemoveEmitter"))
{
if(clearRadioStation)
{
emitter->SetRadioStation(NULL);
}
u32 count = 0;
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex] == emitter)
{
ClearEmitter(emitterIndex);
count++;
}
}
naAssertf(count <= 1, "emitter registered multiple times on one slot (%u)", count);
if(count > 0)
{
CalculatePriority();
}
}
}
void audRadioSlot::ClearEmitter(s32 emitterIndex)
{
m_Emitters[emitterIndex] = NULL;
}
void audRadioSlot::CalculatePriority()
{
const audRadioStation *playerRadioStation = g_RadioAudioEntity.IsPlayerRadioActive() ? g_RadioAudioEntity.GetPlayerRadioStation() : NULL;
for(u32 loop = 0; loop < 2; loop++)
{
if(m_StreamSlots[loop])
{
//Update the priority of our stream slot (if we have one), based on the highest priority emitter we have
u32 priority = audStreamSlot::STREAM_PRIORITY_IDLE;
if(m_Station == playerRadioStation)
{
priority = audStreamSlot::STREAM_PRIORITY_PLAYER_RADIO;
}
for(u32 i = 0; i < g_NumRadioSlotEmitters; i++)
{
if(GetEmitter(i) && GetEmitter(i)->GetPriority() > priority)
{
priority = GetEmitter(i)->GetPriority();
}
}
m_Priority = priority;
m_StreamSlots[loop]->SetPriority(priority);
}
}
}
void audRadioSlot::RetuneEmitter(audRadioEmitter *emitter)
{
//Only request the retune if this slot is in a safe state.
if((m_State == RADIO_SLOT_IDLE) || (m_State == RADIO_SLOT_PLAYING) ||
(m_State == RADIO_SLOT_RETUNING) || (m_State == RADIO_SLOT_STARTING && m_Station && m_Station->IsFrozen()))
{
//Enter the retuning state.
m_State = RADIO_SLOT_RETUNING;
m_RetuningEmitterIndex = FindEmitterIndex(emitter);
}
//else - This slot is not currently in a safe state, so try again next update.
}
void audRadioSlot::SkipForward(u32 timeToSkipMs)
{
//Only request the skip if this slot is playing a real station.
if((m_State == RADIO_SLOT_PLAYING) && (m_Station != NULL))
{
//Enter the retuning state.
m_RetuningEmitterIndex = 0; //This does not matter as we are not changing station.
m_ShouldSkipForward = true;
m_TimeToSkipForwardMs = timeToSkipMs;
m_RetuningStation = m_Station;
m_State = RADIO_SLOT_RETUNING_STOPPING_OLD;
}
//else - This slot is not currently in a safe state, so try again next update.
}
void audRadioSlot::SkipForwardToTransition(u32 timeToSkipMs)
{
//Only request the skip if this slot is playing a real station.
if ((m_State == RADIO_SLOT_PLAYING) && (m_Station != NULL))
{
s32 playTime = m_Station->GetCurrentTrack().GetPlayTime();
s32 duration = m_Station->GetCurrentTrack().GetDuration();
s32 timeRemaining = duration - playTime;
timeRemaining -= timeToSkipMs;
if (timeRemaining > 0)
{
//Enter the retuning state.
m_RetuningEmitterIndex = 0; //This does not matter as we are not changing station.
m_ShouldSkipForward = true;
m_TimeToSkipForwardMs = timeRemaining;
m_RetuningStation = m_Station;
m_State = RADIO_SLOT_RETUNING_STOPPING_OLD;
}
}
//else - This slot is not currently in a safe state, so try again next update.
}
bool audRadioSlot::OverrideEmitters(audRadioEmitter *emitter, audRadioStation *newStation, s32 requestedPCMChannel)
{
naCErrorf(emitter, "Null audradioEmitter passed into OverrideEmitters");
naAssertf(m_StreamSlots[0] || m_StreamSlots[1], "Null stream slot in OverrideEmitters");
//Only override the existing emitters if this slot is in a safe state.
if((m_State == RADIO_SLOT_IDLE) || (m_State == RADIO_SLOT_PLAYING) ||
(m_State == RADIO_SLOT_RETUNING))
{
if(newStation)
{
newStation->MuteEmitters();
}
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex])
{
#if __BANK
if (m_Emitters[emitterIndex]->IsClubEmitter())
{
audDisplayf("[CLUB EMITTERS] Club emitter in slot %d is being overriden for station %s", GetSlotIndex(this), newStation ? newStation->GetName() : "NULL");
}
#endif
m_Emitters[emitterIndex]->SetRadioStation(NULL);
ClearEmitter(emitterIndex);
}
}
u32 emitterIndex = requestedPCMChannel == -1 ? 0 : requestedPCMChannel;
m_Emitters[emitterIndex] = emitter;
m_RetuningEmitterIndex = emitterIndex;
if(emitter)
{
emitter->SetRadioStation(NULL);
}
if(m_Station)
{
m_Station->SetPhysicalStreamingState(false);
m_Station = NULL;
}
m_RetuningStation = newStation;
//Enter the retuning state.
m_State = RADIO_SLOT_RETUNING;
return true;
}
return false;
}
void audRadioSlot::UpdateEmitters(void)
{
if(m_Station)
{
u32 emitterIndex;
f32 emitterVolumeFactor;
f32 playerVehicleInsideFactor = g_RadioAudioEntity.GetPlayerVehicleInsideFactor();
const audRadioStation *playerRadioStation = g_RadioAudioEntity.GetPlayerRadioStation();
f32 stereoCutoff = kVoiceFilterLPFMaxCutoff;
if(playerRadioStation == m_Station)
{
//This is the Player's station, so ramp the volume of the stereo emitter sound.
emitterVolumeFactor = playerVehicleInsideFactor;
}
else
{
//This is not the Player's station, so mute the stereo emitter sound, unless
//we find that one of our positioned emitters is the Player's last vehicle.
emitterVolumeFactor = 0.0f;
if(!g_RadioAudioEntity.IsMobilePhoneRadioActive())
{
CVehicle *lastPlayerVehicle = g_RadioAudioEntity.GetLastPlayerVehicle();
if(lastPlayerVehicle != NULL)
{
for(emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex] && m_Emitters[emitterIndex]->Equals(lastPlayerVehicle))
{
//This is the Player's last vehicle, so ramp the volume of the
//stereo emitter sound.
emitterVolumeFactor = playerVehicleInsideFactor;
break;
}
}
}
}
}
if(g_RadioAudioEntity.GetLastPlayerVehicle())
{
// Boats (specifically, submersibles) can drown, but this shouldn't affect the radio
f32 drowningFactor = (g_RadioAudioEntity.GetLastPlayerVehicle()->InheritsFromSubmarineCar() || g_RadioAudioEntity.GetLastPlayerVehicle()->GetVehicleAudioEntity()->GetAudioVehicleType() == AUD_VEHICLE_BOAT)? 0.0f : g_RadioAudioEntity.GetLastPlayerVehicle()->GetVehicleAudioEntity()->GetDrowningFactor();
stereoCutoff -= (kVoiceFilterLPFMaxCutoff - 500.0f) * playerVehicleInsideFactor * drowningFactor;
}
m_Station->UpdateStereoEmitter(emitterVolumeFactor, stereoCutoff);
bool isPlayerRadioActive = (playerRadioStation != NULL);
bool shouldPlayFullRadio = true;
f32 emittedVolume;
Vector3 emitterPosition;
naEnvironmentGroup *occlusionGroup;
bool muteDry = false;
bool usePositionedPlayerVehicleRadioReverb = false;
u32 LPFCutoff = kVoiceFilterLPFMaxCutoff;
u32 HPFCutoff = 0;
f32 environmentalLoudness = 0.f;
for(emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
audRadioEmitter::RadioEmitterType emitterType = audRadioEmitter::RADIO_EMITTER_TYPE_MAX;
if(m_Emitters[emitterIndex] != NULL)
{
//This is an active emitter, so ramp the volume of the associated positioned emitter sound.
if(isPlayerRadioActive)
{
//The Player's radio is active, so we forcibly ramp down the volume of the positioned emitter sounds.
if(m_Emitters[emitterIndex]->IsPlayerVehicle())
{
// crank it nice and loud, based on how open this car is
if(playerVehicleInsideFactor>0.97f)
{
// emitterVolumeFactor = playerVehicleInsideFactor * g_PlayerPositionedRadioLinVol * (1.f - audNorthAudioEngine::GetGtaEnvironment()->GetOutsideWorldOcclusion());
emitterVolumeFactor = playerVehicleInsideFactor * g_PlayerPositionedRadioLinVol * (1.f - audNorthAudioEngine::GetOcclusionManager()->GetOutsideWorldOcclusionIgnoringInteriors());
CVehicle* playerVehicle = CGameWorld::FindLocalPlayerVehicle();
if (playerVehicle && playerVehicle->GetVehicleType() == VEHICLE_TYPE_CAR)
{
emitterVolumeFactor = playerVehicle->GetVehicleAudioEntity()->GetOpenness(false) * g_PlayerPositionedRadioSoftTopLinVol;
}
if(!g_PositionedPlayerVehicleRadioEnabled && DYNAMICMIXER.GetActiveSceneFromHash(ATSTRINGHASH("INTERIOR_VIEW_AIRCRAFT_PASSENGER_SCENE", 0x7EBA8171)) == NULL)
{
muteDry = true;
}
else
{
emitterVolumeFactor = 1.0f;
usePositionedPlayerVehicleRadioReverb = true;
}
// reduce it for talk stations
if (playerRadioStation && playerRadioStation->IsTalkStation())
{
emitterVolumeFactor *= 0.6f;
}
// and duck for scripted conversations
emitterVolumeFactor *= audNorthAudioEngine::GetFrontendRadioVolumeLin();
// clamp to -60dB to avoid dbz in the shadow sound stuff
emitterVolumeFactor = Max(emitterVolumeFactor, 0.001f);
}
else
{
emitterVolumeFactor = 1.0f - playerVehicleInsideFactor;
// work around for shadow sound issue: we want the player car to stay above silence to ensure that we dont end up with
// no shadow sounds with >0 contributions when computing filter cutoffs
// -60dB should be fine...
emitterVolumeFactor = Max(emitterVolumeFactor, 0.001f);
}
}
else
{
if(m_Emitters[emitterIndex]->ShouldMuteForFrontendRadio())
{
emitterVolumeFactor = 1.0f - playerVehicleInsideFactor;
}
else
{
emitterVolumeFactor = 1.f;
}
}
}
else if(g_RadioAudioEntity.IsPlayerInVehicle())
{
//The Player's radio is inactive, so we forcibly ramp up the volume of the positioned emitter sounds
//as we now can allow positioned radio emitters to be audible even when the Player is in a vehicle.
emitterVolumeFactor = playerVehicleInsideFactor;
}
else
{
//The Player's radio is inactive, so we forcibly ramp up the volume of the positioned emitter sounds
//as we now can allow positioned radio emitters to be audible even when the Player is in a vehicle.
emitterVolumeFactor = 1.0f - playerVehicleInsideFactor;
}
//Extract the emitted volume offset for this emitter.
emittedVolume = m_Emitters[emitterIndex]->GetEmittedVolume();
emitterType = m_Emitters[emitterIndex]->GetEmitterType();
m_Emitters[emitterIndex]->GetPosition(emitterPosition);
occlusionGroup = m_Emitters[emitterIndex]->GetOcclusionGroup();
LPFCutoff = m_Emitters[emitterIndex]->GetLPFCutoff();
HPFCutoff = m_Emitters[emitterIndex]->GetHPFCutoff();
environmentalLoudness = m_Emitters[emitterIndex]->GetEnvironmentalLoudness();
if (occlusionGroup)
{
occlusionGroup->PopulateEnvironmentMetric(&m_OcclusionMetrics[emitterIndex]);
}
// need to set this after calling Populate()
m_OcclusionMetrics[emitterIndex].SetJustReverb(muteDry);
if(usePositionedPlayerVehicleRadioReverb)
{
m_OcclusionMetrics[emitterIndex].SetReverbSmall(g_PositionedPlayerVehicleRadioReverbSmall);
m_OcclusionMetrics[emitterIndex].SetReverbMedium(g_PositionedPlayerVehicleRadioReverbMedium);
m_OcclusionMetrics[emitterIndex].SetReverbLarge(g_PositionedPlayerVehicleRadioReverbLarge);
}
// Reset back to normal levels once we leave aircraft passenger mode
else if(m_WasUsingPositionedPlayerVehicleRadioReverb)
{
m_OcclusionMetrics[emitterIndex].SetReverbSmall(0.0f);
m_OcclusionMetrics[emitterIndex].SetReverbMedium(0.0f);
m_OcclusionMetrics[emitterIndex].SetReverbLarge(0.0f);
}
if(!m_Emitters[emitterIndex]->ShouldPlayFullRadio())
{
shouldPlayFullRadio = false;
}
m_OcclusionMetrics[emitterIndex].SetRolloffFactor(m_Emitters[emitterIndex]->GetRolloffFactor());
m_WasUsingPositionedPlayerVehicleRadioReverb = usePositionedPlayerVehicleRadioReverb;
}
else
{
emitterVolumeFactor = 0.0f;
emitterPosition.Zero();
emittedVolume = 0.0f;
environmentalLoudness = 0.0f;
m_OcclusionMetrics[emitterIndex].Reset();
}
m_Station->UpdatePositionedEmitter(emitterIndex, emittedVolume, emitterVolumeFactor, emitterPosition, LPFCutoff,
HPFCutoff, &m_OcclusionMetrics[emitterIndex], emitterType, environmentalLoudness, m_Emitters[emitterIndex]? m_Emitters[emitterIndex]->IsLastPlayerVehicle() : false);
if (m_Station->HasTrackChanged() && m_Emitters[emitterIndex])
{
m_Emitters[emitterIndex]->NotifyTrackChanged();
}
} //for(u8 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
// Player radio should always play full content
m_Station->SetShouldPlayFullRadio(shouldPlayFullRadio || (playerRadioStation == m_Station));
}
}
s32 audRadioSlot::FindEmitterIndex(audRadioEmitter *emitter)
{
for(s32 emitterIndex=0; emitterIndex<(s32)g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex] == emitter)
{
return emitterIndex;
}
}
return -1;
}
bool audRadioSlot::HasEmitter(audRadioEmitter *emitter)
{
return (FindEmitterIndex(emitter) >= 0);
}
void audRadioSlot::Stop(void)
{
if(m_Station)
{
m_Station->MuteEmitters();
}
for(u32 emitterIndex=0; emitterIndex<g_NumRadioSlotEmitters; emitterIndex++)
{
if(m_Emitters[emitterIndex])
{
m_Emitters[emitterIndex]->SetRadioStation(NULL);
}
ClearEmitter(emitterIndex);
}
FreeStreamSlot();
m_RetuningEmitterIndex = g_NullRadioSlotEmitter;
m_RetuningStation = NULL;
//Enter the stopping state.
m_State = RADIO_SLOT_STOPPING;
}
void audRadioSlot::InvalidateStation()
{
Stop();
m_Station = NULL;
}
void audRadioSlot::FreeStreamSlot()
{
for(u32 loop = 0; loop < 2; loop++)
{
if(m_StreamSlots[loop])
{
if(m_StreamSlots[loop]->GetState() == audStreamSlot::STREAM_SLOT_STATUS_ACTIVE)
{
m_StreamSlots[loop]->Free();
}
else
{
m_StreamSlots[loop]->ClearRequestedSettings();
}
m_StreamSlots[loop] = NULL;
}
}
}
#if RSG_BANK
void audRadioSlot::DrawDebug(audDebugDrawManager &drawMgr)
{
for(u32 i = 0; i < g_MaxRadioSlots; i++)
{
sm_RadioSlots[i]->DrawSlotDebug(drawMgr);
}
}
void audRadioSlot::DrawSlotDebug(audDebugDrawManager &drawMgr)
{
const char *prio[audStreamSlot::NUM_STREAM_PRIORITIES] =
{
"PrioIdle",
"PrioEntityEmitter_Far",
"PrioEntityEmitter_Near",
"PrioStaticEmitter",
"PrioEntityEmitter_Network",
"ScriptedVehicleRadio",
"PrioFrontendMenu",
"PrioScript",
"PrioScriptEmitter",
"PrioMusic",
"PrioPlayerRadio",
"PrioCutscene",
};
const char *states[] =
{
"Idle",
"Starting",
"Playing",
"Stopping",
"Retuning",
"Retuning_StoppingOld",
"Retuning_StartingNew"
};
s32 streamSlotIndex1 = -1;
s32 streamSlotIndex2 = -1;
for(u32 i = 0; i < g_NumStreamSlots; i++)
{
if(audStreamSlot::GetSlot(i) == m_StreamSlots[0])
{
streamSlotIndex1 = (s32)i;
}
if(audStreamSlot::GetSlot(i) == m_StreamSlots[1])
{
streamSlotIndex2 = (s32)i;
}
}
const char *stationName = (m_Station == NULL?"None":m_Station->GetName());
const char *retuningStationName = (m_RetuningStation == NULL?"None":m_RetuningStation->GetName());
drawMgr.DrawLinef("RadioSlot %u state: %s, station: %s, retuningEmitter: %u, retuningStation: %s [stream slots %d/%d]", m_SlotIndex, states[m_State], stationName, m_RetuningEmitterIndex, retuningStationName, streamSlotIndex1, streamSlotIndex2);
for(u32 i = 0; i < g_NumRadioSlotEmitters; i++)
{
if(m_Emitters[i])
{
Vector3 pos;
m_Emitters[i]->GetPosition(pos);
const char *prioString = prio[m_Emitters[i]->GetPriority()];
Vector3 emitterPos;
m_Emitters[i]->GetPosition(emitterPos);
f32 dist = (VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - emitterPos).Mag();
drawMgr.DrawLinef("%u: %s %.2fm (%.2f,%.2f,%.2f) [%s]", i, m_Emitters[i]->GetName(), dist, pos.x, pos.y, pos.z, prioString);
}
}
}
#endif // RSG_BANK
f32 audRadioSlot::ComputeDistanceSqToNearestEmitter() const
{
f32 nearestDist2 = LARGE_FLOAT;
for(u32 i = 0 ; i < g_NumRadioSlotEmitters; i++)
{
if(m_Emitters[i])
{
// Special case active 'fill space' emitters
if(m_Emitters[i]->GetOcclusionGroup() && m_Emitters[i]->GetOcclusionGroup()->IsCurrentlyFillingListenerSpace())
{
return 0.f;
}
Vector3 emitterPos;
m_Emitters[i]->GetPosition(emitterPos);
f32 dist2 = (VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - emitterPos).Mag2();
if(dist2 < nearestDist2)
{
nearestDist2 = dist2;
}
}
}
return nearestDist2;
}
f32 audRadioSlot::ComputeDistanceSqToNearestEmitter(CInteriorInst* pIntInst, s32 roomIdx) const
{
f32 nearestDist2 = LARGE_FLOAT;
for (u32 i = 0; i < g_NumRadioSlotEmitters; i++)
{
if (m_Emitters[i])
{
naEnvironmentGroup* environmentGroup = m_Emitters[i]->GetOcclusionGroup();
if (environmentGroup && environmentGroup->GetInteriorInst() == pIntInst && environmentGroup->GetRoomIdx() == roomIdx)
{
// Special case active 'fill space' emitters
if (m_Emitters[i]->GetOcclusionGroup() && m_Emitters[i]->GetOcclusionGroup()->IsCurrentlyFillingListenerSpace())
{
return 0.f;
}
Vector3 emitterPos;
m_Emitters[i]->GetPosition(emitterPos);
f32 dist2 = (VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition()) - emitterPos).Mag2();
if (dist2 < nearestDist2)
{
nearestDist2 = dist2;
}
}
}
}
return nearestDist2;
}
#endif // NA_RADIO_ENABLED