//
// audio/northaudioengine.cpp
//
// Copyright (C) 1999-2015 Rockstar Games.  All Rights Reserved.
//


#include "northaudioengine.h"

//#include "audiogeometry.h"
#include "pedaudioentity.h"
#include "pedscenariomanager.h"
#include "glassaudioentity.h"
#include "ambience/ambientaudioentity.h"
#include "ambience/audambientzone.h"
#include "ambience/water/audshorelinePool.h"
#include "ambience/water/audshorelineRiver.h"
#include "ambience/water/audshorelineOcean.h"
#include "ambience/water/audshorelineLake.h"
#include "environment/audwarpmanager.h"
#include "vehiclereflectionsaudioentity.h"
#include "explosionaudioentity.h"
#include "emitteraudioentity.h"
#include "fireaudioentity.h"
#include "frontendaudioentity.h"
#include "collisionaudioentity.h"
#include "cutsceneaudioentity.h"
#include "dynamicmixer.h"
#include "radioaudioentity.h"
#include "scriptaudioentity.h"
#include "speechaudioentity.h"
#include "speechmanager.h"
#include "weaponaudioentity.h"
#include "vehicleaudioentity.h"
#include "heliaudioentity.h"
#include "trainaudioentity.h"
#include "caraudioentity.h"
#include "planeaudioentity.h"
#include "bicycleaudioentity.h"
#include "traileraudioentity.h"
#include "boataudioentity.h"
#include "vehiclefireaudio.h"
#include "frontend/NewHud.h"
#include "vehicleengine/vehicleengine.h"
#include "vehicleengine/electricengine.h"
#include "vehicleengine/turbo.h"
#include "vehicleengine/transmission.h"
#include "weatheraudioentity.h" 
#include "audio/environment/environment.h"
#include "policescanner.h"
#include "music/musicplayer.h"
#include "replayaudioentity.h"

#include "camera/CamInterface.h"
#include "camera/cinematic/CinematicDirector.h"
#include "camera/gameplay/aim/ThirdPersonPedAssistedAimCamera.h"
#include "camera/viewports/ViewportManager.h"
#include "control/gamelogic.h"
#include "control/stuntjump.h"
#include "core/game.h"
#include "cutscene/CutSceneManagerNew.h"
#include "debug/debug.h"
#include "debug/debugscene.h"
#include "grcore/debugdraw.h"
#include "game/clock.h"
#include "vehicles/Metadata/VehicleSeatInfo.h"
#include "input/mouse.h"
#include "scene/DataFileMgr.h"
#include "scene/FileLoader.h"
#include "scene/RefMacros.h"
#include "system/system.h"
#include "audiodata/simpletypes.h"
#include "audioeffecttypes/delayeffect.h"
#include "audioeffecttypes/filterdefs.h"
#include "audioengine/categorymanager.h"
#include "audioengine/categorycontrollermgr.h"
#include "audioengine/curverepository.h"
#include "audioengine/engine.h"
#include "audioengine/engineutil.h"
#include "audioengine/environment.h"
#include "audioengine/soundfactory.h"
#include "audioengine/soundmanager.h"
#include "audiohardware/channel.h"
#include "audiohardware/config.h"
#include "audiohardware/grainplayer.h"
#include "audiohardware/device_360.h"
#include "audioengine/dynamicmixmgr.h"
#include "audiohardware/device.h"
#include "audiohardware/device_ps3.h"
#include "audiohardware/driver.h"
#include "audiohardware/driverutil.h"
#include "audiohardware/device_xaudio_pc.h"
#include "audiosoundtypes/speechsound.h"
#include "audiosoundtypes/environmentsound.h"
#include "audiosynth/synthesizer.h"
#include "frontend/PauseMenu.h"
#include "frontend/ProfileSettings.h"
#include "modelinfo/MloModelInfo.h"
#include "parser/manager.h"
#include "profile/profiler.h"
#include "renderer/PostProcessFX.h"
#include "renderer/water.h"
#include "scene/playerswitch/PlayerSwitchInterface.h"
#include "scene/scene.h"
#include "system/ControlMgr.h"
#include "system/memory.h"
#include "system/param.h"
#include "system/performancetimer.h"
#include "system/simpleallocator.h"
#include "peds/ped.h"
#include "vfx/VisualEffects.h"
#include "net/nethardware.h"
#include "frontend/MobilePhone.h"
#include "system/ThreadPriorities.h"
#include "system/memvisualize.h"
#include "streaming/streamingvisualize.h"
#include "video/VideoPlayback.h"

#include "debugaudio.h"

#include "grprofile/pix.h"

#include "Network/Commerce/CommerceManager.h"
#include "Network/Live/livemanager.h"
#include "Network/Network.h"
#include "Network/Voice/NetworkVoice.h"

#if RSG_ORBIS
#include "audiohardware/device_orbis.h"
#include "rline/rlnp.h"
#endif

#if GTA_REPLAY
#include "replaycoordinator/ReplayCoordinator.h"
#include "camera/replay/ReplayFreeCamera.h"
#include "replayaudioentity.h"
#endif

#if RSG_PC
#include "audiohardware/device_xaudio_pc.h"

#if __BANK && RSG_PC
bool g_ShowAudioFrameDebugInfo;
u32 g_NumberOfMixBuffers = rage::kXAudioNumberOfBuffers;
bool g_OverrideCPULimitedAudioSetting = false;
bool g_ForceMinSpecMode = false;

#endif

#endif

AUDIO_OPTIMISATIONS()

extern bool g_UseInteriorCarFilter;

bool g_HasInitialisedStaticClasses = false;
static bool s_HasScanStarted;

static const char *g_AudioConfigPath = "audio:/config/";
// PURPOSE
//	Specifies the distance relative to camera that RegisterLoundSound() responds to
static const f32 g_audLoudSoundDistanceMag = 40.f;

char * audNorthAudioEngine::sm_ExtraContentPath = NULL;
naAnimHandler audNorthAudioEngine::sm_AudAnimHandler;
#if __BANK

static bool g_ShouldMuteWind = false;
static audCategoryController * g_WindMuteController = NULL;
bool g_ForceDefaultCutsceneSettings = false;
bool g_ForceAllowRadioOverScreenFade = false;
extern bool g_DebugPlayingScriptSounds;
extern bool g_GameTimerCutscenes;
bool g_WarpToStaticEmitter = false;
bool g_WarpToAmbientZone = false;
bool g_WarpToInterior = false;
const StaticEmitter *g_RequestedWarpEmitter = NULL;
const AmbientZone *g_RequestedWarpAmbZone = NULL;
const CInteriorProxy *g_RequestedWarpInterior = NULL;
bool g_IgnoreFrontendVolumeSliders = false;

bool g_DisplaySlowMo = false;

#endif

namespace rage
{
	extern float g_SFXHPFCutoff;
	extern bool g_SFXHPFBypass;
}

#if !__FINAL
bool audNorthAudioEngine::sm_ShouldAddStrVisMarkers = false;
class naStreamingDebugInterface : public audStreamingDebugInterface
{
public:
	void SetMarker(const char *text) const
	{
		if(audNorthAudioEngine::ShouldAddStrVisMarkers())
		{
			STRVIS_SET_MARKER_TEXT(text);
		}
	}
};
#endif

volatile bool audNorthAudioEngine::sm_IsAudioUpdateCurrentlyRunning = false;

audMetadataDataFileMounter *audNorthAudioEngine::sm_SoundDlcMounter;
audMetadataDataFileMounter *audNorthAudioEngine::sm_DynamicMixDlcMounter;
audMetadataDataFileMounter  *audNorthAudioEngine::sm_GameObjectDlcMounter;
audMetadataDataFileMounter  *audNorthAudioEngine::sm_CurveDlcMounter;
audMetadataDataFileMounter  *audNorthAudioEngine::sm_SynthDlcMounter;

#if NA_RADIO_ENABLED
static f32 g_RadioDuckingVolume		= -7.0f;
static f32 g_RadioDuckingVolumeStereo		= -7.0f;
static f32 g_RadioDuckingVolumeVoiceChat		= -18.0f;
static f32 g_TalkRadioDuckingVolume = -12.0f;
static f32 g_RadioNonMusicAdditionalDucking = -3.f;
BANK_ONLY(static f32 g_RadioGPSDuckingVolume	= -3.0f);
static bool sm_ShouldDuckRadio		= false;
#endif

static f32 g_RadioDuckingSmoothRate	= 0.001f;
static f32 g_RadioDuckingSmoothRateUp	= 0.0003f;

bool audNorthAudioEngine::sm_IsPlayerSpecialAbilityFadingOut = false;

audNorthAudioEngine::DataSetState audNorthAudioEngine::sm_DataSetState = audNorthAudioEngine::Loaded_SP;
bool audNorthAudioEngine::sm_IsMPDataRequested = false;
u32 audNorthAudioEngine::sm_DataSetStateCount = 0;
u8 *audNorthAudioEngine::sm_SPSoundDataPtr = NULL;
u32 audNorthAudioEngine::sm_SPSoundDataSize = 0;

//u8 *audNorthAudioEngine::sm_ReplayedSoundDataPtr = NULL;
//u32 audNorthAudioEngine::sm_ReplayedSoundDataSize = 0;


#if GTA_REPLAY
bool audNorthAudioEngine::sm_EnableHPF = true;
bool audNorthAudioEngine::sm_EnableLPF = true;
bool audNorthAudioEngine::sm_UseMonoReverbs = false;
float audNorthAudioEngine::sm_AudioFrameTime = 15.0f;
bool audNorthAudioEngine::sm_VehicleEffectsBypass = false;
bool audNorthAudioEngine::sm_UseSingleListener = false;
#endif

u32 audNorthAudioEngine::sm_StreamingSoundBucketId = ~0U;

bank_float g_SlowMoTrainVol = -9.f;
bank_float g_ConversationTrainVol = -0.f;
bank_float g_ConversationHeliVol = -6.f;

f32 g_FadeValueForSilence = 1.f;
bool g_DebugFadeLevels = false;

bool audNorthAudioEngine::sm_ApplyMusicSlider = false;

audSimpleSmoother audNorthAudioEngine::sm_CutsceneLeakageSmoother;
f32 g_CutsceneLeakageSmootherRate = 1.0f;
f32 g_CutscenePositionedLeakage = 0.75f;
bool g_LeakForRPICutscenes = true;
BANK_ONLY(bool g_DebugCutsceneLeakage = false;)

bool g_MuteGameWorldAudio = false;
bool g_MutePositionedRadio = false;
bool g_MuteGameWorldAndPositionedRadioForTV = false;

bool g_AudioInitialised = false;

f32 g_AttachedToTrainVolume = -9.0f;
f32 g_TrainRolloffInSubways = 2.5f;

bool audNorthAudioEngine::sm_IsInNetworkLobby = false;
bool audNorthAudioEngine::sm_IsRenderingHoodMountedVehicleCam = false;
bool audNorthAudioEngine::sm_IsRenderingFirstPersonVehicleCam = false;
bool audNorthAudioEngine::sm_IsRenderingFirstPersonVehicleTurretCam = false;
REPLAY_ONLY(bool audNorthAudioEngine::sm_IsRenderingReplayFreeCamera = false;)
bool audNorthAudioEngine::sm_OverrideLobbyMute = false;
bool audNorthAudioEngine::sm_StartingNewGame = false;
bool audNorthAudioEngine::sm_CinematicThirdPersonAimCameraActive = false;
bool audNorthAudioEngine::sm_IsFirstPersonActiveForPlayer = false;
bool audNorthAudioEngine::sm_IsCutsceneActive = false;

audMetadataManager audNorthAudioEngine::sm_MetadataMgr;
audGameObjectManager audNorthAudioEngine::sm_GameObjectMgr;

float g_PIVMusicVolumeFadeInS = 1.0f;
float g_PIVMusicVolumeFadeOutS = 2.0f;
float g_PIVOpennessFadeInS = 0.1f;
float g_PIVOpennessFadeOutS = 5.0f;
audSmoother audNorthAudioEngine::sm_PIVMusicVolumeSmoother;
audSmoother audNorthAudioEngine::sm_PIVRadioVolumeSmoother;
audSmoother audNorthAudioEngine::sm_PIVOpennessSmoother;

#if __BANK
char audNorthAudioEngine::sm_SoundName[64];
char g_AuditionWaveSlotName[64];
char g_SlowMoModeName[64] = {0};
bool audNorthAudioEngine::sm_ShouldRemoveSoundHierarchy = true;

bool audNorthAudioEngine::sm_ShouldAuditionThroughFocusEntity = false;
bool audNorthAudioEngine::sm_ShouldAuditionThroughPlayer = false;
bool audNorthAudioEngine::sm_ShouldAuditionPannedFrontend = false;
bool audNorthAudioEngine::sm_ShouldAuditionSoundOverNetwork = false;

float audNorthAudioEngine::sm_DebugDrawYOffset = 0.f;
bool audNorthAudioEngine::sm_AuditionSoundUnpausable = true;
bool audNorthAudioEngine::sm_AllowAuditionSoundToLoad = true;
bool audNorthAudioEngine::sm_AuditionSoundsOnPPU = false;
bool audNorthAudioEngine::sm_ForceBaseCategory = false;
audRemoteControl::AuditionSoundDrawMode audNorthAudioEngine::sm_AuditionSoundDrawMode = audRemoteControl::kVariablesOnly;
char audNorthAudioEngine::sm_AuditionSoundSetName[128] = {0};
char audNorthAudioEngine::sm_AuditionSoundName[128] = {0};
#endif

audController audNorthAudioEngine::sm_AudioController;
naEnvironment audNorthAudioEngine::sm_Environment;
naEnvironmentGroupManager audNorthAudioEngine::sm_EnvironmentGroupManager;
naOcclusionManager audNorthAudioEngine::sm_OcclusionManager;
audGunFireAudioEntity audNorthAudioEngine::sm_GunFireAudioEntity;
audSuperConductor audNorthAudioEngine::sm_SuperConductor;
audDynamicMixer audNorthAudioEngine::sm_DynamicMixer;
u32 audNorthAudioEngine::sm_LastLoudSoundTime = 0;
bool audNorthAudioEngine::sm_ShouldMuteAudio = false;
f32 audNorthAudioEngine::sm_EngineVolume = 0.0f;
f32 audNorthAudioEngine::sm_PlayerVehicleOpenness = 1.0f;

//Matrix34 audNorthAudioEngine::sm_ListenerMatrix;
bool audNorthAudioEngine::sm_Paused = false;
bool audNorthAudioEngine::sm_PausedLastFrame = false;
u32 audNorthAudioEngine::sm_TimeInMs = 0;
u32 audNorthAudioEngine::sm_LastTimeInMs = 0;
audSmoother audNorthAudioEngine::sm_RadioDuckingSmoother;
audSmoother audNorthAudioEngine::sm_GPSDuckingSmoother;
audSmoother audNorthAudioEngine::sm_TrainRolloffInSubwaySmoother;
f32 audNorthAudioEngine::sm_FrontendRadioVolumeLin;
u32 audNorthAudioEngine::sm_LastTimeSpecialAbilityActive = 0;
Vec3V audNorthAudioEngine::sm_PedPosLastFrame;

float audNorthAudioEngine::sm_GameTimeHours = 0.f;

audSmoother audNorthAudioEngine::sm_TimeWarpSmoother;
#if GTA_REPLAY
audCurve audNorthAudioEngine::sm_ReplayTimeWarpToPitch;
audCurve audNorthAudioEngine::sm_ReplayTimeWarpToTimeScale;
bool audNorthAudioEngine::sm_AreReplayBanksLoaded = false;
#endif
audCurve audNorthAudioEngine::sm_TimeWarpToPitch;
audCurve audNorthAudioEngine::sm_TimeWarpToTimeScale;
audCurve g_TimeWarpToDelayFeedback;
sysIpcSema audNorthAudioEngine::sm_RunUpdateSema;
sysIpcSema audNorthAudioEngine::sm_UpdateFinishedSema;
sysIpcThreadId audNorthAudioEngine::sm_UpdateThread;
bool audNorthAudioEngine::sm_RunUpdateInSeperateThread = true;
bool audNorthAudioEngine::sm_IsInSlowMo = false;
bool audNorthAudioEngine::sm_ForceSlowMoVideoEditor = false;
bool audNorthAudioEngine::sm_ForceSuperSlowMoVideoEditor = false;
bool audNorthAudioEngine::sm_WaitingForPauseMenuSlowMoToEnd = false;
SlowMoType audNorthAudioEngine::sm_SlowMoMode = AUD_SLOWMO_GENERAL;
u32 audNorthAudioEngine::sm_ActiveSlowMoModes[NUM_SLOWMOTYPE] = {0};

audScene * audNorthAudioEngine::sm_AmbienceMuted = NULL;
audScene * audNorthAudioEngine::sm_CutsceneMuted = NULL;
audScene * audNorthAudioEngine::sm_FrontendMuted = NULL;
audScene * audNorthAudioEngine::sm_MusicMuted = NULL;
audScene * audNorthAudioEngine::sm_RadioMuted = NULL;
audScene * audNorthAudioEngine::sm_SfxMuted = NULL;
audScene * audNorthAudioEngine::sm_SpeechMuted = NULL;
audScene * audNorthAudioEngine::sm_GunsMuted = NULL;
audScene * audNorthAudioEngine::sm_VehiclesMuted = NULL;

audScene * audNorthAudioEngine::sm_NorthAudioMixScene = NULL;
audScene * audNorthAudioEngine::sm_MuteAllScene = NULL;
audScene * audNorthAudioEngine::sm_DeathScene = NULL;
audScene * audNorthAudioEngine::sm_MpLobbyScene = NULL;
audScene * audNorthAudioEngine::sm_ReplayEditorScene = NULL;
audScene * audNorthAudioEngine::sm_TvScene = NULL;
audScene * audNorthAudioEngine::sm_FrontendScene = NULL;
audScene * audNorthAudioEngine::sm_GameWorldScene = NULL;
audScene * audNorthAudioEngine::sm_PosRadioMuteScene = NULL;
audScene * audNorthAudioEngine::sm_SlowMoScene = NULL;
audScene * audNorthAudioEngine::sm_SlowMoVideoEditorScene = NULL;
audScene * audNorthAudioEngine::sm_SuperSlowMoVideoEditorScene = NULL;
audScene * audNorthAudioEngine::sm_PlayerInVehScene = NULL;
audScene * audNorthAudioEngine::sm_SpeechScene = NULL;
audScene * audNorthAudioEngine::sm_GameSuspendedScene = NULL;
audSound * audNorthAudioEngine::sm_SlowMoSound = NULL;
audScene * audNorthAudioEngine::sm_FirstPersonModeScene = NULL;

u32 audNorthAudioEngine::sm_TimeSpeechSceneLastStarted = 0;
u32 audNorthAudioEngine::sm_TimeSpeechSceneLastStopped = 0;
f32 audNorthAudioEngine::sm_SpeechGunfirePatchApplyAtStart = 0.0f;
f32 audNorthAudioEngine::sm_SpeechScorePatchApplyAtStart = 0.0f;
f32 audNorthAudioEngine::sm_SpeechVehiclesPatchApplyAtStart = 0.0f;
f32 audNorthAudioEngine::sm_SpeechVehiclesFirstPersonPatchApplyAtStart = 0.0f;
f32 audNorthAudioEngine::sm_SpeechWeatherPatchApplyAtStart = 0.0f;
f32 audNorthAudioEngine::sm_SpeechGunfirePatchApplyAtStop = 0.0f;
f32 audNorthAudioEngine::sm_SpeechScorePatchApplyAtStop = 0.0f;
f32 audNorthAudioEngine::sm_SpeechVehiclesPatchApplyAtStop = 0.0f;
f32 audNorthAudioEngine::sm_SpeechVehiclesFirstPersonPatchApplyAtStop = 0.0f;
f32 audNorthAudioEngine::sm_SpeechWeatherPatchApplyAtStop = 0.0f;
bool audNorthAudioEngine::sm_SpeechSceeneApplied = false;

audDynMixPatch * audNorthAudioEngine::sm_SpeechGunfirePatch=NULL;
audDynMixPatch * audNorthAudioEngine::sm_SpeechScorePatch=NULL;
audDynMixPatch * audNorthAudioEngine::sm_SpeechVehiclesPatch=NULL;
audDynMixPatch * audNorthAudioEngine::sm_SpeechVehiclesFirstPersonPatch=NULL;
audDynMixPatch * audNorthAudioEngine::sm_SpeechWeatherPatch=NULL;

const audCategory *audNorthAudioEngine::sm_ScoreProxyCat = NULL;
const audCategory *audNorthAudioEngine::sm_RadioProxyCat = NULL;
audCategoryController *audNorthAudioEngine::sm_ScoreVolCategoryController = NULL;
audCategoryController *audNorthAudioEngine::sm_OneShotVolCategoryController = NULL;
audCategoryController *audNorthAudioEngine::sm_MusicSliderController = NULL;
audCategoryController *audNorthAudioEngine::sm_SFXSliderController = NULL;
audCategoryController *audNorthAudioEngine::sm_NoFadeVolCategoryController = NULL;
audCategoryController *audNorthAudioEngine::sm_ScriptedOverrideFadeVolCategoryController = NULL;

#if RSG_PS3 || RSG_ORBIS
float g_PulseHPFCutoff = 50.f;
bool audNorthAudioEngine::sm_ShouldTriggerPulseHeadset = false;
audScene *audNorthAudioEngine::sm_PulseHeadsetScene = NULL;
bool audNorthAudioEngine::sm_IsWirelessHeadsetConnected = false;
s32 audNorthAudioEngine::sm_PreviousOutputPref = 0;
#endif // RSG_PS3 || RSG_ORBIS

audScene *audNorthAudioEngine::sm_StonedScene = NULL;

naMicrophones audNorthAudioEngine::sm_Microphones;

f32 audNorthAudioEngine::sm_SfxVolume = 1.f;
f32 audNorthAudioEngine::sm_MusicVolume = 1.f;

f32 audNorthAudioEngine::sm_ThirdPersonCameraBlendThreshold = 1.0f;
u32 audNorthAudioEngine::sm_LoadingScreenSFXSliderDelay = 0;

volatile bool audNorthAudioEngine::sm_IsShuttingDown = false;

bool audNorthAudioEngine::sm_HasAppliedARC1BankRemappings = false;
bool audNorthAudioEngine::sm_IsFadedToBlack = false;
bool audNorthAudioEngine::sm_ScreenFadedOutThisFrame = false;

audCurve audNorthAudioEngine::sm_BuiltUpToWeaponTails;

u32 audNorthAudioEngine::sm_PauseMenuSlowMoCount = 0;

extern CPlayerSwitchInterface g_PlayerSwitch;

#if GTA_REPLAY
bool g_UseSingleListener = false;
bool g_UseMonoReverbs = false;
bool g_EnableHPF = true;
bool g_EnableLPF = true;
f32 g_AudioFrameTime = 15.f;
extern bool g_VehicleEffectsBypass;
#endif

#if __BANK
u32 audNorthAudioEngine::sm_CurrentMeterIndex = 0;
audMeterList *audNorthAudioEngine::sm_MeterList[g_MaxBufferedMeters];

RegdEnt g_AudioDebugEntity(NULL);
audSound *audNorthAudioEngine::sm_DebugSound = NULL;
#endif

sysMemAllocator *g_AudioPhysicalAllocator;
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
void *g_AudioPhysicalMem;
#endif

#if RSG_PC || RSG_DURANGO || RSG_ORBIS
	#define MAX_WAVE_DATA (24 * 1024 * 1024 * 10)
#elif RSG_XENON
	#define MAX_WAVE_DATA (26 * 1024 * 1024)
#else
	#define MAX_WAVE_DATA (24 * 1024 * 1024)
#endif

const s32 g_AudioUpdateThreadCPU = 5;

// Audio dlc pack order - later packs override previous packs.
#define FIXED_DLC_PACK_ORDER_COUNT 20
const char * g_AudioDLCPackOrder[FIXED_DLC_PACK_ORDER_COUNT] = 
{ 
	"SinglePlayer",
	"GTAOnline",
	"dlcbeach",
	"dlcvalentines",
	"dlcbusiness",
	"dlcbusi2",
	"dlchipster",
	"dlcindependence",
	"dlcpilotschool",
	"dlcmpLTS",
	"dlcxmas2",
	"dlcmpheist",
	"dlcpd03",
	"dlcluxe",
	"dlcluxe2",
	"dlcsfx1",
	"dlclowrider",
	"dlchalloween",
	"dlcapartment",
	"dlcxmas3"
};

u32 GetSizeOfLargestVehicleClass()
{
	size_t maxSize = 0;

	maxSize = Max(sizeof(audCarAudioEntity), maxSize);
	maxSize = Max(sizeof(audHeliAudioEntity), maxSize);
	maxSize = Max(sizeof(audTrainAudioEntity), maxSize);
	maxSize = Max(sizeof(audBoatAudioEntity), maxSize);
	maxSize = Max(sizeof(audPlaneAudioEntity), maxSize);
	maxSize = Max(sizeof(audTrailerAudioEntity), maxSize); 
	maxSize = Max(sizeof(audBicycleAudioEntity), maxSize); 

	return (u32) maxSize;
}

static const int kSizeOfLargestVehicleClassAudio = GetSizeOfLargestVehicleClass();

INSTANTIATE_POOL_ALLOCATOR(audVehicleAudioEntity);

audCategoryController *g_BaseCategory;
audCategoryController *g_GameWorldCategory;

PF_PAGE(AudioEntitiesPage, "GTA Audio Entities");
PF_GROUP(AudioEntities);
PF_LINK(AudioEntitiesPage, AudioEntities);

PF_VALUE_FLOAT(UpdateLength, AudioEntities);
PF_VALUE_FLOAT(SlotUpdateLength, AudioEntities);
PF_VALUE_FLOAT(ControllerUpdateLength, AudioEntities);

PF_PAGE(NorthAudioPage, "North Audio Engine");
PF_GROUP(NorthAudio);
PF_LINK(NorthAudioPage, NorthAudio);
PF_VALUE_INT(MaxVoicesPerBucket, NorthAudio);
PF_VALUE_INT(MaxSoundsPerBucket, NorthAudio);
PF_VALUE_INT(MaxRequestedSettingsPerBucket, NorthAudio);

PF_PAGE(NorthAudioUpdatePage, "North Audio Update");
PF_GROUP(NorthAudioUpdate);
PF_LINK(NorthAudioUpdatePage, NorthAudioUpdate);
PF_TIMER(NorthAudioUpdateTimer, NorthAudioUpdate);
PF_TIMER(NorthAudioUpdateTimer_Update, NorthAudioUpdate);
PF_TIMER(NorthAudioUpdateTimer_CommandBuffer, NorthAudioUpdate);
PF_TIMER(NorthAudioUpdateTimer_FinishUpdate, NorthAudioUpdate);

PF_PAGE(TriggerSoundsPage, "TriggeredSounds");
PF_GROUP(TriggerSoundsGroup);
PF_LINK(TriggerSoundsPage, TriggerSoundsGroup);

PARAM(mute, "[RAGE Audio] Mutes audio.");
PARAM(nogameaudio, "[Audio] Disable game audio engine init for offline tools (may not work in-game)");

XPARAM(audiodesigner);
XPARAM(audiotag);
PARAM(radiohudwidgets, "Activates radio hud widgets");

NOSTRIP_PC_PARAM(minspecaudio, "Force minspec audio processing");
NOSTRIP_PC_PARAM(nominspecaudio, "Force normal audio processing regardless of CPU");

namespace rage
{
	NOSTRIP_XPARAM(audiofolder);
}

#if !__FINAL
XPARAM(allcutsceneaudio);
#endif

#if __BANK
// offline compute audio world sectors
PARAM(calculateAudioWorldSectors, "[Audio] Enabled audio world sectors offline computation.");
PARAM(inGameAudioWorldSectors, "[Audio] Enabled audio world sectors online info.");
namespace rage
{
XPARAM(rave);
XPARAM(raveskipchunks);
PARAM(raveSkipReleasedDlcChunks, "[RAGE Audio] Automatically skips a hard coded list of metadata chunks");
XPARAM(fullsoundhierarchy);
XPARAM(noaudio);
XPARAM(audiowidgets);
XPARAM(printlargesounds);
XPARAM(loadtestsounds);
extern bool g_WarnOnMissingSounds;
extern bool g_WarnOnMissingCurves;
extern bool g_FullCurveTweakAbility;
}
XPARAM(Audio_tty);
XPARAM(usefatcuts);

extern bool g_UseEditedCutscenes;
extern bool g_UseMasterCutscenes;
#endif

#if __DEV
namespace rage
{
extern u32 g_MaxRequestedSettingsSlotsUsed;
extern u32 g_MaxSoundSlotsUsed;
extern u32 g_MaxVoicesUsed;
extern u32 g_MaxSoundsAllocated;
extern u32 g_MaxActiveEnvironmentGroups;
extern u32 g_MaxActiveAudioEntities;
extern u32 g_MaxActiveVirtualVoices;
extern u32 g_MaxActiveVoiceGroups;
extern bool g_AssertOnSoundPoolFull;
extern bool g_PrintSoundPoolWhenFull;
}
#endif

#if !__FINAL
PARAM(audiomem, "[Audio] write audio memory report");

class audMemoryReporter
{
public:
	audMemoryReporter(const char *destFileName)
	{
		m_Stream = ASSET.Create(destFileName, "");
		m_Total = 0;
	}

	bool HasStream() const
	{
		return m_Stream != NULL;
	}

	void AddLine(const char *str)
	{
		char lineBuf[128];
		formatf(lineBuf, "%s\r\n", str);
		m_Stream->Write(lineBuf, istrlen(lineBuf));
	}

	void AddLine(const char *entry, const u32 sizeBytes)
	{
		char lineBuf[128];
		u32 sizeKb = sizeBytes >> 10;
		formatf(lineBuf, "%s,%uKB\r\n", entry, sizeKb);
		m_Stream->Write(lineBuf, istrlen(lineBuf));
		m_Values.PushAndGrow(sizeBytes);
		m_Total += sizeBytes;
	}


	void AddMetadata(const char *entry, const audMetadataManager &metadataMgr)
	{
		char lineBuf[128];
		u32 size = metadataMgr.GetLoadedDataSize();
		m_Total += size;
		m_Values.PushAndGrow(size);
		formatf(lineBuf, "%s_Data: %uKB (Source CL %u)\r\n", entry, size >> 10, metadataMgr.GetAssetChangelist());
		m_Stream->Write(lineBuf, istrlen(lineBuf));
		size = metadataMgr.GetLoadedNameTableSize();
		m_Total += size;
		m_Values.PushAndGrow(size);
		formatf(lineBuf, "%s_Names: %uKB\r\n", entry, size >> 10);
		m_Stream->Write(lineBuf, istrlen(lineBuf));
	}

	void Close()
	{
		PrintTotal();
		PrintCSV();
		m_Stream->Close();
	}

private:

	void PrintCSV()
	{
		char lineBuf[64];
		bool needComma = false;
		for(s32 i = 0 ; i < m_Values.GetCount(); i++)
		{
			formatf(lineBuf, "%s%u", needComma ? "," : "", m_Values[i]);
			m_Stream->Write(lineBuf, istrlen(lineBuf));
			needComma = true;
		}

		formatf(lineBuf, ",%u\r\n", m_Total);
		m_Stream->Write(lineBuf, istrlen(lineBuf));
	}

	void PrintTotal()
	{
		char lineBuf[128];
		formatf(lineBuf, "\r\nTotal: %.2fMB\r\n", m_Total / (1024.f * 1024.f));
		m_Stream->Write(lineBuf, istrlen(lineBuf));
	}

	atArray<u32> m_Values;
	u32 m_Total;
	fiStream *m_Stream;
};

void audNorthAudioEngine::GenerateMemoryReport(const char *destFileName)
{
	char lineBuf[128];

	audMemoryReporter report(destFileName);
	if(report.HasStream())
	{
		const char *platform = "None";
#if RSG_XENON
		platform = "Xbox360";
#elif RSG_PS3
		platform = "PS3";
#elif RSG_ORBIS
		platform = "Orbis";
#elif RSG_DURANGO
		platform = "Durango";
#elif RSG_PC
		platform = "PC";
#else 
	#error "Not Implemented"
#endif
		
		report.AddLine(formatf(lineBuf, "%s Audio memory usage report\r\n", platform));

#if __BANK
		if(PARAM_rave.Get())
		{
			report.AddLine(formatf(lineBuf, "Warning: running with RAVE - memory usage will be inflated\r\n"));
		}
#endif // __BANK
		report.AddLine("WaveSlots_Main", audWaveSlot::GetTotalSlotSizeMain());
#if __PS3
		report.AddLine("WaveSlots_VRAM", audWaveSlot::GetTotalSlotSizeVRAM());
#endif

		report.AddMetadata("Category", g_AudioEngine.GetCategoryManager().GetMetadataManager());
		report.AddMetadata("Curve", g_AudioEngine.GetCurveRepository().GetMetadataManager());
		report.AddMetadata("DynamicMixer", g_AudioEngine.GetDynamicMixManager().GetMetadataManager());
		report.AddMetadata("GameObject", audNorthAudioEngine::GetMetadataManager());
		report.AddMetadata("Sound", g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager());
		report.AddMetadata("ModularSynth", synthSynthesizer::GetMetadataManager());
		report.AddMetadata("Speech", audSpeechSound::GetMetadataManager());

		report.Close();
	}

}

#endif

RAGE_DEFINE_SUBCHANNEL(Audio,NorthAudio)

#if __USEDEBUGAUDIO
class audNorthAudioEngineThreadVerifier : public audControllerThreadVerifier
{
public:
	void OnCreateSound(const audMetadataRef &soundRef) const
	{
		if(sysThreadType::IsUpdateThread() && audNorthAudioEngine::IsAudioUpdateCurrentlyRunning())
		{
			const char *soundName = "invalid";
			if(soundRef.IsValid())
			{
				const Sound *s = reinterpret_cast<const Sound*>(g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectMetadataPtr(soundRef));
				if(s)
				{
					soundName = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectNameFromNameTableOffset(s->NameTableOffset);
				}
			}
			naAssertf(false, "Creating a sound (%s) from the main thread while audNorthAudioEngine Update is running.", soundName);
		}
	}
};
audNorthAudioEngineThreadVerifier g_NorthAudioThreadVerifier;
#endif

void InitialiseAudioForInstall()
{
	audNorthAudioEngine::InitClass();
}

bool audNorthAudioEngine::InitClass()
{
	if(g_AudioInitialised == true)
		return true;

#if !__FINAL
	if(PARAM_nogameaudio.Get())
		return true;
#endif

#if !RSG_FINAL
	PARAM_allcutsceneaudio.Set("yes");	
#endif 

#if __BANK
	if(PARAM_allcutsceneaudio.Get())
	{
		PARAM_loadtestsounds.Set("yes"); 
	}
	if(PARAM_audiodesigner.Get())
	{
		audEnvironmentSound::SetRecordInvalidCategorySounds(true);

		naDisplayf("Enabling audio designer support");
		if(!PARAM_rave.Get())
		{
			PARAM_rave.Set("Sounds,GameObjects,Curves,Categories,ModularSynth,DynamicMixer,AudioConfig"); 
		}
#if RSG_PS3 || RSG_XENON
		if(!PARAM_raveskipchunks.Get())
		{
			PARAM_raveskipchunks.Set("base,singleplayer,optimised");
		}
#endif

		if (PARAM_raveSkipReleasedDlcChunks.Get() && !PARAM_raveskipchunks.Get())
		{
			PARAM_raveskipchunks.Set("dlcbeach,dlcbusiness,dlcbusi2,dlclowrider,dlclow2,dlcImportExport,dlcSpecialRaces,dlcGunrunning,dlcAirraces,dlcSmuggler,dlcChristmas2017,dlcAssault,dlcBattle,dlcAWXM2018,dlchalloween,dlcvalentines,dlchipster,dlcApartment,dlcJanuary2016,mpValentines2,dlcExec1,dlcStunt,dlcBiker,dlcmpheist,dlcpd03,dlcpd02,dlcpilotschool,dlcindependence,dlcmplts,dlcthelab,dlcxmas2,DLC_RADIOSURF,dlcluxe,dlcXmas3,dlcluxe2,dlcsfx1,dlcVinewood,dlcHeist3");
		}

		PARAM_fullsoundhierarchy.Set("yes");
		PARAM_printlargesounds.Set("yes");

		Channel_Audio_NorthAudio.TtyLevel = DIAG_SEVERITY_DEBUG3; // PARAM_Audio_NorthAudio_tty.Set("debug3");
		Channel_Audio.TtyLevel = DIAG_SEVERITY_DEBUG3; // PARAM_Audio_tty.Set("debug3");
		PARAM_audiowidgets.Set("yes");
		PARAM_radiohudwidgets.Set("yes");
	

		sm_AuditionSoundsOnPPU = true;
		
		g_WarnOnMissingCurves = true;
		// No point warning on missing sounds if -noaudio is specified.
		g_WarnOnMissingSounds = !PARAM_noaudio.Get();

		g_FullCurveTweakAbility = true;
		
		// default to ignoring frontend sliders
		g_IgnoreFrontendVolumeSliders = true;
	}
#endif

	USE_MEMBUCKET(MEMBUCKET_AUDIO);
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
	u32 memSize = fwConfigManager::GetInstance().GetSizeOfPool(ATSTRINGHASH("AudioHeap", 0x5293FA5), CONFIGURED_FROM_FILE) * 1024 * 1024;
#if RSG_DURANGO || RSG_ORBIS	
	g_AudioPhysicalMem = sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_VIRTUAL)->Allocate(memSize, 16);	
#else
	const u32 memForUserMusic = 13 * 1024 * 1024;
	memSize += memForUserMusic;

	// url:bugstar:6554866 - Heist 4 : Please increase audio heap for PC dev_ng so that we can have larger Streamed Ambience waveslots
	const u32 ambientBankCompensation = 4 * 1024 * 1024;
	memSize += ambientBankCompensation;

	g_AudioPhysicalMem = sysMemPhysicalAllocate(memSize);
#endif
	Assert(g_AudioPhysicalMem);

	g_AudioPhysicalAllocator = rage_new sysMemSimpleAllocator(g_AudioPhysicalMem, memSize, MEMTYPE_COUNT+1);

#if RAGE_TRACKING
	diagTracker* t = diagTracker::GetCurrent();
	if (t && sysMemVisualize::GetInstance().HasAudio())
	{
		t->InitHeap("Audio Physical", g_AudioPhysicalMem, memSize);
	}
#endif // RAGE_TRACKING

#else // RSG_PC || RSG_DURANGO || RSG_ORBIS

	g_AudioPhysicalAllocator = sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_VIRTUAL);

#endif //RSG_PC || RSG_DURANGO || RSG_ORBIS

	if(!InitializeAudio(sm_AudioController, g_AudioPhysicalAllocator, sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_VIRTUAL),
		g_AudioConfigPath))
	{
		return false;
	}

	LoadSPData();
	// Now keep MP speech data loaded in SP for reindeer assets
	audSpeechSound::LoadSpeechMetadata("GTAOnline", "audio:/config/dlc_gtao_speech.dat");

#if !__FINAL
	static naStreamingDebugInterface s_DebugStreamingInterface;
	audStreamingWaveSlot::SetDebugInterface(&s_DebugStreamingInterface);
#endif

	PS3_ONLY(CheckForWirelessHeadset(true));

#if RSG_BANK && 0
	if(!CFileMgr::ShouldForceReleaseAudioPacklist())
	{
		audDisplayf("Loading placeholder speech lookup");
		audSpeechSound::LoadSpeechMetadata("PlaceholderSpeech", "audio:/config/p_speech.dat");
	}
#endif

	DEBUGAUDIO_ONLY(sm_AudioController.SetThreadVerifier(&g_NorthAudioThreadVerifier));

#if __DEV
	g_AssertOnSoundPoolFull = false;
#endif

	// Assert if we've allocated too much memory for wave banks
	if(audWaveSlot::GetTotalSlotSize() > MAX_WAVE_DATA)
	{
		naWarningf("Total wave data size: %d", audWaveSlot::GetTotalSlotSize());
#if !__PPU
		// This error triggers every single time on PS3, so nerf it for now.
		naErrorf("Audio: Exceeded memory budget - please inform audio team.");
#endif
	}

	sm_MetadataMgr.Init("GameObjects", "audio:/config/game.dat", audMetadataManager::External_NameTable_BankOnly, GAMEOBJECTS_SCHEMA_VERSION, true);

	// Reserve the chunk order for DLC packs. Audio data for some packs will get unmounted on session shutdown then remounted for the next session, whilst 
	// for others it will be persistent, meaning that the order of loaded packs can change. However, we need the audio data order to be consistent so that 
	// we can guarantee that certain packs will override others, regardless of when they were loaded.
	for(u32 dlcPackIndex = 0; dlcPackIndex < FIXED_DLC_PACK_ORDER_COUNT; dlcPackIndex++)
	{		
		audDisplayf("Reserving metadata chunks for pack %s at index %d", g_AudioDLCPackOrder[dlcPackIndex], dlcPackIndex);
		sm_MetadataMgr.ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
		g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
		g_AudioEngine.GetDynamicMixManager().GetMetadataManager().ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
		g_AudioEngine.GetCurveRepository().GetMetadataManager().ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
		g_AudioEngine.GetCategoryManager().GetMetadataManager().ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
		synthSynthesizer::GetMetadataManager().ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
		audSpeechSound::GetMetadataManager().ReserveChunk(g_AudioDLCPackOrder[dlcPackIndex]);
	}

	// assert if we've not loaded valid game.dat
	naAssertf(sm_MetadataMgr.ComputeNumberOfObjects()>0, "Failed to load a valid game.dat in InitClass"); 

	sm_MetadataMgr.SetObjectModifiedCallback(&sm_GameObjectMgr);

	if(PARAM_mute.Get())
	{
		sm_ShouldMuteAudio = true;
	}

	g_AudioEngine.EnableAudio(true);

	sm_StreamingSoundBucketId = audSound::ReserveBucket();
	naDisplayf("Bucket reserved for streaming: %u", sm_StreamingSoundBucketId);
	VIDEO_PLAYBACK_ONLY( VideoPlayback::SetAudioBucketId( sm_StreamingSoundBucketId ) );

	sm_TimeWarpSmoother.Init(5.f/1000.f, 1.f/1000.f, 0.f, 1.f);

	// need to init emitter audio entity early to catch initial object creation
	g_EmitterAudioEntity.Init();

	sm_AudioController.SetEnvironmentGroupManager(&sm_EnvironmentGroupManager);

	sm_RadioDuckingSmoother.Init(g_RadioDuckingSmoothRateUp, g_RadioDuckingSmoothRate, 0.0f, 1.5f);
	sm_GPSDuckingSmoother.Init(g_RadioDuckingSmoothRate, g_RadioDuckingSmoothRate, 0.0f, 1.0f);
	sm_TrainRolloffInSubwaySmoother.Init(0.0003f, 0.0003f, 0.0f, 10.0f);
	
	audCategory *firstSpeechCat;
	firstSpeechCat = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("GLOBAL_SPEECH", 0xAFDC8D3));
	
	const u32 firstSpeechCatIndex = (u32)g_AudioEngine.GetCategoryManager().GetCategoryIndex(firstSpeechCat);
	g_AudioEngine.GetEnvironment().SetSpeechCategoryRange(firstSpeechCatIndex, firstSpeechCatIndex + 12);

	// need a listener
	g_AudioEngine.GetEnvironment().SetListenerContribution(1.0f,0);
	g_AudioEngine.GetEnvironment().SetRollOff(1.0f,0);
	g_AudioEngine.CommitGameSettings(0);
	// need to force an update so that the controller activates
	sm_AudioController.PreUpdate(0);
	sm_AudioController.Update(0);

	audCategoryControllerManager::Instantiate();
	audInteractiveMusicManager::Instantiate();

	// need a listener
	g_AudioEngine.GetEnvironment().SetListenerContribution(1.0f,0);
	g_AudioEngine.CommitGameSettings(0);
	// need to force an update so that the controller activates
	sm_AudioController.PreUpdate(0);
	sm_AudioController.Update(0);
	
	StartUserUpdateFunction();

	g_AudioInitialised = true;
	g_HasInitialisedStaticClasses = false;

	sm_IsShuttingDown = false;
	sm_RunUpdateSema = sysIpcCreateSema(0);
	sm_UpdateFinishedSema = sysIpcCreateSema(0);

	sm_UpdateThread = sysIpcCreateThread(AudioUpdateThread, NULL, 256*1024, PRIO_AUDIO_THREAD, "North Audio Update", g_AudioUpdateThreadCPU, "NorthAudioUpdate");

	audAmbisonics::SetDrawFunction(AmbisonicDraw);

	sm_Microphones.Init();

#if USE_GUN_TAILS
	sm_GunFireAudioEntity.Init(); 
#endif
#if USE_CONDUCTORS
	sm_SuperConductor.Init(); 
#endif
	audGlassAudioEntity::InitClass();
	audSmashableGlassEvent::InitClass();
	audCollisionAudioEntity::InitClass();

	sm_DynamicMixer.Init();

	g_FrontendAudioEntity.Init();

#if GTA_REPLAY
	g_ReplayAudioEntity.Init();
#endif

#if __BANK 
	if(PARAM_calculateAudioWorldSectors.Get())
	{
		sm_Environment.DrawAudioWorldSectors(true); 
		sm_Environment.ProcessSectors(true); 
	}
	else if (PARAM_inGameAudioWorldSectors.Get())
	{
		sm_Environment.ProcessSectors(true); 
	}
#endif
#if !__FINAL
	const char *reportFileName = "audiomem.txt";
	if(PARAM_audiomem.Get(reportFileName))
	{
		GenerateMemoryReport(reportFileName);
	}

#if !__FINAL_LOGGING
	// Display source asset changelists for all metadata types
	audDisplayf("Source asset CLs - Sounds: %u, GameObjects: %u, Categories: %u, DynamicMixer: %u, AudioConfig: %u, Curves: %u, Synth: %u Speech %u",
		g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetAssetChangelist(),
		sm_MetadataMgr.GetAssetChangelist(),
		g_AudioEngine.GetCategoryManager().GetMetadataManager().GetAssetChangelist(),
		g_AudioEngine.GetDynamicMixManager().GetMetadataManager().GetAssetChangelist(),
		audConfig::GetMetadataManager().GetAssetChangelist(),
		g_AudioEngine.GetCurveRepository().GetMetadataManager().GetAssetChangelist(),
		synthSynthesizer::GetMetadataManager().GetAssetChangelist(),
		audSpeechSound::GetMetadataManager().GetAssetChangelist());
#endif
#endif

	sm_ActiveSlowMoModes[AUD_SLOWMO_GENERAL] = ATSTRINGHASH("SLOWMO_GENERAL", 0x995E46B3);

	naAudioEntity::SetDefaultAnimHandler((fwAudioAnimHandlerInterface *)&sm_AudAnimHandler);

	BANK_ONLY(g_AudioEngine.GetCategoryManager().SetTelemetryEnabled(true));

	// Hook into the extra content mounting system
	RegisterDataFileMounters();

	/*
	g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().LoadMetadataChunk("DLC_RADIOSURF", "audio:/config/dlc_radiosurf_sounds.dat");
	GetMetadataManager().LoadMetadataChunk("DLC_RADIOSURF", "audio:/config/dlc_radiosurf_game.dat");
	audSpeechSound::GetMetadataManager().LoadMetadataChunk("DLC_RADIOSURF", "audio:/config/dlc_radiosurf_speech.dat");
	*/

	// This will be loaded as part of a propper dlc, un comment to force load temporarily.
	//g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().LoadMetadataChunk("ReplayED", 
	//	"audio:/config/dlcsfx1_sounds.dat",
	//	sm_ReplayedSoundDataPtr,
	//	sm_ReplayedSoundDataSize);
	//GetMetadataManager().LoadMetadataChunk("ReplayEdSFX", "audio:/config/dlcsfx1_game.dat");
	//if(g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().FindChunkId("ReplayEdSFX") == -1)
	//{
	//	Displayf("Failed to load ReplayED audio data");
	//}

	sm_RadioProxyCat = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("RADIO_VOL_PROXY", 0xA6A84701));
	sm_ScoreProxyCat = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("SCORE_VOL_PROXY", 0x3C496EED));

	sm_ScoreVolCategoryController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("INTERACTIVE_MUSIC", 0x05db47ec6));
	sm_OneShotVolCategoryController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("MUSIC_ONESHOT", 0x020230050));
	sm_SFXSliderController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("SFX_SLIDER", 0xBAD598C7));
	sm_MusicSliderController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("MUSIC_SLIDER", 0xA4D158B0));
	sm_NoFadeVolCategoryController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("FRONTEND_GAME_NOFADE", 0x2C7B342));
	sm_ScriptedOverrideFadeVolCategoryController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("SCRIPTED_OVERRIDE_FADE", 0xEB0390D7));

	sm_CutsceneLeakageSmoother.Init(g_CutsceneLeakageSmootherRate * 0.001f, true);

	sm_DataSetState = Loaded_SP;
	sm_IsMPDataRequested = false;

	const audMetadataManager &soundMetadataManager = g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager();
	const audMetadataChunk &spChunk = soundMetadataManager.GetChunk(soundMetadataManager.FindChunkId("SinglePlayer"));
	sm_SPSoundDataPtr = spChunk.data;
	sm_SPSoundDataSize = spChunk.dataSize;

#if RSG_BANK
	if(g_AudioEngine.GetRemoteControl().IsConnected())
	{
		audRemoteControlSerializer serializer(g_AudioEngine.GetRemoteControl().GetPayloadBuffer(), audRemoteControl::kRemoteControlPayloadSizeBytes);
		g_AudioEngine.GetRemoteControl().SendCommand(serializer, AUDIO_ENGINE_LOADED);
	}
#endif
	return true;
}

extern CutsceneSettings *g_DefaultCutsceneSettings;
void audNorthAudioEngine::Init(u32 initMode)
{
    if(initMode == INIT_CORE)
    {
        if(!InitClass())
        {
            Assertf(FALSE, "Failed to initialise Audio Engine!");
        }
		audController::UpdateEnvironmentGroups(true);
    }
	else if(initMode == INIT_AFTER_MAP_LOADED)
	{
		// Load additional map specific data for Liberty / Prologue
		// Alternative maps are DLC packs, so will load/unload any extra metadata via that mechanism
		//LoadMapData();
	}
    else if(/*initMode == INIT_AFTER_MAP_LOADED || */initMode == INIT_SESSION)
    {
	    g_AudioEngine.GetEnvironment().InitCurves();


		g_BaseCategory = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("BASE", 0x044e21c90));
	    g_GameWorldCategory = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("GAME_WORLD", 0x02729f75d));

		DYNAMICMIXER.StartScene(ATSTRINGHASH("NORTHAUDIO_ENGINE_MIXING", 0xa3657c2c), &sm_NorthAudioMixScene);
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("radioVol", 0xe7e3432c), 0.f);
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("radioFrontendVol", 0xc00f6acd), 0.f);
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("djFrontendVol", 0xc06429e6), 0.f);
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("posRadioVol", 0x4be8fa9a), 0.f);
		//sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("heliVol", 0x5acd3f4a), 0.f);
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("interiorRatio", 0x13fc3a70), 0.f);
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("scriptedSpeechVol", 0x5b3a463), 0.f);

		if(!sm_SpeechScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("SPEECH_SCENE", 0x576520E1),&sm_SpeechScene);
			if(sm_SpeechScene)
			{
				sm_SpeechScene->SetVariableValue(ATSTRINGHASH("apply", 0xE865CDE8), 1.f);
				sm_SpeechGunfirePatch = sm_SpeechScene->GetPatch(ATSTRINGHASH("SPEECH_GUNFIRE_PATCH", 0x82B39AD3));
				sm_SpeechScorePatch = sm_SpeechScene->GetPatch(ATSTRINGHASH("SPEECH_SCORE", 0x216E4ABF));
				sm_SpeechVehiclesPatch = sm_SpeechScene->GetPatch(ATSTRINGHASH("SPEECH_VEHICLES_PATCH", 0x283253));
				sm_SpeechVehiclesFirstPersonPatch = sm_SpeechScene->GetPatch(ATSTRINGHASH("SPEECH_VEHICLES_FIRST_PERSON_PATCH", 0xA2DA442));
				sm_SpeechWeatherPatch = sm_SpeechScene->GetPatch(ATSTRINGHASH("SPEECH_WEATHER_PATCH", 0x33B1F2AC));
			}
		}

	    // init the static emitters - this needs to be done after all dlc has been loaded.
	    g_EmitterAudioEntity.InitStaticEmitters();
	    // tell the emitter audio entity its safe to start triggering audio
	    g_EmitterAudioEntity.SetActive(true);
	    g_EmitterAudioEntity.ResetTimers();
	    g_WeatherAudioEntity.Init();


	    // this needs to be after we've loaded all episodic content
	    if(!g_HasInitialisedStaticClasses)
	    {
			CPacketSoundCreatePos::CalculateValidMetadataOffsets();

		    naEnvironmentGroup::InitClass();
			sm_EnvironmentGroupManager.Init();
		    sm_AudioController.SetEnvironmentGroupManager(&sm_EnvironmentGroupManager);

			naAudioEntity::InitClass();

			audStreamSlot::InitClass();
		    NA_RADIO_ENABLED_ONLY(audRadioAudioEntity::InitClass());

			audDoorAudioEntity::InitClass();
		    audScriptAudioEntity::InitClass();
		    audPedAudioEntity::InitClass();
		    audSpeechAudioEntity::InitClass();
			audVehicleReflectionsEntity::InitClass();
		    audVehicleAudioEntity::InitClass();
			audVehicleEngine::InitClass();
			audVehicleElectricEngine::InitClass();
			audVehicleTurbo::InitClass();
			audVehicleTransmission::InitClass();
			audVehicleCollisionAudio::InitClass();
			audHeliAudioEntity::InitClass();
			audPlaneAudioEntity::InitClass();
			audCarAudioEntity::InitClass();
			audBicycleAudioEntity::InitClass();
			audTrailerAudioEntity::InitClass();
			audBoatAudioEntity::InitClass();
			audTrainAudioEntity::InitClass();
			audAmbientZone::InitClass();
		    audWeatherAudioEntity::InitClass();
		    audExplosionAudioEntity::InitClass();
			audShoreLine::InitClass();
			audShoreLinePool::InitClass();
			audShoreLineRiver::InitClass();
			audShoreLineOcean::InitClass();
			audShoreLineLake::InitClass();
			audVehicleFireAudio::InitClass();
			audFireAudioEntity::InitClass();
			g_FireSoundManager.InitClass();
		    g_FrontendAudioEntity.InitCurves();
			g_HasInitialisedStaticClasses = true;
	    }

		sm_OcclusionManager.Init();
	    sm_Environment.Init();

	    sm_TimeWarpToPitch.Init(ATSTRINGHASH("TIME_WARP_TO_PITCH", 0x61E2DEFA));
	    sm_TimeWarpToTimeScale.Init(ATSTRINGHASH("TIME_WARP_TO_TIME_SCALE", 0xDDE168FB));
	    g_TimeWarpToDelayFeedback.Init(ATSTRINGHASH("TIME_WARP_TO_DELAY_FEEDBACK", 0x6E4FCCB0));
#if GTA_REPLAY
		sm_ReplayTimeWarpToTimeScale.Init(ATSTRINGHASH("REPLAY_TIME_WARP_TO_TIME_SCALE", 0xA2363059));
#endif

	    sm_BuiltUpToWeaponTails.Init(ATSTRINGHASH("BUILT_UP_TO_TAIL_VOLUME", 0xC94C4CC4));

	    NA_RADIO_ENABLED_ONLY(g_RadioAudioEntity.Init());
		NA_POLICESCANNER_ENABLED_ONLY(g_AudioScannerManager.Init());

	    g_CollisionAudioEntity.Init();
	    g_CutsceneAudioEntity.Init();
	    g_SpeechManager.Init();
	    g_ScriptAudioEntity.Init();
	    g_WeaponAudioEntity.Init();
	    g_GlassAudioEntity.Init();
	    g_AmbientAudioEntity.Init();
		g_ReflectionsAudioEntity.Init();
	    g_InteractiveMusicManager.Init();

	    g_MuteGameWorldAudio = false;
	    g_MuteGameWorldAndPositionedRadioForTV = false;

#if __BANK
	    // cutscene mix editor for Nick
	    bkBank *audioBank = BANKMGR.FindBank("CutsceneAudio");
	    if(audioBank)
	    {
		    BANKMGR.DestroyBank(*audioBank);
	    }
#endif

		sm_StartingNewGame = false;
    }

	sm_PIVMusicVolumeSmoother.Init(0.001f / g_PIVMusicVolumeFadeInS, 0.001f / g_PIVMusicVolumeFadeOutS, 0.f, 1.f);
	sm_PIVRadioVolumeSmoother.Init(0.001f / g_PIVMusicVolumeFadeInS, 0.001f / g_PIVMusicVolumeFadeOutS, 0.f, 1.f);
	sm_PIVOpennessSmoother.Init(0.001f / g_PIVOpennessFadeInS, 0.001f / g_PIVOpennessFadeOutS, 0.f, 1.f);
}

void audNorthAudioEngine::DLCInit(u32 initMode)
{
	if(initMode == INIT_SESSION)
	{
		g_EmitterAudioEntity.ReInitStaticEmitters();
		g_CollisionAudioEntity.ProcessMacsModelOverrides();
		g_AmbientAudioEntity.CheckAndAddZones();
		audVehicleConductor::InitForDLC();
		NA_RADIO_ENABLED_ONLY(audRadioAudioEntity::DLCInitClass();)
	}
}

void audNorthAudioEngine::Shutdown(u32 shutdownMode)
{
    if(shutdownMode == SHUTDOWN_CORE)
    {
        ShutdownClass();
    }
	else if(shutdownMode == SHUTDOWN_WITH_MAP_LOADED)
	{
		// Alternative maps are DLC packs, so will load/unload any extra metadata via that mechanism
		//UnloadMapData();
	}
    else if(shutdownMode ==  SHUTDOWN_SESSION)
    {
		sm_Environment.PurgeInteriors();

	    g_EmitterAudioEntity.SetActive(false);
        g_FrontendAudioEntity.ShutdownLevel();
        g_WeatherAudioEntity.Shutdown();
		g_GlassAudioEntity.Shutdown();
		g_InteractiveMusicManager.Shutdown();

        // this needs to be after we've loaded all episodic content
        if (false && g_HasInitialisedStaticClasses)
        {
			sm_EnvironmentGroupManager.Shutdown();
            naEnvironmentGroup::ShutdownClass();
            sm_AudioController.SetEnvironmentGroupManager(NULL);

            audStreamSlot::ShutdownClass();
            NA_RADIO_ENABLED_ONLY(audRadioAudioEntity::ShutdownClass());

            audScriptAudioEntity::ShutdownClass();
            audPedAudioEntity::ShutdownClass();
            audVehicleAudioEntity::ShutdownClass();
            audWeatherAudioEntity::ShutdownClass();
			audShoreLineOcean::ShutDownClass();
			audShoreLineRiver::ShutDownClass();
			audShoreLineLake::ShutDownClass();
			g_FireSoundManager.ShutdownClass();
            audInteractiveMusicManager::Destroy();
			audInteractiveMusicManager::Destroy();
			g_HasInitialisedStaticClasses = false;
        }

		sm_OcclusionManager.Shutdown();
        sm_Environment.Shutdown();

        NA_RADIO_ENABLED_ONLY(g_RadioAudioEntity.Shutdown());
        NA_POLICESCANNER_ENABLED_ONLY(g_AudioScannerManager.Shutdown());

        g_CollisionAudioEntity.Shutdown();
        g_CutsceneAudioEntity.Shutdown();
        g_SpeechManager.Shutdown();
        g_ScriptAudioEntity.Shutdown();
        g_WeaponAudioEntity.Shutdown();

        g_AmbientAudioEntity.Shutdown();
		audShoreLineOcean::ShutDownClass();
		audShoreLineRiver::ShutDownClass();
		audShoreLineLake::ShutDownClass();

		g_ReflectionsAudioEntity.Shutdown();

#if USE_GUN_TAILS
		sm_GunFireAudioEntity.Shutdown();
#endif

		CONDUCTORS_ONLY(SUPERCONDUCTOR.ShutDown());
		//This last just in case other things use categories ... 
        AUDCATEGORYCONTROLLERMANAGER.DestroyController(g_BaseCategory); g_BaseCategory = NULL;
        AUDCATEGORYCONTROLLERMANAGER.DestroyController(g_GameWorldCategory); g_GameWorldCategory = NULL;

	    // don't do this if the game is exiting
	    if(!CSystem::WantToExit())
	    {
		    // ditch any loads in progress to ensure we don't deadlock the streamer
		    audWaveSlot::Drain();
	    }
		// Force a synchronous update to ensure the Stop... calls are actioned immediately (it will be along time until the next update)
		audNorthAudioEngine::MinimalUpdate();

		if(sm_NorthAudioMixScene)
		{
			sm_NorthAudioMixScene->Stop();
		}
		if(sm_PlayerInVehScene)
		{
			sm_PlayerInVehScene->Stop();
		}
		if(sm_SpeechScene)
		{
			sm_SpeechScene->Stop();
		}
		if(sm_MuteAllScene)
		{
			sm_MuteAllScene->Stop();
		}
		if(sm_DeathScene)
		{
			sm_DeathScene->Stop();
		}
		if(sm_MpLobbyScene)
		{
			sm_MpLobbyScene->Stop();
		}
		if(sm_ReplayEditorScene)
		{
			sm_ReplayEditorScene->Stop();
		}
		if(sm_TvScene)
		{
			sm_TvScene->Stop();
		}
		if(sm_FrontendScene)
		{
			sm_FrontendScene->Stop();
		}
		if(sm_GameWorldScene)
		{
			sm_GameWorldScene->Stop();
		}
		if(sm_PosRadioMuteScene)
		{
			sm_PosRadioMuteScene->Stop();
		}
		if(sm_SlowMoScene)
		{
			sm_SlowMoScene->Stop();
		}
		if(sm_SlowMoVideoEditorScene)
		{
			sm_SlowMoVideoEditorScene->Stop();
		}
		if(sm_SuperSlowMoVideoEditorScene)
		{
			sm_SuperSlowMoVideoEditorScene->Stop();
		}
		if(sm_SlowMoSound)
		{
			sm_SlowMoSound->StopAndForget();
		}
	}
}

void audNorthAudioEngine::ShutdownClass()
{
	// if shutdown already then return
	if(g_AudioInitialised == false)
		return;

	if(sm_ExtraContentPath)
	{
		delete[] sm_ExtraContentPath;
		sm_ExtraContentPath = NULL;
	}
#if GTA_REPLAY
	g_ReplayAudioEntity.Shutdown();
#endif
	g_FrontendAudioEntity.Shutdown();
	g_EmitterAudioEntity.Shutdown();
	audScriptAudioEntity::ShutdownClass();
	audPedAudioEntity::ShutdownClass();

	audWeatherAudioEntity::ShutdownClass();
	audVehicleAudioEntity::ShutdownClass();

	sm_DynamicMixer.Shutdown();

	sm_RadioProxyCat = NULL;
	sm_ScoreProxyCat = NULL;

	AUDCATEGORYCONTROLLERMANAGER.DestroyController(sm_ScoreVolCategoryController);
	sm_ScoreVolCategoryController = NULL;
	AUDCATEGORYCONTROLLERMANAGER.DestroyController(sm_OneShotVolCategoryController);
	sm_OneShotVolCategoryController = NULL;
	AUDCATEGORYCONTROLLERMANAGER.DestroyController(sm_SFXSliderController);
	sm_SFXSliderController = NULL;
	AUDCATEGORYCONTROLLERMANAGER.DestroyController(sm_MusicSliderController);
	sm_MusicSliderController = NULL;
	AUDCATEGORYCONTROLLERMANAGER.DestroyController(sm_NoFadeVolCategoryController);
	sm_NoFadeVolCategoryController = NULL;
	AUDCATEGORYCONTROLLERMANAGER.DestroyController(sm_ScriptedOverrideFadeVolCategoryController);
	sm_ScriptedOverrideFadeVolCategoryController = NULL;

	NA_RADIO_ENABLED_ONLY(audRadioAudioEntity::ShutdownClass());

	audStreamSlot::ShutdownClass();

	sm_AudioController.Shutdown();
	g_AudioEngine.Shutdown();

	sm_MetadataMgr.Shutdown();

	StopUserUpdateFunction();

	//Destroy the category manager
	audCategoryControllerManager::Destroy();

#if RSG_PC || RSG_DURANGO || RSG_ORBIS
	delete g_AudioPhysicalAllocator;

#if RSG_DURANGO || RSG_ORBIS
	sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_VIRTUAL)->Free(g_AudioPhysicalMem);
#else
	sysMemPhysicalFree(g_AudioPhysicalMem);
#endif

#endif

	sm_IsShuttingDown = true;
	sysIpcSignalSema(sm_RunUpdateSema);
	sysIpcWaitThreadExit(sm_UpdateThread);
	sysIpcDeleteSema(sm_RunUpdateSema);
	sysIpcDeleteSema(sm_UpdateFinishedSema);

	sm_RunUpdateSema = 0;
	sm_UpdateFinishedSema = 0;
	sm_UpdateThread = sysIpcThreadIdInvalid;

	g_AudioInitialised = false;
}


audMetadataDataFileMounter::audMetadataDataFileMounter(audMetadataManager &metadataManager) : m_MetadataManager(metadataManager)
{
	m_UnloadingChunksCount = 0;
	for(int i=0; i < k_MaxDlcPacks; i++)
	{
		m_FramesToUnload[i] = 0;
		m_UnloadingChunkNames[i][0] = '\0';
	}
}

bool audMetadataDataFileMounter::LoadDataFile(const CDataFileMgr::DataFile &file)
{	
	char chunkName[k_ChunkNameLength];
	DeriveChunkName(file.m_filename, chunkName, sizeof(chunkName));
	audDisplayf("Loading DLC audio chunk %s from %s, time %d", chunkName, file.m_filename, fwTimer::GetTimeInMilliseconds());

	bool alreadyLoaded = false;
	//If we have a defered unload of this pack, cancel it
	for(int i=0; i < k_MaxDlcPacks; i++)
	{
		if(m_UnloadingChunkNames[i][0] != '\0' && atStringHash(chunkName) == atStringHash(m_UnloadingChunkNames[i]))
		{
			audDisplayf("Was unloading %s, cancelling", chunkName);
			m_UnloadingChunkNames[i][0] = '\0';
			m_FramesToUnload[i] = 0;
			--m_UnloadingChunksCount;
			alreadyLoaded = true;
		}
	}
	bool success = false;
	
	if(alreadyLoaded)
	{
		success = true;
	}
	else
	{
		success = m_MetadataManager.LoadMetadataChunk(chunkName, file.m_filename);
	}

	if(file.m_fileType == CDataFileMgr::AUDIO_GAMEDATA)
	{
		int chunkId = audNorthAudioEngine::GetMetadataManager().FindChunkId(chunkName);
		audMetadataObjectInfo objectInfo;
		if(audNorthAudioEngine::GetMetadataManager().GetObjectInfoFromSpecificChunk(ATSTRINGHASH("RADIO_STATIONS_DLC", 0x953BD40D), static_cast<u32>(chunkId), objectInfo))
		{
			audRadioStation::MergeRadioStationList(objectInfo.GetObject<RadioStationList>());
		}
	}

#if !__NO_OUTPUT
	if(success)
	{
		const s32 chunkId = m_MetadataManager.FindChunkId(chunkName);
		audDisplayf("Successfully loaded DLC audio chunk %s, changlist %u", file.m_filename, m_MetadataManager.GetAssetChangelist(chunkId));
	}	
#endif

	if(success && file.m_fileType == CDataFileMgr::AUDIO_DYNAMIXDATA)
	{
		g_AudioEngine.GetDynamicMixManager().InitMixgroupsForChunk(g_AudioEngine.GetDynamicMixManager().GetMetadataManager().FindChunkId(chunkName));
	}

	return success;
}

void audMetadataDataFileMounter::UnloadDataFile(const CDataFileMgr::DataFile &file)
{
	char chunkName[k_ChunkNameLength];
	DeriveChunkName(file.m_filename, chunkName, sizeof(chunkName));
	audDisplayf("Starting unload of DLC audio chunk %s from %s time %d", chunkName, file.m_filename, fwTimer::GetTimeInMilliseconds());
	if(file.m_fileType == CDataFileMgr::AUDIO_DYNAMIXDATA)
	{
		audNorthAudioEngine::GetDynamicMixer().StopScenesFromChunk(chunkName);
	}
	if(file.m_fileType == CDataFileMgr::AUDIO_GAMEDATA)
	{
		const s32 chunkId = audNorthAudioEngine::GetMetadataManager().FindChunkId(chunkName);
		audMetadataObjectInfo objectInfo;
		if(audNorthAudioEngine::GetMetadataManager().GetObjectInfoFromSpecificChunk(ATSTRINGHASH("RADIO_STATIONS_DLC", 0x953BD40D), static_cast<u32>(chunkId), objectInfo))
		{
			audDisplayf("Unloading %s which contained DLC radio stations", chunkName);
			audRadioStation::RemoveRadioStationList(objectInfo.GetObject<RadioStationList>());
		}

		g_CollisionAudioEntity.RemoveMacsModelOverridesForChunk(chunkId, ATSTRINGHASH("MACS_MODELS_OVERRIDES", 0x53F9AB71));
		g_CollisionAudioEntity.RemoveMacsModelOverridesForChunk(chunkId, ATSTRINGHASH("MACS_MODEL_OVERRIDE_LIST_GEN9", 0x149D39FA));
	}

	u32 indexToAdd = k_MaxDlcPacks;
	for(int i=0; i < k_MaxDlcPacks; i++)
	{
		if(m_UnloadingChunkNames[i][0] == '\0')
		{
			indexToAdd = i;
			break;
		}
	}
	FatalAssertf(indexToAdd < k_MaxDlcPacks, "Increase the max number of dlc packs for audMetadataFileMounter");
	m_FramesToUnload[indexToAdd] = 1;
	formatf(m_UnloadingChunkNames[indexToAdd], k_ChunkNameLength, chunkName);
	++m_UnloadingChunksCount;
}

void audMetadataDataFileMounter::Update()
{
	if(m_UnloadingChunksCount > 0)
	{
		for(int i=0; i < k_MaxDlcPacks; i++)
		{
			if(m_FramesToUnload[i] > 0)
			{
				--m_FramesToUnload[i];
			}
			else if(m_UnloadingChunkNames[i][0] != '\0')
			{
				naDebugf1("Attempting unload (deferred) chunk %s metadata type %u, time %d, frame %d", m_UnloadingChunkNames[i], m_MetadataManager.GetMetadataTypeHash(), fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
				if(m_MetadataManager.GetMetadataTypeHash() == ATSTRINGHASH("Sounds", 0x94FA081B))
				{
					if(!g_AudioEngine.GetSoundManager().IsAnyChunkBeingUnloaded())
					{
						g_AudioEngine.RequestThreadPause(true);
						g_AudioEngine.EnterUpdateLock();
						g_AudioEngine.RequestThreadPause(false);

						g_AudioEngine.GetSoundManager().StartUnloadingChunk(m_UnloadingChunkNames[i]);
						u32 initialEngineTimeFrames = g_AudioEngine.GetEngineTimeFrames();
						g_AudioEngine.ExitUpdateLock();				

						// Wait for an update so that any sounds using unloaded metadata automatically stop themselves
						while(initialEngineTimeFrames == g_AudioEngine.GetEngineTimeFrames())
						{
							audDisplayf("Waiting for audio engine update. chunkname=%s", m_UnloadingChunkNames[i]);
							sysIpcSleep(5);
						}

						m_MetadataManager.UnloadMetadataChunk(m_UnloadingChunkNames[i]);
						g_AudioEngine.GetSoundManager().ClearDisabledMetadata();
						
						m_UnloadingChunkNames[i][0] = '\0';
						--m_UnloadingChunksCount;
					}
					else
					{
						continue;
					}
				}
				else if(m_MetadataManager.GetMetadataTypeHash() == ATSTRINGHASH("ModularSynth", 0xDFBAF5D4))
				{
					// need to ensure the mixer isn't running while we unload this data
					synthSynthesizer::UnloadMetadataChunk(m_UnloadingChunkNames[i]);

					m_UnloadingChunkNames[i][0] = '\0';
					--m_UnloadingChunksCount;
				}
				else
				{
					m_MetadataManager.UnloadMetadataChunk(m_UnloadingChunkNames[i]);
				
					m_UnloadingChunkNames[i][0] = '\0';
					--m_UnloadingChunksCount;
				}
			}
		}
	}
}

void audMetadataDataFileMounter::DeriveChunkName(const char *fileName, char *chunkNameBuffer, const size_t chunkNameBufferLen)
{
	const char *chunkNamePtr = strrchr(fileName, '/') + 1;
	const size_t chunkNameLen = strchr(chunkNamePtr, '_') - chunkNamePtr;
	audAssertf(chunkNameLen < chunkNameBufferLen, "Chunk name too long for file %s", fileName);
	safecpy(chunkNameBuffer, chunkNamePtr, Min(chunkNameBufferLen, chunkNameLen + 1));
}


void audNorthAudioEngine::RegisterDataFileMounters()
{
	class audWavePackDataFileMounter : public CDataFileMountInterface
	{
	public:

		virtual bool LoadDataFile(const CDataFileMgr::DataFile &file)
		{
			Displayf("Mounting audio pack %s", file.m_filename);

#if RSG_BANK
			// If running with audiofolder, assume all assets are available at audio:/
			if(PARAM_audiofolder.Get())
			{
				return true;
			}
#endif // RSG_BANK

			char packName[32];
			DerivePackName(file.m_filename, packName, sizeof(packName));
			char prefix[9];
			strncpy(prefix, packName, 8);
			prefix[8] = '\0';

			char searchPath[RAGE_MAX_PATH];
			DeriveSearchPath(file.m_filename, packName, searchPath, sizeof(searchPath));

			audWaveSlot::SetSearchPath(prefix, searchPath);

			if(atStringHash(prefix) == ATSTRINGHASH("dlc_Sum2", 0x7E30C9B))
			{
				audNorthAudioEngine::ApplyARC1BankRemappings();
			}

			return true;
		}

		virtual void UnloadDataFile(const CDataFileMgr::DataFile &file)
		{
			Displayf("Unmounting audio pack %s time %d", file.m_filename, fwTimer::GetTimeInMilliseconds());

			char packName[32];
			DerivePackName(file.m_filename, packName, sizeof(packName));
			char prefix[9];
			memset(prefix, 0, 9);
			strncpy(prefix, packName, 8);
			prefix[8] = '\0';

			audWaveSlot::RemoveSearchPath(prefix);			
		}

	private:
		static void DeriveSearchPath(const char *fileName, const char *packName, char *searchPathBuffer, const size_t searchPathBufferLen)
		{
			safecpy(searchPathBuffer, fileName, searchPathBufferLen);
			const size_t packStart = strlen(searchPathBuffer) - strlen(packName);
			audAssert(packStart <= strlen(searchPathBuffer));
			searchPathBuffer[packStart] = 0;
		}

		static void DerivePackName(const char *fileName, char *chunkNameBuffer, const size_t chunkNameBufferLen)
		{
			const char *chunkNamePtr = strrchr(fileName, '/') + 1;
			const size_t chunkNameLen = strlen(chunkNamePtr);
			audAssertf(chunkNameLen < chunkNameBufferLen, "Pack name too long for file %s", fileName);
			safecpy(chunkNameBuffer, chunkNamePtr, Min(chunkNameBufferLen, chunkNameLen + 1));
		}
	};

	class audSpeechDataFileMounter : public CDataFileMountInterface
	{
	public:
		audSpeechDataFileMounter()
		{

		}

		virtual bool LoadDataFile(const CDataFileMgr::DataFile &file)
		{	
			char chunkName[32];
			DeriveChunkName(file.m_filename, chunkName, sizeof(chunkName));
			audDisplayf("Loading DLC speech audio chunk %s from %s", chunkName, file.m_filename);
			return audSpeechSound::LoadSpeechMetadata(chunkName, file.m_filename);
		}

		virtual void UnloadDataFile(const CDataFileMgr::DataFile &file) 
		{
			char chunkName[32];
			DeriveChunkName(file.m_filename, chunkName, sizeof(chunkName));
			audDisplayf("Unloading DLC speech audio chunk %s from %s", chunkName, file.m_filename);
			audSpeechSound::UnloadSpeechMetadata(chunkName);
		}

	private:

		static void DeriveChunkName(const char *fileName, char *chunkNameBuffer, const size_t chunkNameBufferLen)
		{
			const char *chunkNamePtr = strrchr(fileName, '/') + 1;
			const size_t chunkNameLen = strchr(chunkNamePtr, '_') - chunkNamePtr;
			audAssertf(chunkNameLen < chunkNameBufferLen, "Chunk name too long for file %s", fileName);
			safecpy(chunkNameBuffer, chunkNamePtr, Min(chunkNameBufferLen, chunkNameLen + 1));
		}

	};

	static audMetadataDataFileMounter s_SoundDataMounter(g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager());	
	static audMetadataDataFileMounter s_CurveDataMounter(g_AudioEngine.GetCurveRepository().GetMetadataManager());	
	static audMetadataDataFileMounter s_GameDataMounter(audNorthAudioEngine::GetMetadataManager());		
	static audMetadataDataFileMounter s_DynamicMixerMounter(g_AudioEngine.GetDynamicMixManager().GetMetadataManager());	
	static audMetadataDataFileMounter s_SynthDataMounter(synthSynthesizer::GetMetadataManager());

	static audSpeechDataFileMounter s_SpeechDataMounter;
	static audWavePackDataFileMounter s_WavePackMounter;

	sm_SoundDlcMounter = &s_SoundDataMounter;
	sm_DynamicMixDlcMounter = &s_DynamicMixerMounter;
	sm_GameObjectDlcMounter = &s_GameDataMounter;
	sm_CurveDlcMounter = &s_CurveDataMounter;
	sm_SynthDlcMounter = &s_SynthDataMounter;
	
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_SOUNDDATA, &s_SoundDataMounter, eDFMI_UnloadFirst);
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_CURVEDATA, &s_CurveDataMounter, eDFMI_UnloadFirst);
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_GAMEDATA, &s_GameDataMounter, eDFMI_UnloadFirst);
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_SPEECHDATA, &s_SpeechDataMounter, eDFMI_UnloadFirst);
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_DYNAMIXDATA, &s_DynamicMixerMounter, eDFMI_UnloadFirst);
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_SYNTHDATA, &s_SynthDataMounter, eDFMI_UnloadFirst);
	CDataFileMount::RegisterMountInterface(CDataFileMgr::AUDIO_WAVEPACK, &s_WavePackMounter, eDFMI_UnloadFirst);
}

void audNorthAudioEngine::StartUserUpdateFunction()
{
	//Set the game specific audio thread update
	g_AudioEngine.SetUserGameUpdateFunction(&audNorthAudioEngine::UpdateAudioThread);
}

void audNorthAudioEngine::StopUserUpdateFunction()
{
	//Kill the game update function
	g_AudioEngine.SetUserGameUpdateFunction(NULL);
}

void audNorthAudioEngine::UpdateAudioThread()
{
	AUDCATEGORYCONTROLLERMANAGER.Update();

#if AUD_ATOMIC_CATEGORIES
	// If we re-enable AUD_ATOMIC_CATEGORIES, this seems dodgy.
	g_AudioEngine.GetCategoryManager().CommitCategorySettings();
#endif
}

void audNorthAudioEngine::StartEnvironmentLocalObjectListUpdate()
{
	if(!fwTimer::IsGamePaused())
	{
		sm_Environment.LocalObjectsList_StartAsyncUpdate();
		s_HasScanStarted = true;
	}
}

void audNorthAudioEngine::FinishEnvironmentLocalObjectListUpdate()
{
	if(s_HasScanStarted)
	{
		sm_Environment.LocalObjectsList_EndAsyncUpdate();
		s_HasScanStarted = false;
	}
}

extern bool g_DisableNPCVehicleDSP, g_DisablePlayerVehicleDSP;

XPARAM(headphones);
void audNorthAudioEngine::StartUpdate()
{
#if GTA_REPLAY
	static bool s_HaveInitialisedReplayTracks = false;
	if(CReplayMgr::IsEditorActive() && !s_HaveInitialisedReplayTracks)
	{
		CReplayAudioTrackProvider::InitReplayRadioStations();
		s_HaveInitialisedReplayTracks = true;
	}
#endif // GTA_REPLAY
#if RSG_PC
	if(!PARAM_nominspecaudio.Get())
	{
		static bool forceMinSpec = PARAM_minspecaudio.Get();

		const u32 uMinCores = 4; 
		const float cfMinClockSpeed = 2.6f; 
	
		bool isMinSpec = (sysIpcGetProcessorCount() <= uMinCores) || (sysTimerConsts::CpuSpeed < cfMinClockSpeed) || forceMinSpec;

#if __BANK
		if(g_OverrideCPULimitedAudioSetting)
		{
			isMinSpec = g_ForceMinSpecMode;
		}
#endif

		g_DisableNPCVehicleDSP = isMinSpec;
		audDriver::GetVoiceManager().SetCPULimitedAudio(isMinSpec);
	}

#endif

	sm_Microphones.UpdatePlayerHeadMatrix();

	sm_Environment.ResetHasNaveMeshInfoBeenUpdated();

	bool isAlreadyFadedOut = sm_IsFadedToBlack;
	sm_IsFadedToBlack = camInterface::IsFadedOut();
	if(!isAlreadyFadedOut && sm_IsFadedToBlack)
	{
		sm_ScreenFadedOutThisFrame = true;
	}

	const bool isMixerRunning = audDriver::GetMixer() != NULL;

	PF_PUSH_TIMEBAR("Audio Wait on Thread");
	if(isMixerRunning && !sm_IsShuttingDown)
	{
#if RSG_ORBIS
		audMixerDeviceOrbis *device = ((audMixerDeviceOrbis*)audDriver::GetMixer());
		
		// We need to have a valid user id to associate the pad
		device->SetPadSpeakerUserId(g_rlNp.GetUserServiceId(CControlMgr::GetMainPlayerIndex()));

		// Ensure we have up to date info on the port status
		device->UpdatePortState();
#endif
#if GTA_REPLAY
		if(audDriver::GetMixer() && audDriver::GetMixer()->IsCapturing() && audDriver::GetMixer()->IsFrameRendering())
		{
			g_AudioEngine.TriggerUpdate();
			g_AudioEngine.WaitForAudioFrame();
			audDriver::GetMixer()->EnterReplaySwitchLock();
			audDriver::GetMixer()->TriggerUpdate();
			audDriver::GetMixer()->WaitOnMixBuffers();
			audDriver::GetMixer()->ExitReplaySwitchLock();
		}
#endif
		audDriver::GetMixer()->WaitOnThreadCommandBufferProcessing();
	}
	PF_POP_TIMEBAR();

	if (REPLAY_ONLY(!CReplayMgr::IsPreCachingScene() &&)
		((fwTimer::IsUserPaused() && !fwTimer::GetSingleStepThisFrame()) || 
		fwTimer::IsGamePaused()
		BANK_ONLY(|| g_CutsceneAudioPaused)))
	{
 		Pause();
	}
	else
	{
		UnPause();
	}

	UpdateDataSet();
	
#if __DEV
	// JP: This can cause asserts at startup as it can be called before the viewport is set up
	// Seems to make more sense to just use the focus entity selection stuff.
	// Could probably get rid of g_AudioDebugEntity altogether and just use the focus entity array
	//g_AudioDebugEntity = CDebugScene::GetEntityUnderMouse();
	g_AudioDebugEntity = CDebugScene::FocusEntities_Get(0);
#endif

	// Cache this as its not safe to query on the audio update thread
	sm_IsRenderingHoodMountedVehicleCam = camInterface::GetCinematicDirector().IsRenderingCinematicMountedCamera() && !camInterface::GetCinematicDirector().IsRenderingCinematicPointOfViewCamera();
	sm_IsRenderingFirstPersonVehicleCam = camInterface::IsRenderedCameraInsideVehicle();
	const camBaseCamera* dominantRenderedCamera = camInterface::GetDominantRenderedCamera();

	// These vehicles have turrets that are inside the vehicle, so ensure we keep the regular interior scene active
	if(dominantRenderedCamera)
	{
		if(dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_DUNE3", 0x8501FD84) ||
		   dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_APC_CANNON", 0xD3FE954B) ||
		   dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_APC_MISSILE", 0xD4FCAB99))
		{
			sm_IsRenderingFirstPersonVehicleCam = true;
			sm_IsRenderingFirstPersonVehicleTurretCam = true;
		}
		// Special case for vehicle mounted turret cams - player is visually outside the vehicle when manning these so first person vehicle scene occludes things weirdly
		else if(dominantRenderedCamera &&
			(dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_INSURGENT", 0x3F451CCC) || 
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_INSURGENT3", 0x9722AEFA) ||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_LIMO2", 0x6CCE4543) ||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_BOXVILLE", 0xCB2DF32E) ||	
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_AATRAILER_DUALAA", 0xE52B5A93)	||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_HALFTRACK_TWINMG", 0x546EBC04) ||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_HALFTRACK_QUADMG", 0x5030429E) ||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_AATRAILER_QUADMG", 0xE53AE034) ||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_AATRAILER_MISSILE", 0xE9FA695B) ||
			dominantRenderedCamera->GetNameHash() == ATSTRINGHASH("POV_TURRET_CAMERA_TECHNICAL", 0x33DCCD41)))
		{
			sm_IsRenderingFirstPersonVehicleCam = false;
			sm_IsRenderingFirstPersonVehicleTurretCam = true;
		}
		else
		{
			sm_IsRenderingFirstPersonVehicleTurretCam = false;
		}
	}
	else
	{
		sm_IsRenderingFirstPersonVehicleTurretCam = false;
	}

#if GTA_REPLAY    
    if (CReplayMgr::IsEditorActive() && dominantRenderedCamera && dominantRenderedCamera->GetIsClassId(camReplayFreeCamera::GetStaticClassId()))
    {
        sm_IsRenderingReplayFreeCamera = true;
    }
    else
    {
        sm_IsRenderingReplayFreeCamera = false;
    }

    if(CReplayMgr::IsEditorActive())
    {
        sm_IsRenderingFirstPersonVehicleCam |= (CReplayMgr::IsRenderedCamInVehicle() && CReplayMgr::GetCamInFirstPersonFlag());
    }
#endif

	// World probes to get the water heights, needs to run in the main thread. 
	g_AmbientAudioEntity.UpdateShoreLineWaterHeights();
	BANK_ONLY (g_AmbientAudioEntity.UpdateShoreLineEditor();)

	// Update our occlusion data based on if interiors have streamed in/out
	sm_OcclusionManager.UpdateInteriorMetadata();

	// Update our local environment metrics - this line-scans around the camera to build up a detailed local view of the world
	// Same's true of the static emitter update that needs to grab interior info for non-linked emitters
	if(!fwTimer::IsGamePaused())
	{
		sm_OcclusionManager.Update();
		sm_Environment.UpdateLocalEnvironment();
		g_EmitterAudioEntity.CheckStaticEmittersForInteriorInfo();
	}

	s32 speakerOutputOption = CPauseMenu::GetMenuPreference(PREF_SPEAKER_OUTPUT);

	bool headphones = false;
	audOutputMode outputMode = audDriver::GetHardwareOutputMode();

	static AmbisonicDecoderMode modeArray[AUD_FRONT_SPEAKER_NUM][AUD_REAR_SPEAKER_NUM] =
	{	{kAmbisonicsFwRr, kAmbisonicsFwRm, kAmbisonicsFwRs},
		{kAmbisonicsFmRr, kAmbisonicsFmRm, kAmbisonicsFmRs},
		{kAmbisonicsFnRr, kAmbisonicsFnRm, kAmbisonicsFnRs}
	};

	// On PS4 we can't automatically detect the headset, so if the user selects Headphones and then enables Pulse Headset Profile, force surround.
	
#if RSG_PS3 || RSG_ORBIS
	if(sm_ShouldTriggerPulseHeadset)
	{
		// Sony Wireless Headset
		if(outputMode == AUD_OUTPUT_5_1)
		{
			g_AudioEngine.GetAmbisonics().SetEnvironmentDecoder(kAmbisonicsFmRr);
		}
		else
		{
			g_AudioEngine.GetAmbisonics().SetEnvironmentDecoder(kAmbisonicsHeadphones);
			headphones = true;
		}
	}
	else
#endif // RSG_PS3 || RSG_ORBIS
	{		
		switch(speakerOutputOption)
		{
		case 0: // Speakers
			if(outputMode == AUD_OUTPUT_5_1)
			{
				s32 frontPos = CPauseMenu::GetMenuPreference(PREF_SS_FRONT);
				s32 rearPos = CPauseMenu::GetMenuPreference(PREF_SS_REAR);
				AmbisonicDecoderMode ambiMode = modeArray[frontPos][rearPos];
				
				g_AudioEngine.GetAmbisonics().SetEnvironmentDecoder(ambiMode);
			}
			else
			{
				g_AudioEngine.GetAmbisonics().SetEnvironmentDecoder(kAmbisonicsStereo);
			}
			break;
		case 1: // TV
		case 3: // Stereo Speakers
			outputMode = AUD_OUTPUT_STEREO;
			g_AudioEngine.GetAmbisonics().SetEnvironmentDecoder(kAmbisonicsStereo);
			break;
		case 2: // Headphones
			headphones = true;
			outputMode = AUD_OUTPUT_STEREO;
			g_AudioEngine.GetAmbisonics().SetEnvironmentDecoder(kAmbisonicsHeadphones);
			break;
		
		default:
			naErrorf("Unknown speaker output option");
			CPauseMenu::SetMenuPreference(PREF_SPEAKER_OUTPUT, 0); //Unknown speaker output so set it to speakers
			break;
		}
	}

	g_AudioEngine.GetEnvironment().SetDownmixMode(outputMode);
	g_AudioEngine.GetEnvironment().SetUsingHeadphones(headphones);

	if(!sm_RunUpdateInSeperateThread)
	{
		Update();
	}

	// move on the particle thread buffer index
	if(!fwTimer::IsGamePaused())
	{
		g_AmbientAudioEntity.IncrementAudioEffectWriteIndex();
	}

	//Game updates for audio entities
	NA_RADIO_ENABLED_ONLY(g_RadioAudioEntity.GameUpdate());
	
	g_SpeechManager.UpdateAnimalBanks();
	g_ScriptAudioEntity.UpdateConversation();
	g_GlassAudioEntity.Update();

	audPedAudioEntity::UpdateClass();
	
	audSoundManager &soundManager = g_AudioEngine.GetSoundManager();
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Lighting.CurrentExposure", 0x4FBAC7F6), PostFX::g_adaptation.GetUpdateExposure());
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Lighting.TargetExposure", 0x31FC0865), PostFX::g_adaptation.GetUpdateTargetExposure());
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Lighting.SunVisibility", 0x6E1B558D), CVisualEffects::GetSunVisibility());
	
	CPed *player = CGameWorld::FindLocalPlayer();
	if(player && player->GetSpecialAbility() && player->GetSpecialAbility()->IsFadingOut())
	{
		sm_IsPlayerSpecialAbilityFadingOut = true;
	}
	else
	{
		sm_IsPlayerSpecialAbilityFadingOut = false;
	}

	sm_IsAudioUpdateCurrentlyRunning = true;

	sysIpcSignalSema(sm_RunUpdateSema);
}

void audNorthAudioEngine::FinishUpdate()
{
	PF_FUNC(NorthAudioUpdateTimer_FinishUpdate);
	PF_PUSH_TIMEBAR("Wait on sm_UpdateFinishedSema");
	sysIpcWaitSema(sm_UpdateFinishedSema);
	PF_POP_TIMEBAR();
	sm_IsAudioUpdateCurrentlyRunning = false;

	FinishEnvironmentLocalObjectListUpdate();
	UpdateVehicleCategories();

	audVehicleAudioEntity::ClassFinishUpdate();
	sm_AudioController.PostUpdate();

	sm_OcclusionManager.FinishUpdate();

#if __BANK
	audWaveSlot::DebugDrawSlotSizeErrors();
	naEnvironmentGroup::DebugDrawEnvironmentGroupPool();
	audEnvironmentSound::DebugDrawInvalidCategorySounds();
	g_AmbientAudioEntity.DebugDraw();
	g_CollisionAudioEntity.DrawDebug();
	g_RadioAudioEntity.DebugDrawStations();
	g_InteractiveMusicManager.DebugDraw();
	g_ScriptAudioEntity.DrawDebug();
	sm_SuperConductor.GetVehicleConductor().VehicleConductorDebugInfo();
	audVehicleAudioEntity::UpdateCarRecordingTool();
	if(!PARAM_inGameAudioWorldSectors.Get())
	{
		sm_Environment.GenerateAudioWorldSectorsInfo();
	}

	audCarAudioEntity::UpdateVolumeCaptureUpdateThread();
	audWarpManager::Update();
#endif

	if(!fwTimer::IsGamePaused())
	{
		CONDUCTORS_ONLY(sm_SuperConductor.ProcessUpdate());
		g_WeaponAudioEntity.ProcessCachedWeaponFireEvents();
#if USE_GUN_TAILS
		sm_GunFireAudioEntity.Update(fwTimer::GetTimeInMilliseconds());
#endif
#if __DEV
		g_WeaponAudioEntity.DebugDraw();
		naEnvironment::DebugDraw();  
#endif
		g_CollisionAudioEntity.TriggerDeferedImpacts();
		if( !Water::IsCameraUnderwater())
		{
			sm_Environment.UpdateInterestingObjects();
		}
	}

	naAudioEntity::ProcessDeferredSounds();
	audEntity::ProcessBatchedSoundRequests(0); // 0 timestep for the second update this frame
	audEntity::ResetDeletedObjectLists();

	sm_DynamicMixer.FinishUpdate();
	DYNAMICMIXMGR.PostUpdateProcess();
	g_InteractiveMusicManager.PostUpdate();
	audExplosionAudioEntity::Update();
	
	const bool isMixerRunning = audDriver::GetMixer() != NULL;
	if(isMixerRunning)
		audDriver::GetMixer()->FlagThreadCommandBufferReadyToProcess();

	sm_ScreenFadedOutThisFrame = false;

	if(sm_DynamicMixDlcMounter)
	{
		sm_DynamicMixDlcMounter->Update();
	}
	if(sm_SoundDlcMounter)
	{
		sm_SoundDlcMounter->Update();
	}
	if(sm_GameObjectDlcMounter)
	{
		sm_GameObjectDlcMounter->Update();
	}
	if(sm_CurveDlcMounter)
	{
		sm_CurveDlcMounter->Update();
	}
	if(sm_SynthDlcMounter)
	{
		sm_SynthDlcMounter->Update();
	}

	sm_CinematicThirdPersonAimCameraActive = false;
	const camThirdPersonAimCamera* thirdPersonAimCamera	= camInterface::GetGameplayDirector().GetThirdPersonAimCamera();	

	if (thirdPersonAimCamera && thirdPersonAimCamera->GetIsClassId(camThirdPersonPedAssistedAimCamera::GetStaticClassId()))
	{
		const camThirdPersonPedAssistedAimCamera* assistedAimCamera = static_cast<const camThirdPersonPedAssistedAimCamera*>(thirdPersonAimCamera);
		sm_CinematicThirdPersonAimCameraActive = assistedAimCamera->GetCinematicMomentBlendLevel() >= sm_ThirdPersonCameraBlendThreshold;
	}

#if __BANK
	if(g_WarpToStaticEmitter && g_RequestedWarpEmitter)
	{		
		Vector3 warpPos(g_RequestedWarpEmitter->Position.x, g_RequestedWarpEmitter->Position.y, g_RequestedWarpEmitter->Position.z);
		Vector3 zero(0.f,0.f,0.f);
		CWarpManager::SetWarp(warpPos, zero, 0.f, true, true);
		g_RequestedWarpEmitter = NULL;
	}
	if(g_WarpToAmbientZone && g_RequestedWarpAmbZone)
	{		
		Vector3 warpPos(g_RequestedWarpAmbZone->ActivationZone.Centre);
		Vector3 zero(0.f,0.f,0.f);
		CWarpManager::SetWarp(warpPos, zero, 0.f, true, true);
		g_RequestedWarpAmbZone = NULL;
	}
	if(g_WarpToInterior && g_RequestedWarpInterior)
	{		
		Vec3V warpPos = Vec3V(V_ZERO);
		g_RequestedWarpInterior->GetPosition(warpPos);
		Vector3 zero(0.f,0.f,0.f);
		CWarpManager::SetWarp(VEC3V_TO_VECTOR3(warpPos), zero, 0.f, true, true);
		g_RequestedWarpInterior = NULL;
	}
#endif
}

void audNorthAudioEngine::ApplyARC1BankRemappings()
{
	if (sm_HasAppliedARC1BankRemappings)
	{
		return;
	}

	// See url:bugstar:6176865 - Replay : Can we compress the replay editor banks so that they only use one slot, instead of four?
	SOUNDFACTORY.AddBankRemapping(ATSTRINGHASH("DLC_REPLAYED/REPLAY_01", 0x4534DBDD), ATSTRINGHASH("DLC_SUM20/Replay_SFX", 0x977D1731));
	SOUNDFACTORY.AddBankRemapping(ATSTRINGHASH("DLC_REPLAYED/REPLAY_02", 0x4A57E623), ATSTRINGHASH("DLC_SUM20/Replay_SFX", 0x977D1731));
	SOUNDFACTORY.AddBankRemapping(ATSTRINGHASH("DLC_REPLAYED/REPLAY_03", 0x63801873), ATSTRINGHASH("DLC_SUM20/Replay_SFX", 0x977D1731));
	SOUNDFACTORY.AddBankRemapping(ATSTRINGHASH("DLC_REPLAYED/REPLAY_04", 0x69BD24ED), ATSTRINGHASH("DLC_SUM20/Replay_SFX", 0x977D1731));

	sm_HasAppliedARC1BankRemappings = true;
}

void audNorthAudioEngine::UpdateDataSet()
{
	const s32 framesToWait = 5;
#if GTA_REPLAY
	LoadReplayAudioBanks();
#endif
	
	if(sm_IsMPDataRequested || g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::LoadMPData))
	{
		switch(sm_DataSetState)
		{
		case Loaded_SP:
			UnloadSPData();
			sm_DataSetStateCount = 0;			
			sm_DataSetState = Stopping_SP;
			BANK_ONLY(g_AudioEngine.GetRemoteControl().SetOutgoingMessagesEnabled(false);)
			break;
		case Stopping_SP:
			sm_DataSetStateCount++;
			if(sm_DataSetStateCount > framesToWait)
			{			
				LoadMPData();
				sm_DataSetState = Loaded_MP;
				BANK_ONLY(g_AudioEngine.GetRemoteControl().SetOutgoingMessagesEnabled(true);)
			}
			break;
		case Stopping_MP:
			sm_DataSetStateCount++;
			if(sm_DataSetStateCount > framesToWait)
			{
				LoadMPData();
				sm_DataSetState = Loaded_MP;
				BANK_ONLY(g_AudioEngine.GetRemoteControl().SetOutgoingMessagesEnabled(true);)
			}
			break;
		case Loaded_MP:
			break;
		}
	}
	else
	{
		switch(sm_DataSetState)
		{
		case Loaded_SP:
			break;
		case Stopping_SP:
			sm_DataSetStateCount++;
			if(sm_DataSetStateCount > framesToWait)
			{
				LoadSPData();
				sm_DataSetState = Loaded_SP;
				BANK_ONLY(g_AudioEngine.GetRemoteControl().SetOutgoingMessagesEnabled(true);)
			}
			break;
		case Stopping_MP:
			sm_DataSetStateCount++;
			if(sm_DataSetStateCount > framesToWait)
			{
				LoadSPData();
				sm_DataSetState = Loaded_SP;
				BANK_ONLY(g_AudioEngine.GetRemoteControl().SetOutgoingMessagesEnabled(true);)
			}
			break;
		case Loaded_MP:
			BANK_ONLY(g_AudioEngine.GetRemoteControl().SetOutgoingMessagesEnabled(false);)
			UnloadMPData();
			sm_DataSetStateCount = 0;
			sm_DataSetState = Stopping_MP;
			break;

		}
	}
}

void audNorthAudioEngine::LoadMPData()
{
	audDisplayf("Loading multiplayer metadata");

	// If we enter here in the Stopping_MP state then the data is already loaded
	if(sm_DataSetState != Stopping_MP)
	{
		// Unload SP data (preserve buffer)
		g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().UnloadMetadataChunk("SinglePlayer", false);

		
		g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().LoadMetadataChunk("GTAOnline", 
																							"audio:/config/dlc_gtao_sounds.dat",
																							sm_SPSoundDataPtr,
																							sm_SPSoundDataSize);

		GetMetadataManager().LoadMetadataChunk("GTAOnline", "audio:/config/dlc_gtao_game.dat");
		g_AudioEngine.GetDynamicMixManager().GetMetadataManager().LoadMetadataChunk("GTAOnline", "audio:/config/dlc_gtao_mix.dat");
	
		if(g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().FindChunkId("GTAOnline") == -1)
		{
			Quitf(ERR_AUD_GTA_ONLINE_LOAD,"Failed to load GTAOnline audio data");
		}

		audDisplayf("Loaded GTAOnline audio assets, source CLs: sounds %u, game objects: %u, dynamic mixer: %u", 
											g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetAssetChangelist(-1),
											sm_MetadataMgr.GetAssetChangelist(-1),
											g_AudioEngine.GetDynamicMixManager().GetMetadataManager().GetAssetChangelist(-1));
	

		g_AudioEngine.GetSoundManager().ClearDisabledMetadata();
		
	
		g_AudioEngine.GetDynamicMixManager().InitMixgroupsForChunk(g_AudioEngine.GetDynamicMixManager().GetMetadataManager().FindChunkId("GTAOnline"));

		g_EmitterAudioEntity.ReInitStaticEmitters();
		g_AmbientAudioEntity.CheckAndAddZones();
		audPedAudioEntity::InitMPSpecificSounds();

		// See url:bugstar:5745340 - GTAO - Consolidate 321_GO and MP_WASTED banks into GTAO Snacks
		SOUNDFACTORY.AddBankRemapping(ATSTRINGHASH("SCRIPT/HUD_321_GO", 0x1F90E64), ATSTRINGHASH("DLC_GTAO/SNACKS", 0x81CAD0C));
		SOUNDFACTORY.AddBankRemapping(ATSTRINGHASH("SCRIPT/MP_WASTED", 0x26ED9800), ATSTRINGHASH("DLC_GTAO/SNACKS", 0x81CAD0C));

#if RSG_PC
		// Need to manually point the GTA Online pack at the loose files in the titleupdate folder, otherwise the file system
		// gets confused and tries to read from the original .rpf file that we shipped with. See B*2460076 etc.
		if(!PARAM_audiofolder.Get())
		{
			audWaveSlot::SetSearchPath("DLC_GTAO", "update:/x64/audio/sfx/");
		}
#endif
		g_FrontendAudioEntity.SetMPSpecialAbilityBankId();	// this grabs the bank id for the GTAO/MP_RESIDENT bank
	}
	else
	{
		g_AudioEngine.GetSoundManager().CancelUnloadingChunk("GTAOnline");
		g_AudioEngine.GetDynamicMixManager().CancelUnloadingChunk("GTAOnline");
		g_AudioEngine.GetSoundManager().ClearDisabledMetadata();

		const s32 chunkId = sm_MetadataMgr.FindChunkId("GTAOnline");
		if(naVerifyf(chunkId != -1, "Failed to find loaded gameobject chunk GTAOnline"))
		{
			sm_MetadataMgr.SetChunkEnabled(chunkId, true);
		}

		g_EmitterAudioEntity.ReInitStaticEmitters();
		g_AmbientAudioEntity.CheckAndAddZones();
	}

	audEnvironmentSound::SetMixGroupPitchFrequencyScalingEnabled(true);
}

void audNorthAudioEngine::LoadSPData()
{	
	audDisplayf("Loading singleplayer metadata");

	// Unload multiplayer data if we have any loaded (preserve buffer for sounds)
	g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().UnloadMetadataChunk("GTAOnline",false);
	
	GetMetadataManager().UnloadMetadataChunk("GTAOnline");
	g_AudioEngine.GetDynamicMixManager().GetMetadataManager().UnloadMetadataChunk("GTAOnline");

	g_AudioEngine.GetSoundManager().ClearDisabledMetadata();

	// load SP data
	g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().LoadMetadataChunk("SinglePlayer", 
																						"audio:/config/sp_sounds.dat",
																						sm_SPSoundDataPtr,
																						sm_SPSoundDataSize);

		
	g_EmitterAudioEntity.ReInitStaticEmitters();
	g_AmbientAudioEntity.CheckAndAddZones();

	g_FrontendAudioEntity.SetSPSpecialAbilityBankId();	

	SOUNDFACTORY.RemoveBankRemapping(ATSTRINGHASH("SCRIPT/HUD_321_GO", 0x1F90E64));
	SOUNDFACTORY.RemoveBankRemapping(ATSTRINGHASH("SCRIPT/MP_WASTED", 0x26ED9800));
}

void audNorthAudioEngine::UnloadMPData()
{
	naDisplayf("Unloading MP Data time %u, frame %u", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
	// Ensure no score is playing (it can hold onto Mood object references)
#if GTA_REPLAY
		bool isReplayTransition = (CReplayMgr::IsEditModeActive() && CReplayCoordinator::IsExportingToVideoFile() && CVideoEditorPlayback::IsLoading());
#else
		bool isReplayTransition = false;
#endif
	if (!isReplayTransition)
		g_InteractiveMusicManager.Reset();

	g_AudioEngine.GetSoundManager().StartUnloadingChunk("GTAOnline");
	sm_DynamicMixer.StopOnlineScenes();
	g_AudioEngine.GetDynamicMixManager().StartUnloadingChunk("GTAOnline");

	const s32 chunkId = sm_MetadataMgr.FindChunkId("GTAOnline");
	if(chunkId != -1)
	{
		const audMetadataChunk &chunk = sm_MetadataMgr.GetChunk(chunkId);
		RegisterGameObjectMetadataUnloading(chunk);
		sm_MetadataMgr.SetChunkEnabled(chunkId, false);
	}

	g_EmitterAudioEntity.ReInitStaticEmitters();
	g_AmbientAudioEntity.CheckAndAddZones();

#if RSG_PC
	audWaveSlot::RemoveSearchPath("DLC_GTAO");
#endif

	audEnvironmentSound::SetMixGroupPitchFrequencyScalingEnabled(false);
}

void audNorthAudioEngine::UnloadSPData()
{
	naDisplayf("Unloading SP Data time %u, frame %u", fwTimer::GetTimeInMilliseconds(), fwTimer::GetFrameCount());
	g_AudioEngine.GetSoundManager().StartUnloadingChunk("SinglePlayer");
}

void audNorthAudioEngine::LoadMapData()
{
#if RSG_BANK
	if(!PARAM_audiodesigner.Get())
	{
		return;
	}

	const char *pLevelName = audNorthAudioEngine::GetCurrentAudioLevelName(); 
	audDisplayf("Current audio level: %s", pLevelName);

	size_t len = strlen(pLevelName);
	
	// ignore "gta5_"
	if(len > 5)
	{
		const char *suffix = pLevelName + 5;
		char fileName[RAGE_MAX_PATH];
		char chunkName[RAGE_MAX_PATH];

		formatf(chunkName, "dlc%s", suffix);

		// Sounds
		g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().LoadMetadataChunk(chunkName, formatf(fileName, "audio:/config/dlc%s_sounds.dat", suffix));
		// Curves
		g_AudioEngine.GetCurveRepository().GetMetadataManager().LoadMetadataChunk(chunkName, formatf(fileName, "audio:/config/dlc%s_curves.dat", suffix));
		// DynamicMixer
		g_AudioEngine.GetDynamicMixManager().GetMetadataManager().LoadMetadataChunk(chunkName, formatf(fileName, "audio:/config/dlc%s_mix.dat", suffix));
		// Synth
		synthSynthesizer::GetMetadataManager().LoadMetadataChunk(chunkName, formatf(fileName, "audio:/config/dlc%s_amp.dat", suffix));
		// Game Objects
		sm_MetadataMgr.LoadMetadataChunk(chunkName, formatf(fileName, "audio:/config/dlc%s_game.dat", suffix));
	}
#endif // RSG_BANK
}

void audNorthAudioEngine::UnloadMapData()
{
#if RSG_BANK
	if(!PARAM_audiodesigner.Get())
	{
		return;
	}
	const char *pLevelName = audNorthAudioEngine::GetCurrentAudioLevelName(); 

	size_t len = strlen(pLevelName);

	// ignore "gta5_"
	if(len > 5)
	{
		const char *suffix = pLevelName + 5;		
		char chunkName[RAGE_MAX_PATH];
		formatf(chunkName, "dlc%s", suffix);

		// Sounds
		g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().UnloadMetadataChunk(chunkName);
		// Curves
		g_AudioEngine.GetCurveRepository().GetMetadataManager().UnloadMetadataChunk(chunkName);
		// DynamicMixer
		g_AudioEngine.GetDynamicMixManager().GetMetadataManager().UnloadMetadataChunk(chunkName);
		// Synth
		synthSynthesizer::GetMetadataManager().UnloadMetadataChunk(chunkName);
		// Game Objects
		sm_MetadataMgr.UnloadMetadataChunk(chunkName);
	}
#endif // RSG_BANK
}

void audNorthAudioEngine::RegisterGameObjectMetadataUnloading(const audMetadataChunk& chunk)
{
	// Clear out all CMloModelInfo's that are referencing any InteriorSettings/InteriorRoom GO we're about to unload
	CInteriorProxy::Pool* pool = CInteriorProxy::GetPool();
	if(pool)
	{
		s32 numSlots = pool->GetSize();
		for(s32 slot = 0; slot < numSlots; slot++)
		{
			CInteriorProxy* intProxy = pool->GetSlot(slot);
			if(intProxy)
			{
				CInteriorInst* intInst = intProxy->GetInteriorInst();
				if(intInst && intInst->IsPopulated())
				{
					CMloModelInfo *modelInfo = intInst->GetMloModelInfo();
					if(naVerifyf(modelInfo, "NULL CMLOModelInfo"))
					{
						const s32 numRooms = intInst->GetNumRooms();

						// Start at roomIdx 1 as roomdIdx 0 is always limbo(outside) which we won't have settings for.
						for(s32 roomIdx = 1; roomIdx < numRooms; roomIdx++)
						{
							const InteriorSettings* intSettings;
							const InteriorRoom* intRoom;
							modelInfo->GetAudioSettings(roomIdx, intSettings, intRoom);
							if((intSettings && chunk.IsObjectInChunk(intSettings))
								|| (intRoom && chunk.IsObjectInChunk(intRoom)))
							{
								modelInfo->SetAudioSettings(roomIdx, NULL, NULL);
							}
						}
					}
				}
			}
		}
	}

	// Also clear out any references to those InteriorSettings/InteriorRoom GO's in the environmentGroups
	audNorthAudioEngine::GetAudioController()->RegisterGameObjectMetadataUnloading(chunk);
}

#if GTA_REPLAY
void audNorthAudioEngine::LoadReplayAudioBanks()
{
	BANK_ONLY(bool forceLoad = sm_ForceSuperSlowMoVideoEditor || sm_ForceSlowMoVideoEditor);

	if((CReplayMgr::IsEditModeActive() BANK_ONLY(||forceLoad)) && !sm_AreReplayBanksLoaded)
	{
		sm_AreReplayBanksLoaded = g_ScriptAudioEntity.ReplayLoadScriptBank(ATSTRINGHASH("DLC_SUM20/Replay_SFX", 0x977D1731), 10); 
	}
}

bool audNorthAudioEngine::PumpReplayAudio()
{
	bool addedAudio = false;
	if(audDriver::GetMixer() && audDriver::GetMixer()->IsCapturing() && audDriver::GetMixer()->IsFrameRendering())
	{
		const float kfFrameStep = 1.0f / 31.0f;
		// Only trigger an extra engine update when exporting at 30fps or less to make sure enough audio is generated this frame
		if(audDriver::GetMixer()->GetTotalAudioTimeNs() < audDriver::GetMixer()->GetTotalVideoTimeNs() && CReplayMgr::GetExportFrameStep() >= kfFrameStep)
		{
			g_AudioEngine.TriggerUpdate();
			g_AudioEngine.WaitForAudioFrame();
		}

		while(audDriver::GetMixer()->GetTotalAudioTimeNs() < audDriver::GetMixer()->GetTotalVideoTimeNs())
		{
			audDriver::GetMixer()->EnterReplaySwitchLock();
			audDriver::GetMixer()->TriggerUpdate();
			audDriver::GetMixer()->WaitOnMixBuffers();
			audDriver::GetMixer()->ExitReplaySwitchLock();
			//Displayf("Catch Up Audio: %LLu chasing (%LLu)", 
			//	NANOSECONDS_TO_ONE_H_NS_UNITS(audDriver::GetMixer()->GetTotalAudioTimeNs()), 
			//	NANOSECONDS_TO_ONE_H_NS_UNITS(audDriver::GetMixer()->GetTotalVideoTimeNs()));	
			addedAudio = true;
		}

	}
	return addedAudio;
}
#endif

void audNorthAudioEngine::AudioUpdateThread(void*)
{
	CSystem::SetThisThreadId(SYS_THREAD_AUDIO);
	const bool isMixerRunning = audDriver::GetMixer() != NULL;
	if(isMixerRunning)
	{
		const u32 commandBufferSize = 8192 WIN32PC_ONLY( *COMMAND_BUFFER_SIZE_MUTIPLIER );
		audDriver::GetMixer()->InitClientThread("NorthAudioUpdateThread", commandBufferSize);
	}
	while(true)
	{
		TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"Wait on sm_RunUpdateSema");
		sysIpcWaitSema(sm_RunUpdateSema);
		TELEMETRY_END_ZONE(__FILE__,__LINE__);
		PF_START(NorthAudioUpdateTimer);

		if(sm_IsShuttingDown)
		{
			return;
		}
		if(sm_RunUpdateInSeperateThread)
		{
			u32 timestamp = 0;

			if(isMixerRunning)
			{
				PF_START(NorthAudioUpdateTimer_CommandBuffer);
				TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"WaitOnThreadCommandBufferProcessing");
#if GTA_REPLAY
				if(audDriver::GetMixer() && audDriver::GetMixer()->IsCapturing() && audDriver::GetMixer()->IsFrameRendering())
				{
					audDriver::GetMixer()->TriggerUpdate();
					audDriver::GetMixer()->WaitOnMixBuffers();
				}
#endif
				audDriver::GetMixer()->WaitOnThreadCommandBufferProcessing();
				timestamp = audDriver::GetMixer()->GetMixerTimeFrames();
				TELEMETRY_END_ZONE(__FILE__,__LINE__);
				PF_STOP(NorthAudioUpdateTimer_CommandBuffer);
			}

#if COMMERCE_CONTAINER
			if (CLiveManager::GetCommerceMgr().ContainerIsStoreMode())
			{
				MinimalUpdate();
			}
			else
#endif
			{
				TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"audNorthAudioEngine::Update()");
				PF_START(NorthAudioUpdateTimer_Update);
				Update();
				PF_STOP(NorthAudioUpdateTimer_Update);
				TELEMETRY_END_ZONE(__FILE__,__LINE__);
			}
			
			if(isMixerRunning)
			{
				audDriver::GetMixer()->FlagThreadCommandBufferReadyToProcess(timestamp);
			}
		}
		sysIpcSignalSema(sm_UpdateFinishedSema);
		PF_STOP(NorthAudioUpdateTimer);
	}
}

void audNorthAudioEngine::MinimalUpdate()
{
	const bool isMixerRunning = audDriver::GetMixer() != NULL;
	if(isMixerRunning)
	{
		audDriver::GetMixer()->WaitOnThreadCommandBufferProcessing();
	}
	naAudioEntity::ProcessDeferredSounds();
	audEntity::ProcessBatchedSoundRequests(static_cast<s32>(fwTimer::GetTimeStepInMilliseconds()));
	audEntity::ResetDeletedObjectLists();

	const bool shouldUpdateEnvironmentGroups = sm_AudioController.sm_ShouldUpdateEnvironmentGroups;
	sm_AudioController.sm_ShouldUpdateEnvironmentGroups = false;
	sm_AudioController.Update(sm_TimeInMs);
	sm_AudioController.sm_ShouldUpdateEnvironmentGroups = shouldUpdateEnvironmentGroups;

	//Update the dynamic mixer
	sm_DynamicMixer.Update();
	AUDCATEGORYCONTROLLERMANAGER.Update();
	g_AudioEngine.CommitGameSettings(sm_TimeInMs);

	if(isMixerRunning)
	{
		audDriver::GetMixer()->FlagThreadCommandBufferReadyToProcess();
	}
}

void audNorthAudioEngine::ActivateSlowMoMode(u32 settingsHash)
{
	SlowMoSettings * settings = GetObject<SlowMoSettings>(settingsHash);
	if(settings)
	{
		if(naVerifyf(!sm_ActiveSlowMoModes[settings->Priority], "Trying to set SlowMoType %d, but it is already set; deactivate it first", settings->Priority))
		{
			if(settings->Priority > sm_SlowMoMode)
			{
				if(sm_IsInSlowMo)
				{
					if(sm_SlowMoScene)
					{
						sm_SlowMoScene->Stop();
					}
					sm_DynamicMixer.StartScene(settings->Scene, &sm_SlowMoScene);

					if(sm_SlowMoSound)
					{
						sm_SlowMoSound->StopAndForget();
					}
					g_FrontendAudioEntity.CreateAndPlaySound_Persistent(settings->SlowMoSound, &sm_SlowMoSound);
				}	
				sm_SlowMoMode = (SlowMoType)settings->Priority;
			}
			sm_ActiveSlowMoModes[settings->Priority] = settingsHash;
		}
	}
}

void audNorthAudioEngine::DeactivateSlowMoMode(u32 settingsHash)
{
	SlowMoSettings * settings = GetObject<SlowMoSettings>(settingsHash);
	if(settings)
	{
		if(sm_SlowMoMode == settings->Priority)
		{
			SlowMoType priorityType = AUD_SLOWMO_GENERAL;
			for(int i=1; i<sm_SlowMoMode; i++)
			{
				if(sm_ActiveSlowMoModes[i])
				{
					priorityType = (SlowMoType)i;
				}
			}
			if(sm_SlowMoScene)
			{
				sm_SlowMoScene->Stop();
				SlowMoSettings * newSettings = GetObject<SlowMoSettings>(sm_ActiveSlowMoModes[priorityType]);
				if(newSettings)
				{
					sm_DynamicMixer.StartScene(newSettings->Scene, &sm_SlowMoScene);
				}
			}
			if(sm_SlowMoSound)
			{
				sm_SlowMoSound->StopAndForget();				
				g_FrontendAudioEntity.CreateAndPlaySound_Persistent(settings->SlowMoSound, &sm_SlowMoSound);
			}
			sm_SlowMoMode = priorityType;
		}
		sm_ActiveSlowMoModes[settings->Priority] = 0;
	}
}

void audNorthAudioEngine::DeactivateSlowMoMode(SlowMoType priority)
{
	if(priority == AUD_SLOWMO_GENERAL)
	{
		return; //can't deactivate slowmo general
	}
	if(sm_SlowMoMode == priority)
	{
		SlowMoType priorityType = AUD_SLOWMO_GENERAL;
		for(int i=1; i<sm_SlowMoMode; i++)
		{
			if(sm_ActiveSlowMoModes[i])
			{
				priorityType = (SlowMoType)i;
			}
		}

		SlowMoSettings * newSettings = GetObject<SlowMoSettings>(sm_ActiveSlowMoModes[priorityType]);
		if(sm_SlowMoScene)
		{
			sm_SlowMoScene->Stop();			
			if(newSettings)
			{
				sm_DynamicMixer.StartScene(newSettings->Scene, &sm_SlowMoScene);
			}
		}

		if(sm_SlowMoSound)
		{
			sm_SlowMoSound->StopAndForget();					
			g_FrontendAudioEntity.CreateAndPlaySound_Persistent(newSettings->SlowMoSound, &sm_SlowMoSound);
		}
		sm_SlowMoMode = priorityType;
	}
	sm_ActiveSlowMoModes[priority] = 0;
}

bool audNorthAudioEngine::IsAFirstPersonCameraActive(const CPed* ped, const bool bCheckStrafe, const bool bIncludeClone, const bool bDisableForDominantScriptedCams, const bool bDisableForDominantCutsceneCams, const bool bCheckFlags)
{
	if(!ped)
	{
		return false;
	}

	bool isFirstPerson = FPS_MODE_SUPPORTED_ONLY(ped ? ped->IsFirstPersonShooterModeEnabledForPlayer(bCheckStrafe, bIncludeClone, bDisableForDominantScriptedCams, bDisableForDominantCutsceneCams, bCheckFlags) :) false;

#if GTA_REPLAY
	if(CReplayMgr::IsPlaying())
	{
		return camInterface::IsDominantRenderedCameraAnyFirstPersonCamera();
	}
#endif

	return isFirstPerson;
}

bool audNorthAudioEngine::IsSuperSlowVideoEditor()
{
#if GTA_REPLAY
	if(CReplayMgr::IsEditModeActive())
	{
		if(CReplayMgr::IsUserPaused() && CReplayMgr::GetCursorSpeed() > 0.f && CReplayMgr::GetCursorSpeed() <= 0.06f)
		{
			return true;
		}

		if(!CReplayMgr::IsUserPaused() && CReplayMgr::GetMarkerSpeed() <= 0.06f && CReplayMgr::GetCursorSpeed() == 1.f)
		{
			return true;
		}
	}
#if __BANK
	if(sm_ForceSuperSlowMoVideoEditor)
	{
		return true;
	}
#endif
#endif
	return false;
}

#if GTA_REPLAY
void audNorthAudioEngine::ShutdownReplayEditor()
{
	g_ScriptAudioEntity.CleanUpScriptBankSlotsAfterReplay();
}
#endif

bool audNorthAudioEngine::IsInCinematicSlowMo()
{
	if(sm_IsInSlowMo && sm_CinematicThirdPersonAimCameraActive)
	{
		return true;
	}
	return false;
}

bool audNorthAudioEngine::IsInVideoEditor()
{
#if GTA_REPLAY
	if(CReplayMgr::IsEditModeActive())
	{
		return true;
	}
#endif
	return false;
}

bool audNorthAudioEngine::IsInSlowMoVideoEditor()
{
#if GTA_REPLAY
	if(CReplayMgr::IsEditModeActive())
	{
		if(CReplayMgr::IsUserPaused() && CReplayMgr::GetCursorSpeed() > 0.f && CReplayMgr::GetCursorSpeed() < 1.f)
		{
			return true;
		}

		if(!CReplayMgr::IsUserPaused() && CReplayMgr::GetMarkerSpeed() < 1.f && CReplayMgr::GetCursorSpeed() == 1.f)
		{
			return true;
		}
	}
#endif

#if __BANK
	if(sm_ForceSlowMoVideoEditor)
	{
		return true;
	}
#endif
	
	if(sm_IsInSlowMo && sm_CinematicThirdPersonAimCameraActive)
	{
		return true;
	}

	return false;
}

SlowMoType audNorthAudioEngine::GetActiveSlowMoMode()
{
	return sm_SlowMoMode;
}

void audNorthAudioEngine::Update()
{
	USE_MEMBUCKET(MEMBUCKET_AUDIO);

#if RSG_PC && __BANK
	audMixerDeviceXAudio2::SetNumberOfAudioMixBuffers(g_NumberOfMixBuffers); 
#endif

#if __DEV
	PF_SET(MaxVoicesPerBucket, g_MaxVoicesUsed);
	PF_SET(MaxSoundsPerBucket, g_MaxSoundSlotsUsed);
	PF_SET(MaxRequestedSettingsPerBucket, g_MaxRequestedSettingsSlotsUsed);
#endif
#if __WIN32
	sysPerformanceTimer timer("audNorthAudioEngine");
	timer.Start();
#endif // __WIN32

	PIXBegin(0, "NorthAudioUpdate");

	// Do anything specific if we've just been paused.
	sm_PausedLastFrame = sm_Paused;

	// If we're paused, don't update the time we pass into camera and controller updates
	if (!sm_Paused)
	{
		sm_LastTimeInMs = sm_TimeInMs;
		sm_TimeInMs = fwTimer::GetTimeInMilliseconds();
	}

	sm_IsInSlowMo = fwTimer::GetTimeWarpActive() < 1.f REPLAY_ONLY(|| (CReplayMgr::IsEditModeActive() && IsInSlowMoVideoEditor()) || sm_ForceSlowMoVideoEditor || sm_ForceSuperSlowMoVideoEditor);
	bool isInSpecialAbilitySlowmo = (fwTimer::GetTimeWarpActive() < 1.f && !sm_IsPlayerSpecialAbilityFadingOut) REPLAY_ONLY(|| (CReplayMgr::IsEditModeActive() && IsInSlowMoVideoEditor()));

	if(!sm_IsInSlowMo && sm_WaitingForPauseMenuSlowMoToEnd)
	{
		sm_WaitingForPauseMenuSlowMoToEnd = false;
	}

	// Set up the microphones
	sm_Microphones.SetUpMicrophones();

#if RSG_PS3 || RSG_ORBIS
	ORBIS_ONLY(sm_IsWirelessHeadsetConnected = CPauseMenu::GetMenuPreference(PREF_SPEAKER_OUTPUT) == 2 && CPauseMenu::GetMenuPreference(PREF_PULSE_HEADSET) == TRUE);

	if(sm_IsWirelessHeadsetConnected)
	{
		if(!sm_ShouldTriggerPulseHeadset)
		{
			sm_ShouldTriggerPulseHeadset = true;
			
			g_SFXHPFCutoff = g_PulseHPFCutoff;
			g_SFXHPFBypass = false;

			if(!sm_PulseHeadsetScene)
			{
				DYNAMICMIXER.StartScene("PULSE_HEADSET_SCENE", &sm_PulseHeadsetScene);
			}
		}
	}
	else if(sm_PulseHeadsetScene)
	{
		sm_ShouldTriggerPulseHeadset = false;
		g_SFXHPFBypass = true;
		sm_PulseHeadsetScene->Stop();
		sm_PulseHeadsetScene = NULL;
	}
#endif

	if(g_AudioEngine.GetEnvironment().GetSpecialEffectMode() == kSpecialEffectModeStoned)
	{
		if(!sm_StonedScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("SPECIAL_EFFECT_MODE_STONED_SCENE", 0x8D342873), &sm_StonedScene);
		}
	}
	else
	{
		if(sm_StonedScene)
		{
			sm_StonedScene->Stop();
			sm_StonedScene = NULL;
		}
	}

	CPed * player = CGameWorld::FindLocalPlayer();
	if(player)
	{
		if(player->GetSpecialAbility())
		{
			bool specialAbilityActive = player->GetSpecialAbility()->IsActive();
#if GTA_REPLAY
			if(CReplayMgr::IsEditModeActive())
			{
				specialAbilityActive = false;
			}
#endif

			if(specialAbilityActive)
			{
				sm_LastTimeSpecialAbilityActive = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
			}

			//Displayf("specialAbilityActive %d", specialAbilityActive);

			if(specialAbilityActive && !sm_ActiveSlowMoModes[AUD_SLOWMO_SPECIAL])
			{
				if(CNetwork::IsGameInProgress()) // currently there is no differentiation between local player slowmo and remote player slowmo
				{
					ActivateSlowMoMode(ATSTRINGHASH("GTAO_BT_ACTIVATING_PLAYER", 0x3F4DC076));
					//ActivateSlowMoMode(ATSTRINGHASH("GTAO_SLOWMO_SPECIAL_BYSTANDER", 0xEBCBB786));

				}
				else
				{
					switch(player->GetPedAudioEntity()->GetPedType())
					{
					case PEDTYPE_PLAYER_0: //MICHAEL
						ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_SPECIAL_MICHAEL", 0xAAADDCBC));
						break;
					case PEDTYPE_PLAYER_1: //FRANKLIN
						ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_SPECIAL_FRANKLIN", 0x579EE10D));
						break;
					case PEDTYPE_PLAYER_2: //TREVOR
						ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_SPECIAL_TREVOR", 0xF798196B));
						break;
					default:
						break;
					}
				}
			}
			else if(!specialAbilityActive)
			{
				g_FrontendAudioEntity.StopSpecialAbility();
				if(sm_ActiveSlowMoModes[AUD_SLOWMO_SPECIAL])
				{
					DeactivateSlowMoMode(AUD_SLOWMO_SPECIAL);
				}
			}
		}

		bool weaponWheelActive = false;

		if(CNetwork::IsGameInProgress())
		{
			const CPed *playerPed = CGameWorld::FindLocalPlayer();
			const bool isUsingVehicleWeaponControls = playerPed && (playerPed->GetIsInVehicle() || playerPed->GetIsParachuting() || playerPed->GetPedResetFlag(CPED_RESET_FLAG_IsUsingJetpack));
			weaponWheelActive = CNewHud::IsWeaponWheelVisible() && !isUsingVehicleWeaponControls;
		}
		else
		{
			 weaponWheelActive = CNewHud::IsWeaponWheelActive() && CNewHud::ShouldWheelBlockCamera();
		}

		if(weaponWheelActive)
		{
			if(!sm_ActiveSlowMoModes[AUD_SLOWMO_WEAPON])
			{
				ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_WEAPON", 0xF1D29082));
			}
		}
		else if(!weaponWheelActive && sm_ActiveSlowMoModes[AUD_SLOWMO_WEAPON])
		{
			DeactivateSlowMoMode(AUD_SLOWMO_WEAPON);
		}
		if(CStuntJumpManager::IsAStuntjumpInProgress())
		{
			if(!sm_ActiveSlowMoModes[AUD_SLOWMO_STUNT])
			{
				ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_STUNT", 0xC7CA5E0C));
			}
		}
		if(!CStuntJumpManager::IsAStuntjumpInProgress() && sm_ActiveSlowMoModes[AUD_SLOWMO_STUNT])
		{
			DeactivateSlowMoMode(AUD_SLOWMO_STUNT);
		}		

		if(sm_CinematicThirdPersonAimCameraActive && sm_IsInSlowMo)
		{
			if(!sm_ActiveSlowMoModes[AUD_SLOWMO_THIRD_PERSON_CINEMATIC_AIM])
			{
				ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_THIRD_PERSON_CINEMATIC_AIM", 0xAEDE85AD));
			}			
		}
		else
		{
			DeactivateSlowMoMode(AUD_SLOWMO_THIRD_PERSON_CINEMATIC_AIM);
		}
		
		if(camInterface::GetCinematicDirector().IsSlowMoActive())
		{
			if(!sm_ActiveSlowMoModes[AUD_SLOWMO_CINEMATIC])
			{
				ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_CINEMATIC", 0x929AEE2A));
			}			
		}
		else if(!camInterface::GetCinematicDirector().IsSlowMoActive() && sm_ActiveSlowMoModes[AUD_SLOWMO_CINEMATIC])
		{
			DeactivateSlowMoMode(AUD_SLOWMO_CINEMATIC);
		}

		if(g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::ActivateSwitchWheelAudio) && !sm_ActiveSlowMoModes[AUD_SLOWMO_SWITCH])
		{
			ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_SWITCH", 0xC33BCB9F));
		}
		else if(!g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::ActivateSwitchWheelAudio) && sm_ActiveSlowMoModes[AUD_SLOWMO_SWITCH])
		{
			DeactivateSlowMoMode(AUD_SLOWMO_SWITCH);
		}

		if(sm_SlowMoMode == AUD_SLOWMO_SPECIAL && !sm_ActiveSlowMoModes[AUD_SLOWMO_STUNT])
		{
			sm_IsInSlowMo = isInSpecialAbilitySlowmo;
		}

		if(sm_IsInSlowMo && sm_SlowMoMode == AUD_SLOWMO_SPECIAL REPLAY_ONLY( && !CReplayMgr::IsEditorActive()))
		{
			if(NetworkInterface::IsGameInProgress()) // currently there is no differentiation between local player slowmo and remote player slowmo
			{
				g_FrontendAudioEntity.StartSpecialAbility(ATSTRINGHASH("Multiplayer", 0xDDA1F78));				
			}
			else
			{
				switch(player->GetPedAudioEntity()->GetPedType())
				{
				case PEDTYPE_PLAYER_0: //MICHAEL
					g_FrontendAudioEntity.StartSpecialAbility(ATSTRINGHASH("Michael", 0x55932F38));				
					break;
				case PEDTYPE_PLAYER_1: //FRANKLIN
					g_FrontendAudioEntity.StartSpecialAbility(ATSTRINGHASH("Franklin", 0x44C24694));				
					break;
				case PEDTYPE_PLAYER_2: //TREVOR
					g_FrontendAudioEntity.StartSpecialAbility(ATSTRINGHASH("Trevor", 0x2737D5AC));				
					break;
				default:
					break;
				}
			}
		}
		else
		{
			g_FrontendAudioEntity.StopSpecialAbility();
		}
		if(sm_IsInSlowMo && sm_SlowMoMode == AUD_SLOWMO_SWITCH)
		{
			g_FrontendAudioEntity.StartSwitch();
		}
		else
		{
			g_FrontendAudioEntity.StopSwitch();
		}
		if(weaponWheelActive)
		{
			g_FrontendAudioEntity.StartWeaponWheel();
		}
		else
		{
			g_FrontendAudioEntity.StopWeaponWheel();
		}
		if(sm_IsInSlowMo && sm_SlowMoMode == AUD_SLOWMO_STUNT)
		{
			g_FrontendAudioEntity.StartStunt();
		}
		else
		{
			g_FrontendAudioEntity.StopStunt();
		}
		if(sm_IsInSlowMo && sm_SlowMoMode == AUD_SLOWMO_CINEMATIC)
		{
			g_FrontendAudioEntity.StartCinematic();
		}
		else
		{
			g_FrontendAudioEntity.StopCinematic();
		}
		if(sm_IsInSlowMo && sm_SlowMoMode == AUD_SLOWMO_GENERAL)
		{
			g_FrontendAudioEntity.StartGeneralSlowMo();
		}
		else
		{
			g_FrontendAudioEntity.StopGeneralSlowMo();
		}
	}

	// note: using the audio timer, since fwTimer will be warped by the time warp
	const f32 timeWarp = sm_TimeWarpSmoother.CalculateValue(fwTimer::GetTimeWarpActive(), g_AudioEngine.GetTimeInMilliseconds());
	f32 timeScale = sm_TimeWarpToTimeScale.CalculateValue(timeWarp);

	f32 slowmoTimesWarp = timeWarp;
	f32 slowmoTimesScale = timeScale;
#if GTA_REPLAY
	if(CReplayMgr::IsEditModeActive() BANK_ONLY(|| sm_ForceSlowMoVideoEditor || sm_ForceSuperSlowMoVideoEditor) )
	{

#if __BANK
		if(sm_ForceSlowMoVideoEditor)
		{
			timeScale = 0.2f;
		}
		if(sm_ForceSuperSlowMoVideoEditor)
		{
			timeScale = 0.05f;
		}
		if(!sm_ForceSlowMoVideoEditor && !sm_ForceSuperSlowMoVideoEditor) // if we're not forcing just use the timescale from the marker otherwise use whatever was set
		{
			timeScale = CReplayMgr::GetMarkerSpeed();
		}
#else
		timeScale = CReplayMgr::GetMarkerSpeed();
#endif

		f32 replayTimeScale = timeScale;
		if(timeScale < 1.0f)
		{
			if(sm_ReplayTimeWarpToTimeScale.IsValid())
			{
				replayTimeScale = sm_ReplayTimeWarpToTimeScale.CalculateValue(timeScale);
			}
			else
			{
				static const audThreePointPiecewiseLinearCurve replayTimeScaleCurve(0.05f, 0.25f, 0.475f, 0.55f, 1.0f, 1.0f);
				replayTimeScale = replayTimeScaleCurve.CalculateValue(timeScale);
			}
		}
		g_GameWorldCategory->SetFrequencyRatio(replayTimeScale);
		slowmoTimesScale = replayTimeScale;
		slowmoTimesWarp = replayTimeScale;
	}
	else
	{
		g_GameWorldCategory->SetFrequencyRatio(1.0f);
	}
#endif

	// lets only slow down pausable stuff
	g_AudioEngine.GetSoundManager().SetTimeScale(0, sm_SlowMoMode == AUD_SLOWMO_PAUSEMENU ? 1.f : slowmoTimesScale);

	if(sm_IsInSlowMo)
	{
		SlowMoSettings * slowMo = GetObject<SlowMoSettings>(sm_ActiveSlowMoModes[sm_SlowMoMode]);

		if(slowMo)
		{
			if(!sm_SlowMoScene)
			{						
				sm_DynamicMixer.StartScene(slowMo->Scene, &sm_SlowMoScene);
			}

			if(!sm_SlowMoSound)
			{				
				g_FrontendAudioEntity.CreateAndPlaySound_Persistent(slowMo->SlowMoSound, &sm_SlowMoSound);
			}
		}

		if(sm_SlowMoScene)
		{
			sm_SlowMoScene->SetVariableValue(ATSTRINGHASH("pitchApply", 0x20E8D2B3), slowmoTimesWarp);
		}

		if(sm_SlowMoSound)
		{
			sm_SlowMoSound->FindAndSetVariableValue(ATSTRINGHASH("timeWarp", 0xBC6E3F8F), timeWarp);
		}
	}
	else 
	{
		if(sm_SlowMoScene)
		{
			sm_SlowMoScene->Stop();
		}

		if(sm_SlowMoSound)
		{
			sm_SlowMoSound->StopAndForget();
		}
	}

	if(IsInSlowMoVideoEditor() || IsSuperSlowVideoEditor())
	{
		if(IsSuperSlowVideoEditor())
		{
			if(!sm_SuperSlowMoVideoEditorScene)
			{
				sm_DynamicMixer.StartScene(ATSTRINGHASH("SLO_MO_VIDEO_EDITOR_05", 0x56A19675), &sm_SuperSlowMoVideoEditorScene);		
			}
			if(sm_SlowMoVideoEditorScene)
			{
				sm_SlowMoVideoEditorScene->Stop();
			}
		}
		else 
		{
			if(!sm_SlowMoVideoEditorScene)
			{
				sm_DynamicMixer.StartScene(ATSTRINGHASH("SLO_MO_VIDEO_EDITOR", 0xA828B595), &sm_SlowMoVideoEditorScene);		
			}
			if(sm_SuperSlowMoVideoEditorScene)
			{
				sm_SuperSlowMoVideoEditorScene->Stop();
			}
		}		
	}
	else 
	{
		if(sm_SlowMoVideoEditorScene)
		{
			sm_SlowMoVideoEditorScene->Stop();
		}
		if(sm_SuperSlowMoVideoEditorScene)
		{
			sm_SuperSlowMoVideoEditorScene->Stop();
		}
	}

	if(sm_SlowMoMode == AUD_SLOWMO_RADIOWHEEL && sm_SlowMoScene)
	{
		float radioSlowMoFade = 0.0f;
		float radioOffFade = 1.f;
		if(g_RadioAudioEntity.GetPlayerRadioStation())
		{
			radioSlowMoFade = 1.f;
			radioOffFade = 0.f;
		}
		sm_SlowMoScene->SetVariableValue(ATSTRINGHASH("onApply", 0x6BDBAC89), radioSlowMoFade);
		sm_SlowMoScene->SetVariableValue(ATSTRINGHASH("offApply", 0xA7790120), radioOffFade);	
	}

	sysPerformanceTimer controllerTimer("audController");
	controllerTimer.Start();
	sm_AudioController.PreUpdate(sm_TimeInMs);
	controllerTimer.Stop();

	audEntity::ProcessBatchedSoundRequests(static_cast<s32>(fwTimer::GetTimeStepInMilliseconds()));

	if(!sm_Paused)
	{
		audVehicleAudioEntity::UpdateActivationRanges();
		audTrainAudioEntity::UpdateActivationRanges();
	}

	audSoundManager &soundManager = g_AudioEngine.GetSoundManager();

	// supply sounds with the game time
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.DayOfWeek", 0x357A17E4), static_cast<f32>(CClock::GetDayOfWeek()));
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.Hours", 0x98263E3E), static_cast<f32>(CClock::GetHour()));
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.Minutes", 0xF1150725), static_cast<f32>(CClock::GetMinute()));
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.Seconds", 0x70D769DA), static_cast<f32>(CClock::GetSecond()));
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.Days", 0x147D8EB9), static_cast<f32>(CClock::GetDay()));
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.Months", 0x67F650AC), static_cast<f32>(CClock::GetMonth()));
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.SecsPerMin", 0x266005A7), static_cast<f32>(CClock::GetMsPerGameMinute()/1000.f));

	// whole hours, decimal mins+secs
	sm_GameTimeHours = static_cast<float>(CClock::GetHour()) + (static_cast<float>(CClock::GetMinute()) / 60.f) + (static_cast<float>(CClock::GetSecond()) / 3600.f);
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.DecimalHours", 0x59664070), sm_GameTimeHours);
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Clock.TimeWarp", 0xDFA2FAF7), sm_TimeWarpSmoother.GetLastValue());

	CPed *playerPed = FindPlayerPed();
	if(playerPed)
	{
		const Vec3V playerPos = playerPed->GetTransform().GetPosition();
		soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.Position.x", 0xA39016E6), playerPos.GetXf());
		soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.Position.y", 0x91297215), playerPos.GetYf());
		soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.Position.z", 0x7EE44D8B), playerPos.GetZf());
		soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.Speed", 0x8CE50515), VEC3V_TO_VECTOR3(sm_PedPosLastFrame).Dist(VEC3V_TO_VECTOR3(playerPos))/fwTimer::GetTimeStep());
		soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.Health", 0x5CF478F), playerPed->GetHealth());
		soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.MaxHealth", 0x23B3E65D), playerPed->GetMaxHealth());
		sm_PedPosLastFrame = playerPos;
	}

	const Vec3V panningPos = g_AudioEngine.GetEnvironment().GetPanningListenerPosition();
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Microphone.Position.x", 0xE67CF42F), panningPos.GetXf());
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Microphone.Position.y", 0xA3C06EB7), panningPos.GetYf());
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Microphone.Position.z", 0x85E73305), panningPos.GetZf());

	soundManager.SetVariableValue(ATSTRINGHASH("Game.Water.CameraDepth", 0xD38CC888), Water::GetCameraWaterDepth());

	float firstPerson = sm_Microphones.IsFirstPerson() ? 1.f : 0.f;
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Microphone.FirstPerson", 0x378A1D92),firstPerson);

	float slowMoVideoEditor = IsInSlowMoVideoEditor() ? 1.f : 0.f;
	soundManager.SetVariableValue(ATSTRINGHASH("Game.VideoEditor.Slowmo", 0x9FA46F1D), slowMoVideoEditor);

	float isVideoEditor = IsInVideoEditor() ? 1.f : 0.f;
	soundManager.SetVariableValue(ATSTRINGHASH("Game.VideoEditor.Replay", 0x5EC0D39A), isVideoEditor);

	f32 interiorRatio = 0.0f;
	sm_Environment.AreWeInAnInterior(NULL, &interiorRatio);
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Player.InteriorRatio", 0x8BE286CD), interiorRatio);

	float isCopsAndCrooks = NetworkInterface::IsInCopsAndCrooks() ? 1.f : 0.f;
	soundManager.SetVariableValue(ATSTRINGHASH("Game.Mode.IsCopsAndCrooks", 0xB315260A), isCopsAndCrooks);

	audDoorAudioEntity::UpdateClass();
	audVehicleAudioEntity::UpdateClass();
	audCarAudioEntity::UpdateClass();
	audSpeechAudioEntity::UpdateClass();

	g_FireSoundManager.Update();
	g_SpeechManager.Update();
	g_ReflectionsAudioEntity.Update();

	if(!fwTimer::IsGamePaused())
	{
		g_EmitterAudioEntity.Update();
		NA_POLICESCANNER_ENABLED_ONLY(g_AudioScannerManager.Update());
		g_AmbientAudioEntity.Update(g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
	}

	g_PedScenarioManager.Update();
	audStreamSlot::UpdateSlots(sm_TimeInMs);

#if __BANK
	UpdateAuditionSound();
	UpdateRAVEVariables();
#endif

	// calculate updated environment metrics
	sm_Environment.Update();

	// The environment update relies on the state of the microphone ( frozen/unfrozen ) so just in case it was frozen, lets unfreeze it after the environment update.
	sm_Microphones.UnFreezeMicrophone();

	// Needs to be called after sm_Environment.Update() which gives us all the interior info we need
	sm_OcclusionManager.UpdateOcclusionMetrics();	

	//Update the dynamic mixer
	sm_DynamicMixer.Update();

	controllerTimer.Start();
	sm_AudioController.Update(sm_TimeInMs);
	controllerTimer.Stop();

	//Update length is the time taken to perform update.
	PF_SET(ControllerUpdateLength, (f32)controllerTimer.GetTimeMS());

	UpdateCategories();

	g_AudioEngine.CommitGameSettings(sm_TimeInMs);
	
	// This call can take ~800uS so we don't want to call it every audio frame
	// We don't have to call it every game frame either if we can think of something else to run in its place
	audDriver::GetVoiceManager().UpdateUserMusicState();

#if __WIN32
	timer.Stop();
	//Update length is the time taken to perform update.
	PF_SET(UpdateLength, (f32)timer.GetTimeMS());
#endif // __WIN32

	PIXEnd();
}

void audNorthAudioEngine::MuteGameWorldAndPositionedRadioForTv(bool mute)
{
	if (mute)
	{
		if(!sm_TvScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("TV_SCENE", 2327741164), &sm_TvScene);
		}
	}
	else
	{
		if(sm_TvScene)
		{
			sm_TvScene->Stop();
		}
	}
}

void audNorthAudioEngine::UpdateVehicleCategories()
{
	CVehicle* playerVehicle = NULL; 
	CPed* playerPed = NULL;

	if(!playerVehicle)
	{
		playerVehicle = audPedAudioEntity::GetBJVehicle();
	}

	if(NetworkInterface::IsInSpectatorMode())
	{
		playerVehicle = CGameWorld::FindFollowPlayerVehicle();
		playerPed = CGameWorld::FindFollowPlayer();
	}
	else
	{
		playerVehicle = CGameWorld::FindLocalPlayerVehicle();
		playerPed = CGameWorld::FindLocalPlayer();
	}

	bool isPlayerHangingOffVehicle = false;

	if(playerVehicle && playerVehicle->GetSeatManager())
	{
		s32 pedSeatIndex = playerVehicle->GetSeatManager()->GetPedsSeatIndex(playerPed);

		if(playerVehicle->IsSeatIndexValid(pedSeatIndex))
		{			
			const CVehicleSeatAnimInfo* seatAnimInfo = playerVehicle->GetSeatAnimationInfo(pedSeatIndex);

			if(seatAnimInfo && seatAnimInfo->GetKeepCollisionOnWhenInVehicle())
			{
				isPlayerHangingOffVehicle = true;
			}			
		}
	}

	if (playerVehicle && playerVehicle->GetVehicleAudioEntity()->GetAudioVehicleType() != AUD_VEHICLE_BICYCLE && !isPlayerHangingOffVehicle)
	{
		const float smoothedOpenness = sm_PIVOpennessSmoother.CalculateValue(1.f - playerVehicle->GetVehicleAudioEntity()->GetOpenness(false), fwTimer::GetTimeStep());

		if(!sm_PlayerInVehScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("PLAYER_IN_VEHICLE_SCENE", 0x201E71F0),&sm_PlayerInVehScene);
		}
		if (naVerifyf(sm_PlayerInVehScene, "Failed to create player in vehicle scene, this shouldn't happen"))
		{
			sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("openness", 0x2721BC39), smoothedOpenness);
			sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("musicVolume", 0x499CB684), sm_PIVMusicVolumeSmoother.GetLastValue());
			sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("radioVolume", 0x3C12FAC8), sm_PIVRadioVolumeSmoother.GetLastValue());
			if (sm_Microphones.IsVehBonnetMic())
			{
				// engine boost for rear engine vehicles
				Vector3 enginePos;
				enginePos = VEC3V_TO_VECTOR3(playerVehicle->TransformIntoWorldSpace(playerVehicle->GetVehicleAudioEntity()->GetEngineOffsetPos()));
				// Check if the engine is behind the vehicle
				Vector3 directionToEngine = enginePos - VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetPosition());
				f32 frontDot = directionToEngine.Dot(VEC3V_TO_VECTOR3(playerVehicle->GetTransform().GetB()));
				if (frontDot < 0)
				{
					sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("rearEngineApply", 0x6EB37A69), 1.f);
				}
				else
				{
					sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("rearEngineApply", 0x6EB37A69), 0.f);
				}
			}
			else
			{
				sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("rearEngineApply", 0x6EB37A69), 0.f);
			}
		}
	}
	else
	{
		if(sm_PlayerInVehScene)
		{
			sm_PlayerInVehScene->Stop();
		}

		sm_PIVOpennessSmoother.CalculateValue(0.f, fwTimer::GetTimeStep());
	}

	// offset radio volume by the current player vehicle engine volume.
	sm_EngineVolume = 0.0f;
	sm_PlayerVehicleOpenness = 1.0f;

	if (playerVehicle)
	{
		sm_EngineVolume = playerVehicle->GetVehicleAudioEntity()->GetFrontendRadioVolumeOffset();
		sm_PlayerVehicleOpenness = playerVehicle->GetVehicleAudioEntity()->GetOpenness(false);
	}
}

void audNorthAudioEngine::UpdateCategories()
{
	naAssertf(sm_NorthAudioMixScene, "No north audio mix scene present, need to create a valid scene in Init()");

	if(!sm_NorthAudioMixScene)
	{
		return;
	}

#if RSG_DURANGO && 0
	if(audDriver::GetMixer())
	{
		if(audDriver::GetMixer()->IsSuspended())
		{
			if(!sm_GameSuspendedScene)
			{
				DYNAMICMIXER.StartScene(ATSTRINGHASH("GAME_SUSPENDED_SCENE", 0x37EC776), &sm_GameSuspendedScene);
			}
		}
		else
		{
			if(sm_GameSuspendedScene)
			{
				sm_GameSuspendedScene->Stop();
			}
		}
	}
#endif

#if !__BANK && __WIN32PC
	bool muteForLostFocus = false;
	bool isCapturing = audDriver::GetMixer() && audDriver::GetMixer()->IsCapturing();
	if(!isCapturing && GRCDEVICE.GetLostFocusForAudio() && CPauseMenu::GetMenuPreference(PREF_AUDIO_MUTE_ON_FOCUS_LOSS))
	{
		muteForLostFocus = true;
	}
#else
	const bool muteForLostFocus = false;
#endif
	if (sm_ShouldMuteAudio || muteForLostFocus)
	{
		if(!sm_MuteAllScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("MUTE_ALL_SCENE", 1043566406U), &sm_MuteAllScene);
		}
		else
		{
			sm_MuteAllScene->SetVariableValue(ATSTRINGHASH("basevol", 0xD10CB74A), 1.0f);
		}
	}
	else
	{
		if(sm_MuteAllScene)
		{
			sm_MuteAllScene->Stop();
		}
	}

	// Update Categories
	if (g_BaseCategory)
	{
		// We scale the overall rolloff according to the environment. Makes sense to set it here, so all category changes are done in one place.
		// Similarly, we scale down occlusion in certain camera modes (cine-cam), so do that here too.
		naAssertf(audNorthAudioEngine::GetGtaEnvironment(), "In UpdateCategories, could not get the environment; will access a null ptr");
		g_BaseCategory->SetDistanceRolloffScale(1.0f);//audNorthAudioEngine::GetGtaEnvironment()->GetRollOffFactor());
		audCategory* baseCategory = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("BASE", 0x44E21C90));
		if (baseCategory)
		{
			baseCategory->SetOcclusionDamping(audNorthAudioEngine::GetOcclusionManager()->GetGlobalOcclusionDampingFactor());
		}
	}

	// screen fade stuff only mutes the game world - not frontend or radio
	// fades don't seem to be totally fading out, so we make it silent at a small level
	f32 fadeLevel = camInterface::GetFadeLevel();

	fadeLevel = ClampRange(fadeLevel, 0.0f, g_FadeValueForSilence);
	fadeLevel /= g_FadeValueForSilence;
	
#if GTA_REPLAY
	if(CReplayMgr::IsEditorActive())
	{		
		if(!CReplayCoordinator::IsExportingToVideoFile())
		{
			if(!CReplayMgr::IsEditModeActive() || CVideoEditorPlayback::IsLoading())
			{			
				fadeLevel = 1.f;
			}
		}
	}
	else 
#endif
	if(sm_StartingNewGame || fwTimer::IsUserPaused() || fwTimer::IsGamePaused() BANK_ONLY(|| g_CutsceneAudioPaused))
	{
		BANK_ONLY(if(!fwTimer::IsDebugPause()))
			fadeLevel = 1.f;
	}

	float musicApply = 0.f;

#if GTA_REPLAY
	if(CReplayMgr::IsEditorActive())
	{
		if(!CReplayCoordinator::IsExportingToVideoFile() && !g_InteractiveMusicManager.IsReplayPreviewPlaying())
		{
			musicApply = fadeLevel;
		}		
	}
	else 
#endif
	if((sm_StartingNewGame || CPauseMenu::IsActive()) && sm_DataSetState != Loaded_MP REPLAY_ONLY(&& !g_InteractiveMusicManager.IsReplayPreviewPlaying() && !CReplayCoordinator::IsExportingToVideoFile()))
	{
		musicApply = fadeLevel;
	}

	sm_PIVMusicVolumeSmoother.SetRates(0.001f / g_PIVMusicVolumeFadeInS, 0.001f / g_PIVMusicVolumeFadeOutS);
	sm_PIVRadioVolumeSmoother.SetRates(0.001f / g_PIVMusicVolumeFadeInS, 0.001f / g_PIVMusicVolumeFadeOutS);
	sm_PIVOpennessSmoother.SetRates(0.001f / g_PIVOpennessFadeInS, 0.001f / g_PIVOpennessFadeOutS);

	float isFEMusicPlaying = 0.f;
	float isFERadioPlaying = 0.f;
	if(g_InteractiveMusicManager.IsMusicPlaying())
	{
		isFEMusicPlaying = 1.f;
	}
	if(g_RadioAudioEntity.IsVehicleRadioOn())
	{
		isFEMusicPlaying = 1.f;
		isFERadioPlaying = 1.f;
	}

	isFEMusicPlaying *= Min(1.f, sm_MusicVolume / Max(SMALL_FLOAT, sm_SfxVolume));
	sm_PIVMusicVolumeSmoother.CalculateValue(isFEMusicPlaying, fwTimer::GetTimeStep());

	isFERadioPlaying *= Min(1.f, sm_MusicVolume / Max(SMALL_FLOAT, sm_SfxVolume));
	sm_PIVRadioVolumeSmoother.CalculateValue(isFERadioPlaying, fwTimer::GetTimeStep());

	f32 radioVol = 1.f;
	// when in frontend mode and not paused we don't want to use the screen fade, otherwise we do
	// unless we're in the network lobby
	// when playing end credits fade is controlled by script
	if((!g_ScriptAudioEntity.IsFlagSet(audScriptAudioFlags::AllowRadioOverScreenFade) BANK_ONLY(&& !g_ForceAllowRadioOverScreenFade)) && 
			!audRadioStation::IsPlayingEndCredits() && !(g_RadioAudioEntity.IsInFrontendMode() && !g_AudioEngine.GetSoundManager().IsPaused(2)))
	{
		radioVol = (1.f-fadeLevel) * (!NetworkInterface::IsGameInProgress()&&fwTimer::IsGamePaused()?0.0f:1.0f);
	}

	if(!audDynamicMixer::FrontendSceneIsOverridden())
	{
		if (fadeLevel > 0.f)
		{
			if(!sm_FrontendScene)
			{
				DYNAMICMIXER.StartScene(ATSTRINGHASH("FRONTEND_SCENE", 1182742998U), &sm_FrontendScene);
				naAssertf(sm_FrontendScene, "Couldn't create audio frontend scene");
			}
			if(sm_FrontendScene)
			{
				sm_FrontendScene->SetVariableValue(ATSTRINGHASH("fade", 1855728566U), fadeLevel);
				sm_FrontendScene->SetVariableValue(ATSTRINGHASH("musicApply", 0x72758950), musicApply);
				if(sm_DataSetState == Loaded_MP)
				{
					sm_FrontendScene->SetVariableValue(ATSTRINGHASH("posRadioVol", 0x4BE8FA9A), 1.f - radioVol);
				}
			}
		}
		else
		{
			if(sm_FrontendScene)
			{
				sm_FrontendScene->Stop();
			}
		}
	}

	if (!sm_OverrideLobbyMute && sm_IsInNetworkLobby)
	{
		if(!sm_MpLobbyScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("MP_LOBBY_SCENE", 1883148293U), &sm_MpLobbyScene);
		}
	}
	else
	{
		if(sm_MpLobbyScene)
		{
			sm_MpLobbyScene->Stop();
		}
	}

#if GTA_REPLAY
	// Keep this on permanently when exporting - don't want to risk it flicking on/off around the clip transition points
	if(CReplayMgr::IsEditModeActive() || CReplayCoordinator::IsExportingToVideoFile())
	{
		if(!sm_ReplayEditorScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("REPLAY_EDITOR_PLAYBACK_SCENE", 0x6B579349), &sm_ReplayEditorScene);
		}		
	}
	else
	{
		if(sm_ReplayEditorScene)
		{
			sm_ReplayEditorScene->Stop();
		}
	}
#endif

	bool bFirstPerson = IsAFirstPersonCameraActive(CGameWorld::FindLocalPlayer(), false, false, true, true) || audNorthAudioEngine::IsRenderingFirstPersonTurretCam();
	sm_IsFirstPersonActiveForPlayer = bFirstPerson;
	sm_IsCutsceneActive = (gVpMan.AreWidescreenBordersActive() || ((CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsStreamedCutScene()) && CGameWorld::GetMainPlayerInfo()->AreControlsDisabled()));
	bool isSniping = audNorthAudioEngine::GetMicrophones().IsSniping();
	bool isSwitchingPlayer = g_PlayerSwitch.IsActive();
	if(bFirstPerson && !sm_IsCutsceneActive && !isSniping && !isSwitchingPlayer && !audVehicleAudioEntity::IsVehicleInteriorSceneActive() && !audVehicleAudioEntity::IsVehicleFirstPersonTurretSceneActive() && !audVehicleAudioEntity::IsBonnetCamSceneActive())
	{
		if(!sm_FirstPersonModeScene)
		{
			DYNAMICMIXER.StartScene(ATSTRINGHASH("FIRST_PERSON_MODE_SCENE", 0xAB0410F1), &sm_FirstPersonModeScene);
		}
	}
	else
	{
		if(sm_FirstPersonModeScene)
		{
			sm_FirstPersonModeScene->Stop();
		}
	}

#if NA_RADIO_ENABLED
	sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("radioVol", 0xe7e3432c), 1.f-radioVol);
#endif

	// Update the radio/music volume - primarily to duck it for a variety of reasons
	f32 musicVolLin = 0.f;
	const f32 frontendLinearScaling = 1.f/10.f;

	// Director mode loads MP data, but we want it to use the SP music slider (unless we end up with a MP director mode at some point in the future!)
	if(CPauseMenu::IsMP() || (sm_DataSetState == Loaded_MP && (!CTheScripts::GetIsInDirectorMode() || NetworkInterface::IsGameInProgress())))
	{
		// duck the radio quicker in network games
		sm_RadioDuckingSmoother.SetRates(g_RadioDuckingSmoothRate*1.67f, g_RadioDuckingSmoothRate*1.67f);
		musicVolLin = ((f32)CPauseMenu::GetMenuPreference(PREF_MUSIC_VOLUME_IN_MP)) * frontendLinearScaling;
	}
	else
	{
		sm_RadioDuckingSmoother.SetRates(g_RadioDuckingSmoothRateUp, g_RadioDuckingSmoothRate);
		musicVolLin = ((f32)CPauseMenu::GetMenuPreference(PREF_MUSIC_VOLUME)) * frontendLinearScaling;
	}

	// Don't apply the music slider during the initial game load
	if(!sm_ApplyMusicSlider)
	{
		musicVolLin = 1.f;
	}

	float sfxSliderVolume = 0.0f;
	
	if(sm_DataSetState == Loaded_MP)
	{
#if RSG_PC
		if(NetworkInterface::GetVoice().IsAnyRemoteGamerTalking())
		{
			f32 sfxAudioSliderVolume = ((f32)CPauseMenu::GetMenuPreference(PREF_SFX_VOLUME)) * frontendLinearScaling;
			f32 voiceChatSoundSliderVolume = ((f32)CPauseMenu::GetMenuPreference(PREF_VOICE_SOUND_VOLUME)) * frontendLinearScaling;
			sfxSliderVolume = sfxAudioSliderVolume * voiceChatSoundSliderVolume;

			//f32 sfxVolDb = audDriverUtil::ComputeDbVolumeFromLinear(sfxAudioSliderVolume);
			//f32 voiceVolDb = audDriverUtil::ComputeDbVolumeFromLinear(voiceChatSoundSliderVolume);
			//f32 totalVolDb = sfxVolDb + voiceVolDb;
			//Displayf("sfx %f(%f) - voice %f(%f) - total %f(%f)(%f)", sfxAudioSliderVolume, sfxVolDb, voiceChatSoundSliderVolume, voiceVolDb, sfxSliderVolume, audDriverUtil::ComputeLinearVolumeFromDb(totalVolDb), totalVolDb);
		}
		else
#endif // RSG_PC
		{
			sfxSliderVolume = ((f32)CPauseMenu::GetMenuPreference(PREF_SFX_VOLUME)) * frontendLinearScaling;
		}
	}
	else
	{
		sfxSliderVolume = ((f32)CPauseMenu::GetMenuPreference(PREF_SFX_VOLUME)) * frontendLinearScaling;
	}

#if __BANK
	if(g_IgnoreFrontendVolumeSliders)
	{
		// Only override if not muted
		if(sfxSliderVolume != 0.f)
			sfxSliderVolume = 1.f;
		if(musicVolLin != 0.f)
			musicVolLin = 1.f;
	}
#endif

#if GTA_REPLAY
	if(CReplayMgr::IsEditModeActive() && (CReplayCoordinator::IsExportingToVideoFile() || CReplayMgr::IsPlaying()))
	{
		// In replay, the user uses replay markers to control music/volume so ignore
		// the frontend ones. The exception is when the user has manually paused the replay and
		// is doing stuff with the menus - in this case, use the same menu sfx volume as the main game
		if(CReplayCoordinator::IsExportingToVideoFile())
		{			
			sfxSliderVolume = 1.f;
		}		
		else if(CReplayMgr::IsPlaying() && !CReplayMgr::IsScrubbing() && !CReplayMgr::IsJumping() && !CReplayMgr::IsUserPaused() && !CReplayMgr::GetNextPlayBackState().IsSet(REPLAY_STATE_PAUSE))
		{		
			sfxSliderVolume = 1.f;
		}

		musicVolLin = 1.f;
	}	
	else if(CReplayMgr::IsEditorActive() && VideoPlayback::IsPlaybackActive(false))
	{
		// Unmute audio when watching an already-recorded video
		sfxSliderVolume = 1.f;
	}
	else if(g_RadioAudioEntity.IsReplayMusicPreviewPlaying())
	{
		musicVolLin = 1.f;
	}
#endif

#if RSG_ORBIS
	// Scale pad speaker volume from 0 to 2, map bottom 50% to pad speaker volume, anything above starts sending to SFX
	float controlPadSpeakerVol = ((f32)CPauseMenu::GetMenuPreference(PREF_CTRL_SPEAKER_VOL)) * frontendLinearScaling * 2.f;
	// Don't bother scaling to silence, and tweak the curve to make the most out of the six volume steps we have available
	const float s_PadExp = 1.4f;
	const float padSpeakerRescaledVol = powf((0.2f + Clamp(controlPadSpeakerVol, 0.f, 1.f)) * 0.8333333f, s_PadExp);
	g_AudioEngine.GetEnvironment().SetPadSpeakerVolume(sfxSliderVolume * padSpeakerRescaledVol);
	g_AudioEngine.GetEnvironment().SetPadSpeakerSFXVolume(sfxSliderVolume * Clamp(controlPadSpeakerVol - 1.f, 0.f, 1.f));
	
	audMixerDeviceOrbis *device = ((audMixerDeviceOrbis*)audDriver::GetMixer());
	// Choose which pref to apply based on the Output option: ie we use a separate pref when the user is in Headphone mode
	const s32 controlPadSpeaker = CPauseMenu::AreHeadphonesEnabled() ? PREF_CTRL_SPEAKER_HEADPHONE : PREF_CTRL_SPEAKER;	
	g_AudioEngine.GetEnvironment().SetPadSpeakerEnabled(CPauseMenu::GetMenuPreference(controlPadSpeaker) != 0 && device->IsPadSpeakerEnabled());
#endif // RSG_ORBIS

	u32 sfxSliderCurrentTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(1);
	if(g_FrontendAudioEntity.IsLoadingSceneActive())
	{
		if(sm_LoadingScreenSFXSliderDelay == 0)
		{
			sm_LoadingScreenSFXSliderDelay = sfxSliderCurrentTime + 2000;
		}
		if(sm_LoadingScreenSFXSliderDelay != 0 && sfxSliderCurrentTime > sm_LoadingScreenSFXSliderDelay)
		{
			// Mute SFX during loads
			sfxSliderVolume = 0.f;
		}
	}
	else
	{
		sm_LoadingScreenSFXSliderDelay = 0;
	}

	// mute if lost focus PC only
#if __WIN32PC
	if(muteForLostFocus)
	{
		sfxSliderVolume = 0.f;
		musicVolLin = 0.f;
	}
#endif
	SetSfxVolume(sfxSliderVolume);
	SetMusicVolume(musicVolLin);

	// In the pause menu, we boost scripted speech
	const f32 audSettingsScriptBoostScale = ((f32)CPauseMenu::GetMenuPreference(PREF_DIAG_BOOST)) * frontendLinearScaling;
	sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("scriptedSpeechVol", 0x5b3a463), audSettingsScriptBoostScale);

#if NA_RADIO_ENABLED
	// the amount we duck depends on whether we're in stereo/prologic mode or not - we duck less if we're not
	f32 duckVolDb = g_RadioDuckingVolumeStereo;
	if (audDriver::GetDownmixOutputMode() >= AUD_OUTPUT_5_1)
	{
		duckVolDb = g_RadioDuckingVolume;
	}
		
	const audRadioStation* station = g_RadioAudioEntity.GetPlayerRadioStation();
	
	// duck more for talk stations
	if (station && station->IsTalkStation())
	{
		duckVolDb = g_TalkRadioDuckingVolume;
	}
	else
	{
		if (station)
		{
			// or if dj, adverts, idents, etc are playing

			const audRadioTrack &track = station->GetCurrentTrack();
			if (track.IsInitialised())
			{
				u32 trackCategory = track.GetCategory();
				if(trackCategory != RADIO_TRACK_CAT_MUSIC && trackCategory != RADIO_TRACK_CAT_TAKEOVER_MUSIC)
				{
					// duck it some more
					duckVolDb += g_RadioNonMusicAdditionalDucking;
				}
			}
		}
	}
		
	// Increase ducking with the engine volume offset
	duckVolDb -= sm_EngineVolume;
	
	float radioProxyDuckVolDb = 0.f;
	if(sm_RadioProxyCat)
	{
		radioProxyDuckVolDb += sm_RadioProxyCat->GetVolume();
	}

	float scoreDuckVolDb = 0.f;
	if(sm_ScoreProxyCat)
	{
		scoreDuckVolDb += sm_ScoreProxyCat->GetVolume();
	}

	if(GetSfxVolume() == 0.f || g_RadioAudioEntity.IsInFrontendMode())
	{
		// dont duck when Sfx are muted or we are in frontend radio mode
		duckVolDb = 0.f;
		scoreDuckVolDb = radioProxyDuckVolDb = 0.f;
	}
	else
	{
		const f32 musicVoldB = audDriverUtil::ComputeDbVolumeFromLinear(GetMusicVolume());
		const f32 sfxVoldB = audDriverUtil::ComputeDbVolumeFromLinear(GetSfxVolume());
		// preserve relative balance between sfx and music		
		// Clamp to ensure we don't apply positive gain
		duckVolDb = Min(duckVolDb - (musicVoldB - sfxVoldB), 0.0f);

		// Don't duck radio/score if sfx is down (ie proximity effect ducking radio, which sounds weird if engine sounds are quiet)
		scoreDuckVolDb = Clamp(scoreDuckVolDb + (musicVoldB - sfxVoldB), scoreDuckVolDb, 0.0f);
		radioProxyDuckVolDb = Clamp(radioProxyDuckVolDb + (musicVoldB - sfxVoldB), radioProxyDuckVolDb, 0.0f);
	}

	if(sm_ScoreVolCategoryController && sm_OneShotVolCategoryController)
	{
		sm_ScoreVolCategoryController->SetVolumeDB(scoreDuckVolDb);
		sm_OneShotVolCategoryController->SetVolumeDB(scoreDuckVolDb);
	}

	f32 desiredRadioDuckingVolume = 0.f;

	// Is there a scripted conversation ongoing?
	if (!g_ScriptAudioEntity.ShouldNotDuckRadioThisLine() &&
		(g_ScriptAudioEntity.ShouldDuckForScriptedConversation() || sm_ShouldDuckRadio || g_AudioScannerManager.ShouldDuckRadio()))
	{
		desiredRadioDuckingVolume = duckVolDb;
	}
	else if(audPedAudioEntity::GetShouldDuckRadioForPlayerRingtone())
	{
		desiredRadioDuckingVolume = duckVolDb;
	}


#if RSG_PC
	if(sm_DataSetState == Loaded_MP)
	{
		if(NetworkInterface::GetVoice().IsAnyRemoteGamerTalking())
		{
			desiredRadioDuckingVolume = audDriverUtil::ComputeDbVolumeFromLinear( ((f32)CPauseMenu::GetMenuPreference(PREF_VOICE_MUSIC_VOLUME)) * frontendLinearScaling );
			//Displayf("Radio Duck %f", desiredRadioDuckingVolume);
		}
	}
#else
	if(CPauseMenu::GetMenuPreference(PREF_VOICE_OUTPUT) == 0 &&
#if __XENON
		CProfileSettings::GetInstance().GetInt(CProfileSettings::VOICE_VOLUME)>0 &&
		CProfileSettings::GetInstance().GetInt(CProfileSettings::VOICE_THRU_SPEAKERS) &&
#endif
		(NetworkInterface::GetVoice().IsAnyRemoteGamerTalking()/* || NetworkInterface::GetVoice().IsAnyRemoteGamerAboutToTalk()*/))
	{
		desiredRadioDuckingVolume = g_RadioDuckingVolumeVoiceChat;
	}
#endif

	// use unpausable timer so that it unducks radio in pause menu
	f32 smoothedRadioDuckingVolumeLin = sm_RadioDuckingSmoother.CalculateValue(audDriverUtil::ComputeLinearVolumeFromDb(desiredRadioDuckingVolume), g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(1));
	
	const f32 missionCompleteDuckingVolDb = g_ScriptAudioEntity.ComputeRadioVolumeForMissionComplete();
	const f32 missionCompleteDuckingVolLin = audDriverUtil::ComputeLinearVolumeFromDb(missionCompleteDuckingVolDb);

	sm_FrontendRadioVolumeLin = missionCompleteDuckingVolLin * smoothedRadioDuckingVolumeLin * audDriverUtil::ComputeLinearVolumeFromDb(radioProxyDuckVolDb);

	sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("radioFrontendVol", 0xc00f6acd), 1.f-sm_FrontendRadioVolumeLin);
	if(sm_PlayerInVehScene)
	{
		sm_PlayerInVehScene->SetVariableValue(ATSTRINGHASH("musicVol", 0x7F9D4065),1.f- GetMusicVolume());
	}
	
	// this is relatively nasty - if the music volume (controlled via the frontend) is fully silenced then set the radio category to silence, so that
	// emitters (which don't go via the music effect route) are also muted
	// also have to explicitly mute positioned radio in the frontend, since the radio can be unpaused in the audio menu

	// Note posRadioVol is a linear volume the wrong way round; 1 is silent, 0 is full volume
	if(GetMusicVolume() == 0.f)
	{
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("posRadioVol", 0x4be8fa9a), 1.f);
	}
	else
	{
		sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("posRadioVol", 0x4be8fa9a), 0.f);
	}

#endif // NA_RADIO_ENABLED
/*
	f32 desiredGPSDuckingVolume = 0.0f;
	// Is there a scripted conversation ongoing?
	if (g_ScriptAudioEntity.ShouldDuckForScriptedConversation())
	{
		desiredGPSDuckingVolume = g_RadioDuckingVolume;
	}
	f32 smoothedGPSDuckingVolume = sm_GPSDuckingSmoother.CalculateValue(audDriverUtil::ComputeLinearVolumeFromDb(desiredGPSDuckingVolume), sm_TimeInMs);
	g_GpsCategory->SetVolumeLinear(smoothedGPSDuckingVolume);
*/	

	// Duck all things train if we're riding on it or in it
	/*f32 trainVolume = 1.0f;
	if (camInterface::IsAttachedToTrain()) - CDE: Removed custom camera code for trains.
	{
		trainVolume = g_AttachedToTrainVolume;
	}
	// also duck the train if we're in slowmo as its rather prone to clipping
	if(fwTimer::GetTimeWarpActive() < 0.9f)
	{
		trainVolume *= audDriverUtil::ComputeLinearVolumeFromDb(g_SlowMoTrainVol);
	}
	if (g_TrainCategory)
	{
		g_TrainCategory->SetVolumeLinear(trainVolume);
	} */
	u32 currentTime = g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0);
	f32 applyValue = 1.0f;
	f32 weatherApply = sm_PlayerVehicleOpenness;

	if(g_ScriptAudioEntity.ShouldDuckForScriptedConversation())
	{
		if (sm_SpeechScene )
		{
			if(!sm_SpeechSceeneApplied)
			{
				naDisplayf("SPEECH_SCENE apply set to 1.");
				sm_TimeSpeechSceneLastStarted = currentTime;
				sm_SpeechSceeneApplied = true;
				sm_SpeechGunfirePatchApplyAtStart = (sm_SpeechGunfirePatch ? sm_SpeechGunfirePatch->GetApplyFactor() : 0.f);
				sm_SpeechScorePatchApplyAtStart = (sm_SpeechScorePatch ? sm_SpeechScorePatch->GetApplyFactor() : 0.f);
				sm_SpeechVehiclesPatchApplyAtStart = (sm_SpeechVehiclesPatch ? sm_SpeechVehiclesPatch->GetApplyFactor() : 0.f);
				sm_SpeechVehiclesFirstPersonPatchApplyAtStart = (sm_SpeechVehiclesFirstPersonPatch ? sm_SpeechVehiclesFirstPersonPatch->GetApplyFactor() : 0.f);
				sm_SpeechWeatherPatchApplyAtStart = (sm_SpeechWeatherPatch ? sm_SpeechWeatherPatch->GetApplyFactor() : 0.f);
			}

			u32 timeSinceSceneStarted = (currentTime - sm_TimeSpeechSceneLastStarted);
			if(sm_SpeechGunfirePatch && sm_SpeechGunfirePatch->GetApplyFactor() != 1.0f)
			{
				applyValue = (sm_SpeechGunfirePatch->GetFadeInTime() == 0) ? 1.0f:
					Clamp(sm_SpeechGunfirePatchApplyAtStart + ((float)timeSinceSceneStarted/(float)sm_SpeechGunfirePatch->GetFadeInTime()),
					0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("gunfireApply", applyValue);
			}
			if(sm_SpeechScorePatch && sm_SpeechScorePatch->GetApplyFactor() != 1.0f)
			{
				applyValue = (sm_SpeechScorePatch->GetFadeInTime() == 0) ? 1.0f:
					Clamp(sm_SpeechScorePatchApplyAtStart + ((float)timeSinceSceneStarted/(float)sm_SpeechScorePatch->GetFadeInTime()),
					0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("scoreApply", applyValue);
			}
			if(sm_SpeechVehiclesPatch && sm_SpeechVehiclesPatch->GetApplyFactor() != 1.0f)
			{
				applyValue = (sm_SpeechVehiclesPatch->GetFadeInTime() == 0) ? 1.0f:
					Clamp(sm_SpeechVehiclesPatchApplyAtStart + ((float)timeSinceSceneStarted/(float)sm_SpeechVehiclesPatch->GetFadeInTime()),
					0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("vehiclesApply", applyValue);
			}
			if(sm_SpeechVehiclesFirstPersonPatch)
			{
				applyValue = (sm_SpeechVehiclesFirstPersonPatch->GetFadeInTime() == 0) ? 1.0f:
					Clamp(sm_SpeechVehiclesFirstPersonPatchApplyAtStart + ((float)timeSinceSceneStarted/(float)sm_SpeechVehiclesFirstPersonPatch->GetFadeInTime()),
					0.0f, 1.0f);
				// the apply factor it's only 1 when in 1st person camera : 
				if( !sm_Microphones.IsVehBonnetMic() )
				{
					applyValue = (sm_SpeechVehiclesFirstPersonPatch->GetFadeInTime() == 0) ? 0.0f:
						Clamp(sm_SpeechVehiclesFirstPersonPatchApplyAtStop - ((float)timeSinceSceneStarted/(float)sm_SpeechVehiclesFirstPersonPatch->GetFadeOutTime()), 0.0f, 1.0f);
				}
				sm_SpeechScene->SetVariableValue("vehiclesFirstPersonApply", applyValue);
			}
			if(sm_SpeechWeatherPatch && sm_SpeechWeatherPatch->GetApplyFactor() != weatherApply)
			{
				applyValue = (sm_SpeechWeatherPatch->GetFadeInTime() == 0) ? 1.0f:
					Clamp(sm_SpeechWeatherPatchApplyAtStart + ((float)timeSinceSceneStarted/(float)sm_SpeechWeatherPatch->GetFadeInTime()),
					0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("weatherApply", applyValue * weatherApply);
			}
		}
	}
	else
	{
		if(sm_SpeechScene)
		{
			if(sm_SpeechSceeneApplied)
			{
				naDisplayf("SPEECH_SCENE apply set to 0.");
				sm_TimeSpeechSceneLastStopped = currentTime;
				sm_SpeechSceeneApplied = false;
				sm_SpeechGunfirePatchApplyAtStop = (sm_SpeechGunfirePatch ? sm_SpeechGunfirePatch->GetApplyFactor() : 0.f);
				sm_SpeechScorePatchApplyAtStop = (sm_SpeechScorePatch ? sm_SpeechScorePatch->GetApplyFactor() : 0.f);
				sm_SpeechVehiclesPatchApplyAtStop = (sm_SpeechVehiclesPatch ? sm_SpeechVehiclesPatch->GetApplyFactor() : 0.f);
				sm_SpeechVehiclesFirstPersonPatchApplyAtStop = (sm_SpeechVehiclesFirstPersonPatch ? sm_SpeechVehiclesFirstPersonPatch->GetApplyFactor() : 0.f);
				sm_SpeechWeatherPatchApplyAtStop = (sm_SpeechWeatherPatch ? sm_SpeechWeatherPatch->GetApplyFactor() : 0.f);

			}

			u32 timeSinceSceneStopped = (currentTime - sm_TimeSpeechSceneLastStopped);
			if(sm_SpeechGunfirePatch && sm_SpeechGunfirePatch->GetApplyFactor() != 0.0f)
			{
				applyValue = (sm_SpeechGunfirePatch->GetFadeOutTime() == 0) ? 0.0f :
					Clamp(sm_SpeechGunfirePatchApplyAtStop - ((float)timeSinceSceneStopped/(float)sm_SpeechGunfirePatch->GetFadeOutTime()), 0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("gunfireApply", applyValue);
			}
			if(sm_SpeechScorePatch && sm_SpeechScorePatch->GetApplyFactor() != 0.0f)
			{
				applyValue = (sm_SpeechScorePatch->GetFadeOutTime() == 0) ? 0.0f:
					Clamp(sm_SpeechScorePatchApplyAtStop - ((float)timeSinceSceneStopped/(float)sm_SpeechScorePatch->GetFadeOutTime()), 0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("scoreApply", applyValue);
			}
			if(sm_SpeechVehiclesPatch && sm_SpeechVehiclesPatch->GetApplyFactor() != 0.0f)
			{
				applyValue = (sm_SpeechVehiclesPatch->GetFadeOutTime() == 0) ? 0.0f:
					Clamp(sm_SpeechVehiclesPatchApplyAtStop - ((float)timeSinceSceneStopped/(float)sm_SpeechVehiclesPatch->GetFadeOutTime()), 0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("vehiclesApply", applyValue);
			}
			if(sm_SpeechVehiclesFirstPersonPatch && sm_SpeechVehiclesFirstPersonPatch->GetApplyFactor() != 0.0f)
			{
				applyValue = (sm_SpeechVehiclesFirstPersonPatch->GetFadeOutTime() == 0) ? 0.0f:
					Clamp(sm_SpeechVehiclesFirstPersonPatchApplyAtStop - ((float)timeSinceSceneStopped/(float)sm_SpeechVehiclesFirstPersonPatch->GetFadeOutTime()), 0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("vehiclesFirstPersonApply", applyValue);
			}
			if(sm_SpeechWeatherPatch && sm_SpeechWeatherPatch->GetApplyFactor() != weatherApply)
			{
				applyValue = (sm_SpeechWeatherPatch->GetFadeOutTime() == 0) ? 0.0f:
					Clamp(sm_SpeechWeatherPatchApplyAtStop - ((float)timeSinceSceneStopped/(float)sm_SpeechWeatherPatch->GetFadeOutTime()), 0.0f, 1.0f);
				sm_SpeechScene->SetVariableValue("weatherApply", applyValue * weatherApply);
			}
		}
	}
	// Get rid of waves in interiors. And church bells.
	f32 playerInteriorRatio = 0.0f;
	//bool isInteriorASubway = false;
	//bool inInterior = audNorthAudioEngine::GetGtaEnvironment()->AreWeInAnInterior(&isInteriorASubway, &playerInteriorRatio);

	sm_NorthAudioMixScene->SetVariableValue(ATSTRINGHASH("interiorRatio", 0x13fc3a70), playerInteriorRatio);

	/*
	if (g_AmbienceJetsCategory)
	{
		g_AmbienceJetsCategory->SetVolumeLinear(waveVolumeDB);
	} 
	// If we're in a subway, increase the rolloff on trains - need to smooth it, over at least a second
	f32 desiredTrainRolloff = 1.0f;
	if (inInterior && isInteriorASubway)
	{
		desiredTrainRolloff = g_TrainRolloffInSubways;
	}
	f32 trainRolloffSmoothed = sm_TrainRolloffInSubwaySmoother.CalculateValue(desiredTrainRolloff, sm_TimeInMs);
	if (g_TrainCategory)
	{
		g_TrainCategory->BeginDistRolloffScaleApproach(trainRolloffSmoothed);
	} */

	// We don't want to smooth leakage when going from black as it's a hard cut, so reset the smoother
	if(IsScreenFadedOut())
	{
		sm_CutsceneLeakageSmoother.Reset();
	}

	// Also do the mocap cutscene leakage here, because it vaguely fits
	f32 leakage;
	if (sm_IsCutsceneActive)
	{
		leakage = sm_CutsceneLeakageSmoother.CalculateValue(g_CutscenePositionedLeakage, g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
		g_AudioEngine.GetEnvironment().SetGlobalPositionedLeakage(leakage);
	}
	else
	{
		leakage = sm_CutsceneLeakageSmoother.CalculateValue(0.0f, g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(0));
		g_AudioEngine.GetEnvironment().SetGlobalPositionedLeakage(leakage);
	}

#if __BANK
	if(g_DebugCutsceneLeakage)
	{
		sm_CutsceneLeakageSmoother.SetRate(g_CutsceneLeakageSmootherRate * 0.001f);

		static audMeterList leakageMeter;
		static const char* leakageMeterName = "Cutscene Leakage";
		static f32 leakageMeterValue;

		leakageMeterValue = leakage;

		leakageMeter.left = 150.f;
		leakageMeter.width = 50.f;
		leakageMeter.bottom = 350.f;
		leakageMeter.height = 50.f;
		leakageMeter.names = &leakageMeterName;
		leakageMeter.values = &leakageMeterValue;
		leakageMeter.numValues = 1;
		DrawLevelMeters(&leakageMeter);
	}
#endif

#if __BANK
	if(g_ShouldMuteWind)
	{
		if(!g_WindMuteController)
		{
			g_WindMuteController = AUDCATEGORYCONTROLLERMANAGER.CreateController(ATSTRINGHASH("AMBIENCE_WEATHER", 0xB26931C3));
			if(g_WindMuteController)
			{
				g_WindMuteController->SetVolumeLinear(0.f);
			}
		}
	}
	else if(g_WindMuteController)
	{
		AUDCATEGORYCONTROLLERMANAGER.DestroyController(g_WindMuteController);
		g_WindMuteController = NULL;
	}
#endif
}

void audNorthAudioEngine::Pause()
{
	// Timer 0 is the normal timer, timer 5 is the slow-mo timer
	// Both should pause/unpause together
	if((sm_FrontendScene && sm_FrontendScene->IsFullyAppliedForPause()))
	{ 
		sm_Paused = true;
		g_AudioEngine.GetSoundManager().Pause(0);
		g_AudioEngine.GetSoundManager().Pause(5);
	}
	else if(fwTimer::IsUserPaused() BANK_ONLY(|| fwTimer::IsDebugPause()))
	{
		// When pausing via the debug keyboard we don't want to apply the fade, but we do want to pause
		sm_Paused = true;
		g_AudioEngine.GetSoundManager().Pause(0); 
		g_AudioEngine.GetSoundManager().Pause(5);
	}
	g_AudioEngine.SetGamePaused(true);
}

void audNorthAudioEngine::UnPause()
{
	sm_Paused = false;
	g_AudioEngine.GetSoundManager().UnPause(0);
	g_AudioEngine.GetSoundManager().UnPause(5);
	g_AudioEngine.SetGamePaused(false);
}

void audNorthAudioEngine::RegisterLoudSound(Vector3 &pos, bool playerShot)
{
	Vector3 dist = pos - VEC3V_TO_VECTOR3(g_AudioEngine.GetEnvironment().GetVolumeListenerPosition());
	if(dist.Mag2() <= (g_audLoudSoundDistanceMag*g_audLoudSoundDistanceMag))
	{
		audNorthAudioEngine::sm_LastLoudSoundTime = fwTimer::GetTimeInMilliseconds();
		g_EmitterAudioEntity.NotifyLoudSound();
		// Send message to the superconductor to stop the QUITE_SCENE
		if(audSuperConductor::sm_StopQSOnLoudSounds && !playerShot && !NetworkInterface::IsGameInProgress())
		{
			ConductorMessageData message;
			message.conductorName = SuperConductor;
			message.message = StopQuietScene;
			message.bExtraInfo = false;
			message.vExtraInfo = Vector3((f32)audSuperConductor::sm_LoudSoundQSFadeOutTime
										,(f32)audSuperConductor::sm_LoudSoundQSDelayTime
										,(f32)audSuperConductor::sm_LoudSoundQSFadeInTime);
			SUPERCONDUCTOR.SendConductorMessage(message);
		}
	}
}

u32 audNorthAudioEngine::GetLastLoudSoundTime()
{
	return audNorthAudioEngine::sm_LastLoudSoundTime;
}

const char* audNorthAudioEngine::GetCurrentAudioLevelName()
{			
	// Game level index value defaults to level 1 even with 0 levels loaded...
	s32 levelIndex = CGameLogic::GetCurrentLevelIndex();

	if(levelIndex < CScene::GetLevelsData().GetCount())
	{				
		const char* levelNameString = CScene::GetLevelsData().GetFilename(levelIndex);
		s32 baseFolderStart = atString(levelNameString).LastIndexOf('\\');

		if(baseFolderStart >= 0)
		{
			return &levelNameString[baseFolderStart + 1];
		}		
	}

	return "gta5";
}

bool audNorthAudioEngine::IsScreenFadedOut()
{
	return sm_IsFadedToBlack;
}

void audNorthAudioEngine::DebugDraw()
{
#if __BANK
	g_RadioAudioEntity.DrawDebug();

	DrawBufferedLevelMeters();
	g_AudioEngine.DrawDebug();
	g_AudioEngine.GetSoundManager().DebugDrawSounds();

	audSpeechAudioEntity::DebugDraw();

	if(audGrainPlayer::s_DebugDrawGrainPlayer)
	{
		audGrainPlayer::s_DebugDrawGrainPlayer->DebugDraw();
	}

	sm_DynamicMixer.DrawDebugScenes();

	g_PoliceScanner.DrawDebug();

	DebugDrawSlowMo();

	audSoundDebugDrawManager waveSlotDrawMgr(100.f, 50.f, 50.f, 720.f);
	audWaveSlot::DebugDraw(waveSlotDrawMgr);
#endif
}
void *audNorthAudioEngine::GetObject(const u32 nameHash, const u32 TYPE_ID)
{
	u8 *ptr = static_cast<u8*>(sm_MetadataMgr.GetObjectMetadataPtr(nameHash));
	if(ptr)
	{
		naCErrorf(TYPE_ID == (*ptr), "In GetObject, we've found an object for nameHash (%d) but it's type id (%d) doesn't match the one we expected (%d)", nameHash, (*ptr), TYPE_ID);
		if(TYPE_ID != (*ptr))
		{
			ptr = NULL;
		}
	}
	return ptr;
}

void audNorthAudioEngine::AmbisonicDraw(ambisonicDrawData data[90])
{
#if __BANK
	Vector2 unit = grcDebugDraw::Get2DAspect();
	Vector2 origin(0.5f, 0.5f);
	Vector2 startA(0.f, 0.f);
	Vector2 prevA(0.f, 0.f);
	Vector2 ang(0.f, 0.f);
	Vector2 angE(0.f, 0.f);
	Vector2 angV(0.f, 0.f);
	Vector2 vecE(0.f, 0.f);
	Vector2 vecV(0.f, 0.f);
	Vector2 vecEPrev(0.f, 0.f);
	Vector2 vecVPrev(0.f, 0.f);
	Vector2 energy(0.f, 0.f);
	Vector2 energyPrev(0.f, 0.f);
	Color32 colour;
	
	
	startA.Set(0.2f*Cosf(0.f), 0.2f*Sinf(DtoR*(0.f)));
	angE.Set(0.15f*Cosf(data[0].reproducedAngE), 0.15f*Sinf(data[0].reproducedAngE));
	angV.Set(0.1f*Cosf(data[0].reproducedAngV), 0.1f*Sinf(data[0].reproducedAngV));

	startA.Multiply(unit);
	angE.Multiply(unit);
	angV.Multiply(unit);
	startA.Add(origin);
	angE.Add(origin);
	angV.Add(origin);

	vecE.Set(0.2f*data[0].vecLengthE*Cosf(0.f), 0.2f*data[0].vecLengthE*Sinf(0.f));
	vecE.Multiply(unit);
	vecE.Add(origin);

	vecV.Set(0.2f*data[0].vecLengthV*Cosf(0.f), 0.2f*data[0].vecLengthV*Sinf(0.f));
	vecV.Multiply(unit);
	vecV.Add(origin);

	energy.Set(0.05f*data[0].energy*Cosf(0.f), 0.05f*data[0].energy*Sinf(0.f));
	energy.Multiply(unit);
	energy.Add(origin);

	colour.Set(255, 0, 0);
	grcDebugDraw::Line(origin, startA, colour);
	colour.Set(0, 255, 0);
	grcDebugDraw::Line(origin, angE, colour);
	colour.Set(0, 0, 255);
	grcDebugDraw::Line(origin, angV, colour);
	
	prevA.Set(startA);
	vecVPrev.Set(vecV);
	vecEPrev.Set(vecE);
	energyPrev.Set(energy);

	for(s32 i=1; i<90; i++)
	{
		f32 curAngle = DtoR*((float)(i*4));
		f32 curCos = Cosf(curAngle);
		f32 curSin = Sinf(curAngle);
		ang.Set(0.2f*curCos, 0.2f*curSin);
		ang.Multiply(unit);
		ang.Add(origin);


		vecE.Set(0.2f*data[i].vecLengthE*curCos, 0.2f*data[i].vecLengthE*curSin);
		vecE.Multiply(unit);
		vecE.Add(origin);

		vecV.Set(0.2f*data[i].vecLengthV*curCos, 0.2f*data[i].vecLengthV*curSin);
		vecV.Multiply(unit);
		vecV.Add(origin);

		energy.Set(0.05f*data[i].energy*curCos, 0.05f*data[i].energy*curSin);
		energy.Multiply(unit);
		energy.Add(origin);

		colour.Set(255, 255, 255);
		grcDebugDraw::Line(prevA, ang, colour);
		colour.Set(100, 50, 100);
		grcDebugDraw::Line(vecEPrev, vecE, colour);
		colour.Set(100, 150, 0);
		grcDebugDraw::Line(vecVPrev, vecV, colour);
		colour.Set(0, 0, 0);
		grcDebugDraw::Line(energyPrev, energy, colour);

		vecVPrev.Set(vecV);
		vecEPrev.Set(vecE);
		energyPrev.Set(energy);

		//Compare incident angles to reproduced angles
		if(i == 15 || i ==22 || i == 30 || i == 45 || i == 60 || i == 75)
		{
			angE.Set(0.15f*Cosf(data[i].reproducedAngE), 0.15f*Sinf(data[i].reproducedAngE));
			angV.Set(0.1f*Cosf(data[i].reproducedAngV), 0.1f*Sinf(data[i].reproducedAngV));
			angE.Multiply(unit);
			angV.Multiply(unit);
			angE.Add(origin);
			angV.Add(origin);

			colour.Set(255, 255, 255);
			grcDebugDraw::Line(origin, ang, colour);
			colour.Set(0, 255, 0);
			grcDebugDraw::Line(origin, angE, colour);
			colour.Set(0, 0, 255);
			grcDebugDraw::Line(origin, angV, colour);
		}

		prevA.Set(ang);
	}

	ang.Set(0.2f*Cosf(2.f*PI), 0.2f*Sinf(2*PI));
	ang.Multiply(unit);
	ang.Add(origin);

	vecE.Set(0.2f*data[0].vecLengthE*Cosf(2.f*PI), 0.2f*data[0].vecLengthE*Sinf(2.f*PI));
	vecE.Multiply(unit);
	vecE.Add(origin);

	vecV.Set(0.2f*data[0].vecLengthV*Cosf(2.f*PI), 0.2f*data[0].vecLengthV*Sinf(2.f*PI));
	vecV.Multiply(unit);
	vecV.Add(origin);

	energy.Set(0.05f*data[0].energy*Cosf(2*PI), 0.05f*data[0].energy*Sinf(2*PI));
	energy.Multiply(unit);
	energy.Add(origin);

	colour.Set(255, 255, 255);
	grcDebugDraw::Line(prevA, ang, colour);
	colour.Set(100, 50, 100);
	grcDebugDraw::Line(vecEPrev, vecE, colour);
	colour.Set(100, 150, 0);
	grcDebugDraw::Line(vecVPrev, vecV, colour);
	colour.Set(0, 0, 0);
	grcDebugDraw::Line(energyPrev, energy, colour);
#else // __BANK
	(void)data;
#endif // __BANK
}

void audNorthAudioEngine::AmbisonicSave()
{
	CProfileSettings& settings = CProfileSettings::GetInstance();

	if(settings.AreSettingsValid())
	{
		ambisonicDecodeData decoderData;
		g_AudioEngine.GetAmbisonics().SaveDecoder(decoderData);

		s32 i = static_cast<s32>(CProfileSettings::AMBISONIC_DECODER);
		for(s32 sp = 0; sp < AMBISONICS_SPEAKER_MAX; sp++)
		{
			for(s32 a = 0; a < AMB_ORDER; a++)
			{
				settings.Set(static_cast<CProfileSettings::ProfileSettingId>(i), decoderData.decoder[sp].Co[a]);
				++i;
				settings.Set(static_cast<CProfileSettings::ProfileSettingId>(i), decoderData.decoder[sp].Si[a]);
				++i;
			}
			settings.Set(static_cast<CProfileSettings::ProfileSettingId>(i), decoderData.decoder[sp].w);
			++i;
		}

		settings.Set(CProfileSettings::AMBISONIC_DECODER_TYPE, decoderData.decoderType);

		settings.Write();
		Displayf("Ambisonic Decoder Saved");
	}
}

void audNorthAudioEngine::AmbisonicLoad()
{
	CProfileSettings& settings = CProfileSettings::GetInstance();

	if(settings.AreSettingsValid())
	{
		ambisonicDecodeData decodeData;

		s32 i = static_cast<s32>(CProfileSettings::AMBISONIC_DECODER);
		for(s32 sp = 0; sp < AMBISONICS_SPEAKER_MAX; sp++)
		{
			for(s32 a = 0; a < AMB_ORDER; a++)
			{
				if(settings.Exists(static_cast<CProfileSettings::ProfileSettingId>(i))) 
				{
					decodeData.decoder[sp].Co[a] = settings.GetFloat(static_cast<CProfileSettings::ProfileSettingId>(i));
				}
				++i;
				if(settings.Exists(static_cast<CProfileSettings::ProfileSettingId>(i))) 
				{
					decodeData.decoder[sp].Si[a] = settings.GetFloat(static_cast<CProfileSettings::ProfileSettingId>(i));
				}
				++i;
			}
			if(settings.Exists(static_cast<CProfileSettings::ProfileSettingId>(i))) 
			{
				decodeData.decoder[sp].w = settings.GetFloat(static_cast<CProfileSettings::ProfileSettingId>(i));
			}
			++i;
		}

		if(settings.Exists(CProfileSettings::AMBISONIC_DECODER_TYPE)) 
		{
			decodeData.decoderType = static_cast<EDecoderType>(settings.GetInt(CProfileSettings::AMBISONIC_DECODER_TYPE));
		}

		g_AudioEngine.GetAmbisonics().LoadDecoder(decodeData);
		Displayf("Ambisonic Decoder Loaded");
	}
}

void audNorthAudioEngine::MuteCategory(audScene ** scene, const char * settings, bool mute)
{
	if(mute && !*scene)
	{
		naDisplayf("[NorthAudio] Muting %s", settings);
		sm_DynamicMixer.StartScene(settings, scene);
	}
	else if(!mute && *scene)
	{
		naDisplayf("[NorthAudio] Unmuting %s", settings);
		(*scene)->Stop();
	}
#if ! __FINAL
	else if(mute)
	{
		naDisplayf("[NorthAudio] Mute %s is already on", settings);
	}
	else
	{
		naDisplayf("[NorthAudio] Mute %s is already off", settings);
	}
#endif

}	

void audNorthAudioEngine::MuteAmbience(bool mute)
{
	MuteCategory(&sm_AmbienceMuted, "MUTES_AMBIENCE_SCENE", mute);
}
		
void audNorthAudioEngine::MuteCutscene(bool mute)
{
	MuteCategory(&sm_CutsceneMuted, "MUTES_CUTSCENE_SCENE", mute);
}

void audNorthAudioEngine::MuteFrontend(bool mute)
{
	MuteCategory(&sm_FrontendMuted, "MUTES_FRONTEND_SCENE", mute);
}

void audNorthAudioEngine::MuteMusic(bool mute)
{
	MuteCategory(&sm_MusicMuted, "MUTES_MUSIC_SCENE", mute);
}

void audNorthAudioEngine::MuteRadio(bool mute)
{
	MuteCategory(&sm_RadioMuted, "MUTES_RADIO_SCENE", mute);
}

void audNorthAudioEngine::MuteSfx(bool mute)
{
	MuteCategory(&sm_SfxMuted, "MUTES_SFX_SCENE", mute);
}

void audNorthAudioEngine::MuteSpeech(bool mute)
{
	MuteCategory(&sm_SpeechMuted, "MUTES_SPEECH_SCENE", mute);
}

void audNorthAudioEngine::MuteGuns(bool mute)
{
	MuteCategory(&sm_GunsMuted, "MUTES_GUNS_SCENE", mute);
}

void audNorthAudioEngine::MuteVehicles(bool mute)
{
	MuteCategory(&sm_VehiclesMuted, "MUTES_VEHICLES_SCENE", mute);
}

void audNorthAudioEngine::ToggleMuteAmbience() 
{ 
	MuteAmbience(sm_AmbienceMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteCutscene() 
{ 
	MuteCutscene(sm_CutsceneMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteFrontend() 
{ 
	MuteFrontend(sm_FrontendMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteMusic()	  
{ 
	MuteMusic(sm_MusicMuted ? false:true); 
}
void audNorthAudioEngine::ToggleMuteRadio()	  
{ 
	MuteMusic(sm_RadioMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteSfx()	  
{ 
	MuteSfx(sm_SfxMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteSpeech()	  
{ 
	MuteSpeech(sm_SpeechMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteGuns()	  
{ 
	MuteGuns(sm_GunsMuted ? false:true); 
}

void audNorthAudioEngine::ToggleMuteVehicles()	  
{ 
	MuteVehicles(sm_VehiclesMuted ? false:true); 
}

void audNorthAudioEngine::NotifyLocalPlayerArrested()
{
	NA_POLICESCANNER_ENABLED_ONLY(g_PoliceScanner.ReportSuspectArrested());
	g_InteractiveMusicManager.GetWantedMusic().NotifyArrested();
}

void audNorthAudioEngine::NotifyLocalPlayerDied()
{
	/*if(!CTheScripts::GetPlayerIsOnAMission() && !g_ScriptAudioEntity.IsPlayingMissionComplete() && g_ScriptAudioEntity.CanPlayDeathJingle())
	{
		// Before Triggering it check if we have to cancel if there is one preparing ( We have seen this happening when you fail a stuntjump and die at the same time).
		if( g_ScriptAudioEntity.IsPreparingMissionComplete())
		{
			g_ScriptAudioEntity.StopMissionCompleteSound();
		}
		g_ScriptAudioEntity.TriggerMissionComplete("DEAD_OFFMISSION", 3);
	}*/
	g_InteractiveMusicManager.GetWantedMusic().NotifyDied();
}

void audNorthAudioEngine::NotifyLocalPlayerPlaneCrashed()
{
	g_InteractiveMusicManager.GetVehicleMusic().NotifyPlaneCrashed();
}

void audNorthAudioEngine::NotifyLocalPlayerBailedFromAircraft()
{
	g_InteractiveMusicManager.GetVehicleMusic().NotifyPlayerBailed();
}

void audNorthAudioEngine::NotifyOpenNetwork()
{
	sm_IsMPDataRequested = true;
}

void audNorthAudioEngine::NotifyCloseNetwork(const bool fullClose)
{
	g_InteractiveMusicManager.GetWantedMusic().NotifyCloseNetwork();
	if(fullClose)
	{
		sm_IsMPDataRequested = false;
	}
}

void audNorthAudioEngine::StartLongPlayerSwitch(const CPed &UNUSED_PARAM(oldPed), const CPed &UNUSED_PARAM(newPed))
{
	g_RadioAudioEntity.SetForcePlayerCharacterStation(true);
}

void audNorthAudioEngine::StopLongPlayerSwitch()
{
	g_RadioAudioEntity.SetForcePlayerCharacterStation(false);
	g_FrontendAudioEntity.StopLongSwitchSkyLoop();
}

void audNorthAudioEngine::StartPauseMenuSlowMo()
{
	if(sm_PauseMenuSlowMoCount == 0)
	{
		ActivateSlowMoMode(ATSTRINGHASH("SLOWMO_PAUSEMENU", 0xFAFBB6BF));
	}

	sm_PauseMenuSlowMoCount++;
}

void audNorthAudioEngine::StopPauseMenuSlowMo()
{
	if(sm_PauseMenuSlowMoCount > 0)
	{
		sm_PauseMenuSlowMoCount--;
		if(sm_PauseMenuSlowMoCount == 0)
		{
			sm_WaitingForPauseMenuSlowMoToEnd = true;
			DeactivateSlowMoMode(AUD_SLOWMO_PAUSEMENU);
		}
	}
}

void audNorthAudioEngine::SetSfxVolume(const float volLinear)
{
	sm_SfxVolume = volLinear;
	if(sm_SFXSliderController)
	{
		sm_SFXSliderController->SetVolumeLinear(volLinear);
	}
	if(sm_NoFadeVolCategoryController)
	{
		sm_NoFadeVolCategoryController->SetVolumeLinear(volLinear);
	}
	if(sm_ScriptedOverrideFadeVolCategoryController)
	{
		sm_ScriptedOverrideFadeVolCategoryController->SetVolumeLinear(volLinear);
	}
	
	g_AudioEngine.GetSoundManager().SetVariableValue(ATSTRINGHASH("Game.Audio.Sliders.SFXSlider", 0x2432BB37), volLinear);
}

void audNorthAudioEngine::SetMusicVolume(const float volLinear)
{
	sm_MusicVolume = volLinear;
	if(sm_MusicSliderController)
	{
		sm_MusicSliderController->SetVolumeLinear(volLinear);
	}

	g_AudioEngine.GetSoundManager().SetVariableValue(ATSTRINGHASH("Game.Audio.Sliders.MusicSlider", 0xFB69E838), volLinear);
}

#if RSG_PS3
void audNorthAudioEngine::CheckForWirelessHeadset(const bool loadPRX /*=false*/)
{
	sm_IsWirelessHeadsetConnected = ((audMixerDevicePs3*)audDriver::GetMixer())->IsPulseHeadsetConnected(loadPRX);
}
#endif

#if __BANK

void audNorthAudioEngine::UpdateRAVEVariables()
{
	if((fwTimer::GetFrameCount() & 7) == 7)
	{
		if(g_AudioEngine.GetRemoteControl().IsConnected())
		{
			static char messageBuf[4096];
			formatf(messageBuf, "<RAVEMessage timeStamp=\"%u\">", g_AudioEngine.GetSoundManager().GetTimeInMilliseconds(1));

			// Focus Entity variables
			CEntity *ent = CDebugScene::FocusEntities_Get(0);

			if(ent && ent->GetAudioEntity())
			{
				safecat(messageBuf, "<FocusEntityVariables>");

				audVariableBlock *vars = ent->GetAudioEntity()->GetVariableBlock();
				if(vars)
				{
					for(u32 i = 0; i < vars->GetMetadata()->numVariables; i++)
					{
						safecatf(messageBuf, "<Variable nameHash=\"%u\" value=\"%f\"/>", vars->GetMetadata()->Variable[i].Name, vars->GetValueByIndex(i));
					}			
				}

				safecat(messageBuf, "</FocusEntityVariables>");			
			}

			// Global variables
			ravesimpletypes::VariableList *list = audConfig::GetMetadataManager().GetObject<ravesimpletypes::VariableList>("GlobalVariableList");
			safecat(messageBuf, "<GlobalVariables>");
			if(list)
			{
				for(u32 i = 0; i < list->numVariables; i++)
				{		
					safecatf(messageBuf, "<Variable nameHash=\"%u\" value=\"%f\"/>", list->Variable[i].Name, g_AudioEngine.GetSoundManager().GetVariableByIndex(i));
				}
			}
			safecat(messageBuf, "</GlobalVariables>");

			safecat(messageBuf, "</RAVEMessage>");
			g_AudioEngine.GetRemoteControl().SendXmlMessage(messageBuf);
		}
	}
}

void audNorthAudioEngine::UpdateAuditionSound()
{
	g_AudioEngine.GetRemoteControl().SetAuditionSoundDrawMode(sm_AuditionSoundDrawMode);
	g_AudioEngine.GetRemoteControl().SetAuditionSoundDrawOffsetY(sm_DebugDrawYOffset);

	u32 soundNameHash;
	if(SOUNDFACTORY.GetAuditionStartSound(soundNameHash))
	{
		audMetadataObjectInfo info;
		if(SOUNDFACTORY.GetMetadataManager().GetObjectInfo(soundNameHash, info))
		{
			if(gSoundsIsOfType(info.GetType(), Sound::TYPE_ID))
			{
				AuditionSound(soundNameHash);
			}
		}
		else
		{
			audWarningf("RAVE Auditioning: Failed to find sound with hash %u", soundNameHash);
		}
	}

	u32 auditionStopNameHash;
	if(SOUNDFACTORY.GetAuditionStopSound(auditionStopNameHash))
	{
		audSound *auditionSound = *g_AudioEngine.GetRemoteControl().GetAuditionSoundPtr();
		if(auditionSound)
		{
			auditionSound->StopAndForget();
		}
	}
}

void audNorthAudioEngine::AuditionSound(const u32 soundNameHash)
{
	audSound **auditionSoundPtr = g_AudioEngine.GetRemoteControl().GetAuditionSoundPtr();

	if(*auditionSoundPtr)
	{
		(*auditionSoundPtr)->StopAndForget();
	}

	audSoundInitParams initParams;
	initParams.RemoveHierarchy = false;
	initParams.IsAuditioning = true;
	if(sm_AuditionSoundsOnPPU)
	{
		// stick it in the last bucket, which should be reserved
		Assign(initParams.BucketId, audSound::GetStaticPool().GetNumBuckets()-1);
	}

	audWaveSlot *auditionWaveSlot = audWaveSlot::FindWaveSlot(g_AuditionWaveSlotName);
	initParams.WaveSlot = auditionWaveSlot;
	initParams.AllowLoad = sm_AllowAuditionSoundToLoad;
	initParams.TimerId = sm_AuditionSoundUnpausable ? 1 : 0;
	if(sm_ForceBaseCategory)
	{
		initParams.Category = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("BASE", 0x44E21C90));
	}

	CEntity *auditioningEntity = NULL;
	if(sm_ShouldAuditionThroughPlayer)
	{
		auditioningEntity = FindPlayerPed();
	}
	else if(sm_ShouldAuditionThroughFocusEntity)
	{
		auditioningEntity = CDebugScene::FocusEntities_Get(0);
	}

	if(auditioningEntity && !sm_ShouldAuditionPannedFrontend)
	{
		initParams.Tracker = auditioningEntity->GetPlaceableTracker();
		initParams.EnvironmentGroup = auditioningEntity->GetAudioEnvironmentGroup();
	}
	else
	{
		initParams.Pan = 0;
	}

	if(auditioningEntity && auditioningEntity->GetAudioEntity())
	{
		auditioningEntity->GetAudioEntity()->CreateSound_PersistentReference(soundNameHash, auditionSoundPtr, &initParams);
	}
	else
	{
		g_FrontendAudioEntity.CreateSound_PersistentReference(soundNameHash, auditionSoundPtr, &initParams);
	}

	if(*auditionSoundPtr == NULL)
	{
		audWarningf("Couldn't instantiate auditioned sound with hash %u / name %s", soundNameHash, SOUNDFACTORY.GetMetadataManager().GetObjectName(soundNameHash));
		return;
	}
	else
	{
		audSound *auditionSound = *auditionSoundPtr;
		auditionSound->PrepareAndPlay(auditionWaveSlot, sm_AllowAuditionSoundToLoad, 3000);
	}
}

void audNorthAudioEngine::AuditionSound(const audMetadataRef soundRef)
{
	audSound **auditionSoundPtr = g_AudioEngine.GetRemoteControl().GetAuditionSoundPtr();

	if(*auditionSoundPtr)
	{
		(*auditionSoundPtr)->StopAndForget();
	}

	audSoundInitParams initParams;
	initParams.RemoveHierarchy = false;
	initParams.IsAuditioning = true;
	if(sm_AuditionSoundsOnPPU)
	{
		// stick it in the last bucket, which should be reserved
		Assign(initParams.BucketId, audSound::GetStaticPool().GetNumBuckets()-1);
	}

	audWaveSlot *auditionWaveSlot = audWaveSlot::FindWaveSlot(g_AuditionWaveSlotName);
	initParams.WaveSlot = auditionWaveSlot;
	initParams.AllowLoad = sm_AllowAuditionSoundToLoad;
	initParams.TimerId = sm_AuditionSoundUnpausable ? 1 : 0;
	if(sm_ForceBaseCategory)
	{
		initParams.Category = g_AudioEngine.GetCategoryManager().GetCategoryPtr(ATSTRINGHASH("BASE", 0x44E21C90));
	}

	CEntity *auditioningEntity = NULL;
	if(sm_ShouldAuditionThroughPlayer)
	{
		auditioningEntity = FindPlayerPed();
	}
	else if(sm_ShouldAuditionThroughFocusEntity)
	{
		auditioningEntity = CDebugScene::FocusEntities_Get(0);
	}

	if(auditioningEntity && !sm_ShouldAuditionPannedFrontend)
	{
		initParams.Tracker = auditioningEntity->GetPlaceableTracker();
		initParams.EnvironmentGroup = auditioningEntity->GetAudioEnvironmentGroup();
	}
	else
	{
		initParams.Pan = 0;
	}

	if(auditioningEntity && auditioningEntity->GetAudioEntity())
	{
		auditioningEntity->GetAudioEntity()->CreateSound_PersistentReference(soundRef, auditionSoundPtr, &initParams);
	}
	else
	{
		g_FrontendAudioEntity.CreateSound_PersistentReference(soundRef, auditionSoundPtr, &initParams);
	}

	if(*auditionSoundPtr == NULL)
	{
		audWarningf("Couldn't instantiate auditioned sound  %u / name %s",soundRef.Get(), SOUNDFACTORY.GetMetadataManager().GetObjectName(soundRef));
		return;
	}
	else
	{
		audSound *auditionSound = *auditionSoundPtr;
		auditionSound->PrepareAndPlay(auditionWaveSlot, sm_AllowAuditionSoundToLoad, 3000);
	}
}

void audNorthAudioEngine::DrawBufferedLevelMeters()
{
	if(sm_CurrentMeterIndex > 0)
	{		
		PUSH_DEFAULT_SCREEN();

		audDriver::SetDebugDrawRenderStates();

		for(u32 ml = 0; ml < sm_CurrentMeterIndex; ml++)
		{
			const float elemWidth = sm_MeterList[ml]->width / (float)sm_MeterList[ml]->numValues;

			// draw alpha'd background
			grcBegin(drawTriStrip, 4);
				grcColor(sm_MeterList[ml]->bgColour);
				grcVertex2f(sm_MeterList[ml]->left,sm_MeterList[ml]->bottom);
				grcVertex2f(sm_MeterList[ml]->left,sm_MeterList[ml]->bottom-sm_MeterList[ml]->height);
				grcVertex2f(sm_MeterList[ml]->left+sm_MeterList[ml]->width,sm_MeterList[ml]->bottom);
				grcVertex2f(sm_MeterList[ml]->left+sm_MeterList[ml]->width,sm_MeterList[ml]->bottom-sm_MeterList[ml]->height);
			grcEnd();

			for(u32 i = 0; i < sm_MeterList[ml]->numValues; i++)
			{
				const f32 x = sm_MeterList[ml]->left + (elemWidth * (float)i);
				const f32 value = Clamp(sm_MeterList[ml]->values[i] / sm_MeterList[ml]->rangeMax, 0.f, 1.f);
				const f32 y = sm_MeterList[ml]->bottom - (value * sm_MeterList[ml]->height);
				grcBegin(drawTriStrip, 4);
					grcColor(sm_MeterList[ml]->bottomColour);
					grcVertex2f(x, sm_MeterList[ml]->bottom);
					grcColor(sm_MeterList[ml]->topColour);
					grcVertex2f(x, y);
					grcColor(sm_MeterList[ml]->bottomColour);
					grcVertex2f(x + elemWidth, sm_MeterList[ml]->bottom);
					grcColor(sm_MeterList[ml]->topColour);
					grcVertex2f(x + elemWidth, y);
				grcEnd();

				grcBegin(drawLineStrip, 2);
					grcColor(sm_MeterList[ml]->bgColour);
					grcVertex2f(x, sm_MeterList[ml]->bottom);
					grcVertex2f(x, y);
				grcEnd();

				grcColor(sm_MeterList[ml]->textColour);
				grcDraw2dText(x, sm_MeterList[ml]->bottom + 10.f, sm_MeterList[ml]->names[i], true);

				if(sm_MeterList[ml]->drawValues)
				{
					char valueString[32];
					formatf(valueString, "%.02f", sm_MeterList[ml]->values[i]);
					grcDraw2dText(x, sm_MeterList[ml]->bottom + 20.f, valueString, true);
				}
			}

			if(sm_MeterList[ml]->title)
			{
				grcDraw2dText(sm_MeterList[ml]->left, sm_MeterList[ml]->bottom - sm_MeterList[ml]->height - 20.0f, sm_MeterList[ml]->title, true);
			}
		}

		sm_CurrentMeterIndex = 0;
		POP_DEFAULT_SCREEN();
	}
}

void audNorthAudioEngine::TriggerSound()
{
	audSoundInitParams initParams;
	initParams.RemoveHierarchy = sm_ShouldRemoveSoundHierarchy;
	initParams.WaveSlot = audWaveSlot::FindWaveSlot(g_AuditionWaveSlotName);
	initParams.AllowLoad = true;
	initParams.PrepareTimeLimit = 3000;
	initParams.IsAuditioning = true;
	if(sm_ShouldAuditionThroughPlayer)
	{
		initParams.Tracker = (FindPlayerPed()->GetPlaceableTracker());
		initParams.EnvironmentGroup = FindPlayerPed()->GetPedAudioEntity()->GetEnvironmentGroup(true);
	}

	if(sm_DebugSound)
	{
		sm_DebugSound->StopAndForget();
	}

	g_FrontendAudioEntity.CreateSound_PersistentReference(sm_SoundName, &sm_DebugSound, &initParams);
	if(sm_DebugSound)
	{
		sm_DebugSound->PrepareAndPlay();
	}
	else
	{
		naDisplayf("Couldn't play sound %s", sm_SoundName);
	}
}

void audNorthAudioEngine::StopSound()
{
	if(sm_DebugSound)
	{
		sm_DebugSound->StopAndForget();
	}
}

void audNorthAudioEngine::AuditionWaveSlotChanged()
{
	/*audWaveSlot* waveSlot = NULL;
	waveSlot = audWaveSlot::FindWaveSlot(g_AuditionWaveSlotName);
	g_AudioEngine.SetAuditionWaveSlot(waveSlot);*/
}

extern bank_bool g_DisableExplosionSFX;

void audNorthAudioEngine::InitWidgets()
{
	if (PARAM_audiodesigner.Get())
	{
		BANKMGR.CreateOutputWindow("Audio", "NamedColor:Black"); 
	}

	sprintf(g_AuditionWaveSlotName, "AMBIENT_SCRIPT_SLOT_0");
	AuditionWaveSlotChanged();

	if(PARAM_audiowidgets.Get())
	{
		CreateWidgets();
	}
	else
	{
		bkBank *bankPtr = BANKMGR.FindBank("Audio");
		if(bankPtr)
		{
			bankPtr->Destroy();
		}
		bkBank& bank = BANKMGR.CreateBank("Audio");

		bank.AddButton("Create Audio Widgets", CFA(CreateWidgets));
	}

}

void audNorthAudioEngine::StopSoundFromWidget()
{
	audSound **auditionSoundPtr = g_AudioEngine.GetRemoteControl().GetAuditionSoundPtr();

	if(*auditionSoundPtr)
	{
		(*auditionSoundPtr)->StopAndForget();
	}
}

void audNorthAudioEngine::TriggerSoundFromWidget()
{
	audMetadataObjectInfo info;
	audSoundSet soundSet;

	soundSet.Init(sm_AuditionSoundSetName);
	if(soundSet.IsInitialised())
	{
		AuditionSound(soundSet.Find(sm_AuditionSoundName));
	}
	else
	{
		if(SOUNDFACTORY.GetMetadataManager().GetObjectInfo(atStringHash(sm_AuditionSoundName), info))
		{
			if(gSoundsIsOfType(info.GetType(), Sound::TYPE_ID))
			{
				AuditionSound(atStringHash(sm_AuditionSoundName));
			}
		}
	}

	if(sm_ShouldAuditionSoundOverNetwork)
	{
		Vec3V position = Vec3V(V_ZERO);
		if(CEntity *auditioningEntity = sm_ShouldAuditionThroughPlayer ? FindPlayerPed() : CDebugScene::FocusEntities_Get(0))
		{
			position = auditioningEntity->GetTransform().GetPosition();
		}
		
		CGameScriptId scriptId;
		CPlaySoundEvent::Trigger(VEC3V_TO_VECTOR3(position), atStringHash(sm_AuditionSoundSetName), atStringHash(sm_AuditionSoundName), 0xff, scriptId, 50);
	}
}

void ActivateSlowMoCB()
{
	audNorthAudioEngine::ActivateSlowMoMode(atStringHash(g_SlowMoModeName));
}

void DeactivateSlowMoCB()
{
	audNorthAudioEngine::DeactivateSlowMoMode(atStringHash(g_SlowMoModeName));
}

u32 g_CutsceneSkipTime = 0;


void SkipCutsceneCB()
{
	g_CutsceneAudioEntity.SkipCutsceneToTime(g_CutsceneSkipTime);
}

void TriggerCutsceneCB()
{
	g_CutsceneAudioEntity.TriggerCutscene();
}

void EnableMPSpeechExclusionCB()
{
	audDisplayf("Enabling NFG speech exclusion");
	audSpeechSound::SetExclusionPrefix("NFG");
}

void DisableMPSpeechExclusionCB()
{
	audDisplayf("Disabling speech exclusion");
	audSpeechSound::SetExclusionPrefix(NULL);
}

void StopCutsceneAudioCB()
{
	g_CutsceneAudioEntity.StopOverunAudio();
}

void audNorthAudioEngine::CreateWidgets()
{

	bkBank *bankPtr = BANKMGR.FindBank("Audio");
	if(bankPtr)
	{
		bankPtr->Destroy();
	}
	bkBank& bank = BANKMGR.CreateBank("Audio");

#if RSG_PS3 || RSG_ORBIS
	bank.AddToggle("PulseHeadsetSupport", &sm_ShouldTriggerPulseHeadset);
#endif

	bank.AddToggle("g_UseInteriorCarFilter", &g_UseInteriorCarFilter);

#if RSG_PC
	bank.AddToggle("Show Audio Frame Debug Info", &g_ShowAudioFrameDebugInfo);
	bank.AddSlider("Number of mix buffers", &g_NumberOfMixBuffers, 8, 16, 1);

	bank.AddToggle("Override CPU Limited Audio Setting ", &g_OverrideCPULimitedAudioSetting);
	bank.AddToggle("Force Min Spec Mode", &g_ForceMinSpecMode);
#endif	

	bank.AddButton("EnableSpeechExcl", CFA(EnableMPSpeechExclusionCB));
	bank.AddButton("DisableSpeechExcl", CFA(DisableMPSpeechExclusionCB));

	bank.AddToggle("StrVisMarkers", &sm_ShouldAddStrVisMarkers);
	bank.AddSlider("Cutscene time offset ms", &g_CutsceneTimeOffset, 0, 1000, 1);
	bank.AddSlider("Cutscene time scale", &g_CutsceneTimeScale, 0.0001f, 2.f, 0.1f);
	bank.AddButton("Stop Cutscene Audio", CFA(StopCutsceneAudioCB));
	bank.AddToggle("Use game time for cutscenes", &g_GameTimerCutscenes);

	bank.AddToggle("IgnoreFrontendVolumeSliders", &g_IgnoreFrontendVolumeSliders);
	DEV_ONLY(bank.AddToggle("AssertOnSoundPoolFull", &g_AssertOnSoundPoolFull);)
	DEV_ONLY(bank.AddToggle("PrintSoundPoolWhenFull", &g_PrintSoundPoolWhenFull);)
	bank.AddToggle("Warn on missing sounds", &g_WarnOnMissingSounds);
	bank.AddToggle("Warn on missing curves", &g_WarnOnMissingCurves);
	bank.AddToggle("Mute audio", &sm_ShouldMuteAudio);
	bank.AddToggle("Mute wind", &g_ShouldMuteWind);
	bank.AddToggle("DisableExplosionSFX", &g_DisableExplosionSFX);
	bank.AddToggle("Force SlowMo Video Editor", &sm_ForceSlowMoVideoEditor);	
	bank.AddToggle("Force Super SlowMo Video Editor", &sm_ForceSuperSlowMoVideoEditor);	
	bank.AddSlider("Slow Mo Camera Blend Threshold", &sm_ThirdPersonCameraBlendThreshold, 0.0f, 1.f, 0.01f);
	bank.AddToggle("Force Allow Radio Over Screen Fade", &g_ForceAllowRadioOverScreenFade);		

	bank.PushGroup("Sound Auditioning", true);
		bank.AddToggle("Audition through player entity", &sm_ShouldAuditionThroughPlayer);
		bank.AddToggle("Audition through focus entity", &sm_ShouldAuditionThroughFocusEntity);
		bank.AddToggle("Audition panned frontend", &sm_ShouldAuditionPannedFrontend);
		bank.AddToggle("Audition over network", &sm_ShouldAuditionSoundOverNetwork);
		bank.AddText("AuditionWaveSlot", &g_AuditionWaveSlotName[0], sizeof(g_AuditionWaveSlotName),false, &audNorthAudioEngine::AuditionWaveSlotChanged);

		bank.AddToggle("AuditionSoundsOnPPU", &sm_AuditionSoundsOnPPU);
		bank.AddToggle("AuditionSoundUnpausable", &sm_AuditionSoundUnpausable);
		bank.AddToggle("AllowAuditionSoundToLoad", &sm_AllowAuditionSoundToLoad);
		audRemoteControl::AddWidgets(bank);
		const char *nameList[] =
		{
			"Nothing",
			"Everything",
			"HierarchyOnly",
			"NoDynamicChildren",
			"VariablesOnly"
		};
		bank.AddCombo("AuditionSoundDrawMode", (s32*)&sm_AuditionSoundDrawMode, audRemoteControl::kNumDrawModes, nameList, 0);
		bank.AddSlider("VerticalScroll", &sm_DebugDrawYOffset, 0.f, 2000.f, 1.f);
	
		bank.AddText("SoundSet Name", sm_AuditionSoundSetName, sizeof(sm_AuditionSoundSetName), false);
		bank.AddText("SoundName", sm_AuditionSoundName, sizeof(sm_AuditionSoundName), false);
		bank.AddButton("Play Sound", CFA(TriggerSoundFromWidget));
		bank.AddButton("Stop Sound", CFA(StopSoundFromWidget));
	
	bank.PopGroup();

	audSoundManager::AddSoundDebuggingWidgets(bank);
	
	bank.AddToggle("Use Edited Cutscenes", &g_UseEditedCutscenes);
	bank.AddToggle("Use Master Cutscenes", &g_UseMasterCutscenes);
	bank.AddSlider("Cutscene skip time", &g_CutsceneSkipTime, 0, 90000, 10);
	bank.AddButton("Skip Cutscene", CFA(SkipCutsceneCB));
	bank.AddButton("Trigger Cutscene", CFA(TriggerCutsceneCB));
	bank.AddToggle("Debug playing script sounds", &g_DebugPlayingScriptSounds);
	bank.PushGroup("audNorthAudioEngine", false);
		bank.AddText("Sound Name", &sm_SoundName[0], sizeof(sm_SoundName),false);
		bank.AddToggle("Remove sound hierarchy", &sm_ShouldRemoveSoundHierarchy);
		bank.AddButton("Play Sound", CFA(TriggerSound));
		bank.AddButton("Stop Sound", CFA(StopSound));

		bank.AddText("SloMoModeName", g_SlowMoModeName, sizeof(g_SlowMoModeName), false);
		bank.AddButton("Activate SlowMo Mode", CFA(ActivateSlowMoCB));
		bank.AddButton("Deactivate SlowMo Mode", CFA(DeactivateSlowMoCB));
		bank.AddToggle("Display SloMo", &g_DisplaySlowMo);

#if NA_RADIO_ENABLED
		bank.AddSlider("Radio ducking volume",&g_RadioDuckingVolume, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("Radio ducking volume stereo",&g_RadioDuckingVolumeStereo, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("g_RadioDuckingVolumeVoiceChat",&g_RadioDuckingVolumeVoiceChat, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("Talk Radio ducking volume",&g_TalkRadioDuckingVolume, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("Radio gps ducking volume",&g_RadioGPSDuckingVolume, -100.0f, 0.0f, 0.1f);
		bank.AddToggle("Duck radio", &sm_ShouldDuckRadio);
#endif
		bank.AddSlider("Radio ducking smooth rate",&g_RadioDuckingSmoothRate, 0.0f, 100.0f, 0.1f);

		bank.AddToggle("ThreadedAudioUpdate", &sm_RunUpdateInSeperateThread);

		bank.AddToggle("g_MuteGameWorldAudio", &g_MuteGameWorldAudio);
		bank.AddToggle("g_MutePositionedRadio", &g_MutePositionedRadio);

		bank.AddSlider("g_FadeValueForSilence",&g_FadeValueForSilence, 0.05f, 1.0f, 0.05f);
		bank.AddToggle("g_DebugFadeLevels", &g_DebugFadeLevels);

		bank.AddSlider("g_CutsceneLeakageSmootherRate", &g_CutsceneLeakageSmootherRate, 0.0f, 1.0f, 0.0001f);
		bank.AddSlider("g_CutscenePositionedLeakage",&g_CutscenePositionedLeakage, 0.0f, 1.0f, 0.05f);
		bank.AddToggle("g_LeakForRPICutscenes", &g_LeakForRPICutscenes);
		bank.AddToggle("g_DebugCutsceneLeakage", &g_DebugCutsceneLeakage);

		bank.AddSlider("g_AttachedToTrainVolume",&g_AttachedToTrainVolume, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("SlowMoTrainVol",&g_SlowMoTrainVol, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("g_ConversationTrainVol",&g_ConversationTrainVol, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("g_ConversationHeliVol",&g_ConversationHeliVol, -100.0f, 0.0f, 0.1f);
		bank.AddSlider("g_TrainRolloffInSubways",&g_TrainRolloffInSubways, 1.0f, 10.0f, 0.1f);

		bank.PushGroup("Ambisonics");
		bank.AddButton("Save decoder", CFA(AmbisonicSave));
		bank.AddButton("Load decoder", CFA(AmbisonicLoad));
		bank.PopGroup();

		bank.PushGroup("Category Mutes");
		bank.AddButton("Mute ambience", CFA(ToggleMuteAmbience));
			bank.AddButton("Mute cutscene", datCallback(ToggleMuteCutscene));
			bank.AddButton("Mute frontend", datCallback(ToggleMuteFrontend));
			bank.AddButton("Mute music", datCallback(ToggleMuteMusic));
			bank.AddButton("Mute sfx", datCallback(ToggleMuteSfx));
			bank.AddButton("Mute speech", datCallback(ToggleMuteSpeech));
			bank.AddButton("Mute guns", datCallback(ToggleMuteGuns));
			bank.AddButton("Mute vehicles", datCallback(ToggleMuteVehicles));
			bank.PopGroup();
bank.PopGroup();

	bank.PushGroup("RAGE Audio", false);
		g_AudioEngine.AddWidgets(bank);
	bank.PopGroup();

#if USE_GUN_TAILS
	audGunFireAudioEntity::AddWidgets(bank);
#endif
#if USE_CONDUCTORS
	audSuperConductor::AddWidgets(bank);
#endif
	audObjectAudioEntity::AddWidgets(bank);
	audExplosionAudioEntity::AddWidgets(bank);
	audFireAudioEntity::AddWidgets(bank);
	audDoorAudioEntity::AddWidgets(bank);
	audVehicleAudioEntity::AddWidgets(bank);
	audCarAudioEntity::AddWidgets(bank);
	audHeliAudioEntity::AddWidgets(bank);
	audTrainAudioEntity::AddWidgets(bank);
	audPlaneAudioEntity::AddWidgets(bank);
	audBoatAudioEntity::AddWidgets(bank);
	audBicycleAudioEntity::AddWidgets(bank);
	audTrailerAudioEntity::AddWidgets(bank);
	audPedAudioEntity::AddWidgets(bank);
	audSpeechAudioEntity::AddWidgets(bank);
	audScriptAudioEntity::AddWidgets(bank);
	bank.PushGroup("Environment", false);
		bank.AddSlider("PIVMusicVolumeFadeInS", &g_PIVMusicVolumeFadeInS, 0.f, 30.f, 0.01f);
		bank.AddSlider("PIVMusicVolumeFadeOutS", &g_PIVMusicVolumeFadeOutS, 0.f, 30.f, 0.01f);
		bank.AddSlider("PIVOpennessFadeInS", &g_PIVOpennessFadeInS, 0.f, 30.f, 0.01f);
		bank.AddSlider("PIVOpennessFadeOutS", &g_PIVOpennessFadeOutS, 0.f, 30.f, 0.01f);

		naEnvironment::AddWidgets(bank);
		naEnvironmentGroupManager::AddWidgets(bank);
		naEnvironmentGroup::AddWidgets(bank);
		naOcclusionManager::AddWidgets(bank);
		naMicrophones::AddWidgets(bank);
	bank.PopGroup();
	audGlassAudioEntity::AddWidgets(bank);
	audVehicleFireAudio::AddWidgets(bank);
	NA_RADIO_ENABLED_ONLY(audRadioAudioEntity::AddWidgets(bank));
	audCollisionAudioEntity::AddWidgets(bank);
	audEmitterAudioEntity::AddWidgets(bank);
	audWeaponAudioEntity::AddWidgets(bank);
	audWeatherAudioEntity::AddWidgets(bank);
	audFrontendAudioEntity::AddWidgets(bank);
#if GTA_REPLAY
	audReplayAudioEntity::AddWidgets(bank);
#endif
	NA_POLICESCANNER_ENABLED_ONLY(g_AudioScannerManager.AddWidgets(bank));
	g_AmbientAudioEntity.AddWidgets(bank);
	g_ReflectionsAudioEntity.AddWidgets(bank);
	g_InteractiveMusicManager.AddWidgets(bank);
	AUDCATEGORYCONTROLLERMANAGER.AddWidgets(bank);
	sm_DynamicMixer.AddWidgets(bank);
//	NA_RADIO_ENABLED_ONLY(CRadioHud::InitWidgets());
}


void audNorthAudioEngine::DebugDrawSlowMo() 
{

	if(!g_DisplaySlowMo)
	{
		return;
	}

	PUSH_DEFAULT_SCREEN();


	audNorthDebugDrawManager drawMgr(100.f, 50.f, 50.f, 720.f);

	Color32 prevColor = grcFont::GetCurrent().GetInternalColor();
	bool bOldLighting = grcLighting( false );

	grcFont::GetCurrent().SetInternalColor( Color32( 250, 250, 250 ) );

	char sectionHeader[128];
	formatf(sectionHeader, "Active SlowMo Modes");
	drawMgr.PushSection(sectionHeader);



	for(int i=0; i<NUM_SLOWMOTYPE; i++)
	{
		grcFont::GetCurrent().SetInternalColor( Color32( 250, 250, 250 ) );

		const char *slowMoName = GetMetadataManager().GetObjectName(sm_ActiveSlowMoModes[i]);

		formatf(sectionHeader, "%s: %s", SlowMoType_ToString((SlowMoType)i), slowMoName);
		drawMgr.DrawLine(sectionHeader);
	}

	drawMgr.PopSection();

#if __USEDEBUGAUDIO
	formatf(sectionHeader, "Current SlowMo Mode");
	drawMgr.PushSection(sectionHeader);
	formatf(sectionHeader, "%s: %s : %s", SlowMoType_ToString(sm_SlowMoMode), sm_SlowMoScene ? DYNAMICMIXMGR.GetMetadataManager().GetObjectNameFromNameTableOffset(sm_SlowMoScene->GetSceneSettings()->NameTableOffset): NULL, sm_SlowMoSound ? g_AudioEngine.GetSoundManager().GetFactory().GetMetadataManager().GetObjectNameFromNameTableOffset(sm_SlowMoSound->GetNameTableOffset()): NULL);

	drawMgr.DrawLine(sectionHeader);

	drawMgr.PopSection();
#endif

	grcFont::GetCurrent().SetInternalColor( prevColor );
	grcLighting(bOldLighting);

	POP_DEFAULT_SCREEN();
}
#endif


void audGameObjectManager::OnObjectViewed(const u32 BANK_ONLY(nameHash))
{
#if __BANK
	audMetadataObjectInfo info;
	if(audNorthAudioEngine::GetMetadataManager().GetObjectInfo(nameHash,info))
	{
		if(info.GetType() == ModelAudioCollisionSettings::TYPE_ID)
		{
			ModelAudioCollisionSettings * macs = audNorthAudioEngine::GetObject<ModelAudioCollisionSettings>(nameHash);
			if(macs)
			{
				g_CollisionAudioEntity.HandleRaveSelectedMaterial(macs);
			}
		}
		else if(info.GetType() == StaticEmitter::TYPE_ID)
		{
			g_RequestedWarpEmitter = info.GetObject<StaticEmitter>();			
		}
		else if(g_WarpToInterior && info.GetType() == InteriorSettings::TYPE_ID)
		{
			CInteriorProxy::Pool* proxyPool = CInteriorProxy::GetPool();
			if(proxyPool)
			{
				for(int i =0; i < proxyPool->GetSize(); i++)
				{
					CInteriorProxy* pProxy = proxyPool->GetSlot(i);
					if(pProxy && pProxy->GetNameHash() == nameHash)
					{
						g_RequestedWarpInterior = pProxy;			
					}
				}
			}
		}
		else if(info.GetType() == AmbientZone::TYPE_ID)
		{
			g_RequestedWarpAmbZone = info.GetObject<AmbientZone>();			
		}
	}
#endif
}

void audGameObjectManager::OnObjectOverridden(const u32 BANK_ONLY(nameHash))
{
#if __BANK
	audMetadataObjectInfo info;
	if(audNorthAudioEngine::GetMetadataManager().GetObjectInfo(nameHash,info))
	{
		if(info.GetType() == AmbientZone::TYPE_ID)
		{
			const AmbientZone* ambientZone = info.GetObject<AmbientZone>();
			g_AmbientAudioEntity.HandleRaveZoneCreatedNotification(const_cast<AmbientZone*>(ambientZone));
		}
		else if(info.GetType() == StaticEmitter::TYPE_ID)
		{
			g_EmitterAudioEntity.HandleRaveStaticEmitterCreatedNotification(nameHash);
		}
		else if (info.GetType() == ShoreLinePoolAudioSettings::TYPE_ID)
		{
			ShoreLinePoolAudioSettings* settings = audNorthAudioEngine::GetObject<ShoreLinePoolAudioSettings>(nameHash);
			g_AmbientAudioEntity.HandleRaveShoreLineCreatedNotification(static_cast<ShoreLineAudioSettings*>(settings));
		}
		else if (info.GetType() == ShoreLineRiverAudioSettings::TYPE_ID)
		{
			ShoreLineRiverAudioSettings* settings = audNorthAudioEngine::GetObject<ShoreLineRiverAudioSettings>(nameHash);
			g_AmbientAudioEntity.HandleRaveShoreLineCreatedNotification(static_cast<ShoreLineAudioSettings*>(settings));
		}
		else if (info.GetType() == ShoreLineOceanAudioSettings::TYPE_ID)
		{
			ShoreLineOceanAudioSettings* settings = audNorthAudioEngine::GetObject<ShoreLineOceanAudioSettings>(nameHash);
			g_AmbientAudioEntity.HandleRaveShoreLineCreatedNotification(static_cast<ShoreLineAudioSettings*>(settings));
		}
		else if (info.GetType() == ShoreLineLakeAudioSettings::TYPE_ID)
		{
			ShoreLineLakeAudioSettings* settings = audNorthAudioEngine::GetObject<ShoreLineLakeAudioSettings>(nameHash);
			g_AmbientAudioEntity.HandleRaveShoreLineCreatedNotification(static_cast<ShoreLineAudioSettings*>(settings));
		}
	}
	OnObjectEdit(nameHash);
#endif
}

void audGameObjectManager::OnObjectModified(const u32 BANK_ONLY(nameHash))
{
#if __BANK
	OnObjectEdit(nameHash);
#endif
}

void audGameObjectManager::OnObjectEdit(const u32 BANK_ONLY(nameHash))
{
#if __BANK
	audMetadataObjectInfo info;
	if(audNorthAudioEngine::GetMetadataManager().GetObjectInfo(nameHash,info))
	{
		if(info.GetType() == VehicleCollisionSettings::TYPE_ID)
		{
			audVehicleCollisionAudio::UpdateCollisionSettings();
		}
		else if(info.GetType() == CollisionMaterialSettings::TYPE_ID)
		{
			audVehicleCollisionAudio::UpdateCollisionSettings();
		}
		else if (gGameObjectsIsOfType(info.GetType(),ShoreLineAudioSettings::TYPE_ID))
		{
			g_AmbientAudioEntity.HandleRaveShoreLineEditedNotification();
		}
	}
#endif
}

void audGameObjectManager::OnObjectAuditionStart(const u32 BANK_ONLY(nameHash))
{
#if __BANK
	audMetadataObjectInfo info;
	audNorthAudioEngine::GetMetadataManager().GetObjectInfo(nameHash,info);
	if(gGameObjectsIsOfType(info.GetType(), MusicAction::TYPE_ID) || info.GetType() == MusicEvent::TYPE_ID)
	{
		g_InteractiveMusicManager.GetEventManager().TriggerEvent(info.GetName(), nameHash);
	}
#endif
}


void naAnimHandler::HandleEvent(const audAnimEvent &event,fwEntity *entity)
{
	CEntity *pEnt = (CEntity* ) entity;
	if(pEnt->GetIsTypeObject())
	{
		CObject *pObject = (CObject *)entity;
		if(pObject)
		{
			CWeapon *pWeapon = pObject->GetWeapon();
			if(pWeapon)
			{
				g_WeaponAudioEntity.AddWeaponAnimEvent(event,pObject);
			}
			else
			{
				g_EmitterAudioEntity.AddBuildingAnimEvent(event.hash, pEnt->GetTransform().GetPosition());
			}
		}
	
	}
	else if(pEnt->GetIsTypeBuilding())
	{
		g_EmitterAudioEntity.AddBuildingAnimEvent(event.hash, pEnt->GetTransform().GetPosition());
	}
}


