code:
#include "common.h"

#include "AudioScriptObject.h"
#include "Pools.h"
#include "DMAudio.h"
#include "SaveBuf.h"

cAudioScriptObject::cAudioScriptObject()
{
	Reset();
};

cAudioScriptObject::~cAudioScriptObject()
{
	Reset();
};

void
cAudioScriptObject::Reset()
{
	AudioId = SCRIPT_SOUND_INVALID;
	Posn = CVector(0.0f, 0.0f, 0.0f);
	AudioEntity = AEHANDLE_NONE;
}

void *
cAudioScriptObject::operator new(size_t sz) throw()
{
	return CPools::GetAudioScriptObjectPool()->New();
}

void *
cAudioScriptObject::operator new(size_t sz, int handle) throw()
{
	return CPools::GetAudioScriptObjectPool()->New(handle);
}

void
cAudioScriptObject::operator delete(void *p, size_t sz) throw()
{
	CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p);
}

void
cAudioScriptObject::operator delete(void *p, int handle) throw()
{
	CPools::GetAudioScriptObjectPool()->Delete((cAudioScriptObject *)p);
}

void
cAudioScriptObject::LoadAllAudioScriptObjects(uint8 *buf, uint32 size)
{
	INITSAVEBUF

	CheckSaveHeader(buf, 'A', 'U', 'D', '\0', size - SAVE_HEADER_SIZE);

	int32 pool_size;
	ReadSaveBuf(&pool_size, buf);
	for (int32 i = 0; i < pool_size; i++) {
		int32 handle;
		ReadSaveBuf(&handle, buf);
		cAudioScriptObject *p = new(handle) cAudioScriptObject;
		assert(p != nil);
		ReadSaveBuf(p, buf);
		p->AudioEntity = DMAudio.CreateLoopingScriptObject(p);
	}

	VALIDATESAVEBUF(size);
}

void
cAudioScriptObject::SaveAllAudioScriptObjects(uint8 *buf, uint32 *size)
{
	INITSAVEBUF

	int32 pool_size = CPools::GetAudioScriptObjectPool()->GetNoOfUsedSpaces();
	*size = SAVE_HEADER_SIZE + sizeof(int32) + pool_size * (sizeof(cAudioScriptObject) + sizeof(int32));
	WriteSaveHeader(buf, 'A', 'U', 'D', '\0', *size - SAVE_HEADER_SIZE);
	WriteSaveBuf(buf, pool_size);

	int32 i = CPools::GetAudioScriptObjectPool()->GetSize();
	while (i--) {
		cAudioScriptObject *p = CPools::GetAudioScriptObjectPool()->GetSlot(i);
		if (p != nil) {
			WriteSaveBuf(buf, CPools::GetAudioScriptObjectPool()->GetIndex(p));
			WriteSaveBuf(buf, *p);
		}
	}

	VALIDATESAVEBUF(*size);
}

void
PlayOneShotScriptObject(uint8 id, CVector const &pos)
{
	cAudioScriptObject *audioScriptObject = new cAudioScriptObject();
	audioScriptObject->Posn = pos;
	audioScriptObject->AudioId = id;
	audioScriptObject->AudioEntity = AEHANDLE_NONE;
	DMAudio.CreateOneShotScriptObject(audioScriptObject);
}

license: mit


code:
#pragma once

class cAudioScriptObject
{
public:
	int16 AudioId;
	CVector Posn;
	int32 AudioEntity;

	cAudioScriptObject();
	~cAudioScriptObject();

	void Reset(); /// ok

	static void* operator new(size_t) throw();
	static void* operator new(size_t, int) throw();
	static void operator delete(void*, size_t) throw();
	static void operator delete(void*, int) throw();

	static void LoadAllAudioScriptObjects(uint8 *buf, uint32 size);
	static void SaveAllAudioScriptObjects(uint8 *buf, uint32 *size);
};

VALIDATE_SIZE(cAudioScriptObject, 20);

extern void PlayOneShotScriptObject(uint8 id, CVector const &pos);


code:
#include "common.h"

#include "DMAudio.h"
#include "MusicManager.h"
#include "AudioManager.h"
#include "AudioScriptObject.h"
#include "sampman.h"

cDMAudio DMAudio;

void
cDMAudio::Initialise(void)
{
	AudioManager.Initialise();
}

void
cDMAudio::Terminate(void)
{
	AudioManager.Terminate();
}

void
cDMAudio::Service(void)
{
	AudioManager.Service();
}

int32
cDMAudio::CreateEntity(eAudioType type, void *UID)
{
	return AudioManager.CreateEntity(type, (CPhysical *)UID);
}

void
cDMAudio::DestroyEntity(int32 audioEntity)
{
	AudioManager.DestroyEntity(audioEntity);
}

bool8
cDMAudio::GetEntityStatus(int32 audioEntity)
{
	return AudioManager.GetEntityStatus(audioEntity);
}

void
cDMAudio::SetEntityStatus(int32 audioEntity, bool8 status)
{
	AudioManager.SetEntityStatus(audioEntity, status);
}

void
cDMAudio::PlayOneShot(int32 audioEntity, uint16 oneShot, float volume)
{
	AudioManager.PlayOneShot(audioEntity, oneShot, volume);
}

void
cDMAudio::DestroyAllGameCreatedEntities(void)
{
	AudioManager.DestroyAllGameCreatedEntities();
}

void
cDMAudio::SetMonoMode(bool8 mono)
{
	AudioManager.SetMonoMode(mono);
}

void
cDMAudio::SetEffectsMasterVolume(uint8 volume)
{
	uint8 vol = volume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

	AudioManager.SetEffectsMasterVolume(vol);
}

void
cDMAudio::SetMusicMasterVolume(uint8 volume)
{
	uint8 vol = volume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

	AudioManager.SetMusicMasterVolume(vol);
}

void
cDMAudio::SetEffectsFadeVol(uint8 volume)
{
	uint8 vol = volume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

	AudioManager.SetEffectsFadeVol(vol);
}

void
cDMAudio::SetMusicFadeVol(uint8 volume)
{
	uint8 vol = volume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

	AudioManager.SetMusicFadeVol(vol);
}

uint8
cDMAudio::GetNum3DProvidersAvailable(void)
{
	return AudioManager.GetNum3DProvidersAvailable();
}

char *
cDMAudio::Get3DProviderName(uint8 id)
{
	return AudioManager.Get3DProviderName(id);
}

int8
cDMAudio::GetCurrent3DProviderIndex(void)
{
	return AudioManager.GetCurrent3DProviderIndex();
}

int8
cDMAudio::SetCurrent3DProvider(uint8 which)
{
	return AudioManager.SetCurrent3DProvider(which);
}

void
cDMAudio::SetSpeakerConfig(int32 config)
{
	AudioManager.SetSpeakerConfig(config);
}

bool8
cDMAudio::IsMP3RadioChannelAvailable(void)
{
	return AudioManager.IsMP3RadioChannelAvailable();
}

void
cDMAudio::ReleaseDigitalHandle(void)
{
	AudioManager.ReleaseDigitalHandle();
}

void
cDMAudio::ReacquireDigitalHandle(void)
{
	AudioManager.ReacquireDigitalHandle();
}

void
cDMAudio::SetDynamicAcousticModelingStatus(bool8 status)
{
#ifdef AUDIO_REFLECTIONS
	AudioManager.SetDynamicAcousticModelingStatus(status);
#endif
}

bool8
cDMAudio::CheckForAnAudioFileOnCD(void)
{
	return AudioManager.CheckForAnAudioFileOnCD();
}

char
cDMAudio::GetCDAudioDriveLetter(void)
{
	return AudioManager.GetCDAudioDriveLetter();
}

bool8
cDMAudio::IsAudioInitialised(void)
{
	return AudioManager.IsAudioInitialised();
}

void
cDMAudio::ResetPoliceRadio()
{
	AudioManager.ResetPoliceRadio();
}

void
cDMAudio::ReportCrime(eCrimeType crime, const CVector &pos)
{
	AudioManager.ReportCrime(crime, pos);
}

int32
cDMAudio::CreateLoopingScriptObject(cAudioScriptObject *scriptObject)
{
	int32 audioEntity = AudioManager.CreateEntity(AUDIOTYPE_SCRIPTOBJECT, scriptObject);

	if ( AEHANDLE_IS_OK(audioEntity) )
		AudioManager.SetEntityStatus(audioEntity, TRUE);

	return audioEntity;
}

void
cDMAudio::DestroyLoopingScriptObject(int32 audioEntity)
{
	AudioManager.DestroyEntity(audioEntity);
}

void
cDMAudio::CreateOneShotScriptObject(cAudioScriptObject *scriptObject)
{
	int32 audioEntity = AudioManager.CreateEntity(AUDIOTYPE_SCRIPTOBJECT, scriptObject);

	if ( AEHANDLE_IS_OK(audioEntity) )
	{
		AudioManager.SetEntityStatus(audioEntity, TRUE);
		AudioManager.PlayOneShot(audioEntity, scriptObject->AudioId, 0.0f);
	}
}

void
cDMAudio::PlaySuspectLastSeen(float x, float y, float z)
{
	AudioManager.PlaySuspectLastSeen(x, y, z);
}

void
cDMAudio::ReportCollision(CEntity *entityA, CEntity *entityB, uint8 surfaceTypeA, uint8 surfaceTypeB, float collisionPower, float velocity)
{
	AudioManager.ReportCollision(entityA, entityB, surfaceTypeA, surfaceTypeB, collisionPower, velocity);
}

void
cDMAudio::PlayFrontEndSound(uint16 frontend, uint32 volume)
{
	AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, frontend, (float)volume);
}

void
cDMAudio::PlayRadioAnnouncement(uint8 announcement)
{
	MusicManager.PlayAnnouncement(announcement);
}

void
cDMAudio::PlayFrontEndTrack(uint8 track, bool8 frontendFlag)
{
	MusicManager.PlayFrontEndTrack(track, frontendFlag);
}

void
cDMAudio::StopFrontEndTrack(void)
{
	MusicManager.StopFrontEndTrack();
}

void
cDMAudio::ResetTimers(uint32 time)
{
	AudioManager.ResetTimers(time);
}

void
cDMAudio::ChangeMusicMode(uint8 mode)
{
	MusicManager.ChangeMusicMode(mode);
}

void
cDMAudio::PreloadCutSceneMusic(uint8 track)
{
	MusicManager.PreloadCutSceneMusic(track);
}

void
cDMAudio::PlayPreloadedCutSceneMusic(void)
{
	MusicManager.PlayPreloadedCutSceneMusic();
}

void
cDMAudio::StopCutSceneMusic(void)
{
	MusicManager.StopCutSceneMusic();
}

void
cDMAudio::PreloadMissionAudio(Const char *missionAudio)
{
	AudioManager.PreloadMissionAudio(missionAudio);
}

uint8
cDMAudio::GetMissionAudioLoadingStatus(void)
{
	return AudioManager.GetMissionAudioLoadingStatus();
}

void
cDMAudio::SetMissionAudioLocation(float x, float y, float z)
{
	AudioManager.SetMissionAudioLocation(x, y, z);
}

void
cDMAudio::PlayLoadedMissionAudio(void)
{
	AudioManager.PlayLoadedMissionAudio();
}

bool8
cDMAudio::IsMissionAudioSampleFinished(void)
{
	return AudioManager.IsMissionAudioSampleFinished();
}

void
cDMAudio::ClearMissionAudio(void)
{
	AudioManager.ClearMissionAudio();
}

uint8
cDMAudio::GetRadioInCar(void)
{
	return MusicManager.GetRadioInCar();
}

void
cDMAudio::SetRadioInCar(uint32 radio)
{
	MusicManager.SetRadioInCar(radio);
}

void
cDMAudio::SetRadioChannel(uint8 radio, int32 pos)
{
	MusicManager.SetRadioChannelByScript(radio, pos);
}


code:
#pragma once

#include "audio_enums.h"
#include "soundlist.h"
#include "Crime.h"

#define AEHANDLE_IS_FAILED(h) ((h)<0)
#define AEHANDLE_IS_OK(h)     ((h)>=0)

class cAudioScriptObject;
class CEntity;

class cDMAudio
{
public:
	~cDMAudio()
	{ }

	void Initialise(void);
	void Terminate(void);
	void Service(void);

	int32 CreateEntity(eAudioType type, void *UID);
	void DestroyEntity(int32 audioEntity);
	bool8 GetEntityStatus(int32 audioEntity);
	void SetEntityStatus(int32 audioEntity, bool8 status);
	void PlayOneShot(int32 audioEntity, uint16 oneShot, float volume);
	void DestroyAllGameCreatedEntities(void);

	void SetMonoMode(bool8 mono);
	void SetEffectsMasterVolume(uint8 volume);
	void SetMusicMasterVolume(uint8 volume);
	void SetEffectsFadeVol(uint8 volume);
	void SetMusicFadeVol(uint8 volume);

	uint8 GetNum3DProvidersAvailable(void);
	char *Get3DProviderName(uint8 id);

	int8 GetCurrent3DProviderIndex(void);
	int8 SetCurrent3DProvider(uint8 which);

	void SetSpeakerConfig(int32 config);

	bool8 IsMP3RadioChannelAvailable(void);

	void ReleaseDigitalHandle(void);
	void ReacquireDigitalHandle(void);

	void SetDynamicAcousticModelingStatus(bool8 status);

	bool8 CheckForAnAudioFileOnCD(void);

	char GetCDAudioDriveLetter(void);
	bool8 IsAudioInitialised(void);

	void ResetPoliceRadio();
	void ReportCrime(eCrimeType crime, CVector const &pos);

	int32 CreateLoopingScriptObject(cAudioScriptObject *scriptObject);
	void DestroyLoopingScriptObject(int32 audioEntity);
	void CreateOneShotScriptObject(cAudioScriptObject *scriptObject);

	void PlaySuspectLastSeen(float x, float y, float z);

	void ReportCollision(CEntity *entityA, CEntity *entityB, uint8 surfaceTypeA, uint8 surfaceTypeB, float collisionPower, float velocity);

	void PlayFrontEndSound(uint16 frontend, uint32 volume);
	void PlayRadioAnnouncement(uint8 announcement);
	void PlayFrontEndTrack(uint8 track, bool8 frontendFlag);
	void StopFrontEndTrack(void);

	void ResetTimers(uint32 time);

	void ChangeMusicMode(uint8 mode);

	void PreloadCutSceneMusic(uint8 track);
	void PlayPreloadedCutSceneMusic(void);
	void StopCutSceneMusic(void);

	void PreloadMissionAudio(Const char *missionAudio);
	uint8 GetMissionAudioLoadingStatus(void);
	void SetMissionAudioLocation(float x, float y, float z);
	void PlayLoadedMissionAudio(void);
	bool8 IsMissionAudioSampleFinished(void);
	void ClearMissionAudio(void);

	uint8 GetRadioInCar(void);
	void SetRadioInCar(uint32 radio);
	void SetRadioChannel(uint8 radio, int32 pos);
};
extern cDMAudio DMAudio;


code:
#include "common.h"
#include <time.h>
#include "soundlist.h"
#include "MusicManager.h"
#include "AudioManager.h"
#include "ControllerConfig.h"
#include "Camera.h"
#include "Font.h"
#include "Hud.h"
#include "ModelIndices.h"
#include "Replay.h"
#include "Pad.h"
#include "Text.h"
#include "Timer.h"
#include "World.h"
#include "sampman.h"

#if !defined FIX_BUGS && (defined RADIO_SCROLL_TO_PREV_STATION || defined RADIO_OFF_TEXT)
static_assert(false, "RADIO_SCROLL_TO_PREV_STATION and RADIO_OFF_TEXT won't work correctly without FIX_BUGS");
#endif

cMusicManager MusicManager;
int32 gNumRetunePresses;
int32 gRetuneCounter;
bool8 bHasStarted;

cMusicManager::cMusicManager()
{
	m_bIsInitialised = FALSE;
	m_bDisabled = FALSE;
	m_nMusicMode = MUSICMODE_DISABLED;
	m_nNextTrack = NO_TRACK;
	m_nPlayingTrack = NO_TRACK;
	m_bFrontendTrackFinished = FALSE;
	m_bPlayInFrontend = FALSE;
	m_bSetNextStation = FALSE;
	m_nAnnouncement = NO_TRACK;
	m_bPreviousPlayerInCar = FALSE;
	m_bPlayerInCar = FALSE;
	m_bAnnouncementInProgress = FALSE;
	m_bVerifyAmbienceTrackStartedToPlay = FALSE;
	bHasStarted = FALSE;
}

bool8
cMusicManager::PlayerInCar()
{
	if(!FindPlayerVehicle())
		return FALSE;

	int32 State = FindPlayerPed()->m_nPedState;

	if(State == PED_DRAG_FROM_CAR || State == PED_EXIT_CAR || State == PED_ARRESTED)
		return FALSE;

	if (!FindPlayerVehicle())
		return TRUE;

	if (FindPlayerVehicle()->GetStatus() == STATUS_WRECKED)
		return FALSE;

	switch (FindPlayerVehicle()->GetModelIndex()) {
	case MI_FIRETRUCK:
	case MI_AMBULAN:
	case MI_MRWHOOP:
	case MI_PREDATOR:
	case MI_TRAIN:
	case MI_SPEEDER:
	case MI_REEFER:
	case MI_GHOST: return FALSE;
	default: return TRUE;
	}
}

void
cMusicManager::DisplayRadioStationName()
{
	int8 pRetune;
	int8 gStreamedSound;
	int8 gRetuneCounter;
	static wchar *pCurrentStation = nil;
	static uint8 cDisplay = 0;

	if(!CTimer::GetIsPaused() && !TheCamera.m_WideScreenOn && PlayerInCar() &&
	   !CReplay::IsPlayingBack()) {
		if(m_bPlayerInCar && !m_bPreviousPlayerInCar)
			pCurrentStation = nil;

#ifdef FIX_BUGS
		const int curRadio = GetCarTuning();
#else
		const int curRadio = m_nNextTrack;
#endif

#ifdef RADIO_SCROLL_TO_PREV_STATION
		if(gNumRetunePresses < 0) {
			gStreamedSound = curRadio;

			gRetuneCounter = gNumRetunePresses;
			pRetune = gStreamedSound;

			while(gRetuneCounter < 0) {
				if(pRetune == HEAD_RADIO) {
					pRetune = RADIO_OFF;
				} else if(pRetune == RADIO_OFF || pRetune == NUM_RADIOS) {
					pRetune =  SampleManager.IsMP3RadioChannelAvailable() ? USERTRACK : USERTRACK - 1;
				} else
					pRetune--;

				++gRetuneCounter;
			}
		} else
#endif
		if(SampleManager.IsMP3RadioChannelAvailable()) {
			gStreamedSound = curRadio;

			if(gStreamedSound == STREAMED_SOUND_CITY_AMBIENT ||
			   gStreamedSound == STREAMED_SOUND_WATER_AMBIENT) { // which means OFF
				gStreamedSound = NUM_RADIOS;
			} else if(gStreamedSound > STREAMED_SOUND_RADIO_MP3_PLAYER)
				return;

			pRetune = gNumRetunePresses + gStreamedSound;

#ifdef FIX_BUGS
			while(pRetune > NUM_RADIOS)
				pRetune -= (NUM_RADIOS + 1);
#endif
			if(pRetune == NUM_RADIOS) {
				pRetune = RADIO_OFF;
			}
#ifndef FIX_BUGS
			else if(pRetune > NUM_RADIOS) {
				pRetune = pRetune - (NUM_RADIOS + 1);
			}
#endif
		} else {
			gStreamedSound = curRadio;
			pRetune = gNumRetunePresses + gStreamedSound;

			if(pRetune >= USERTRACK) {
				gRetuneCounter = gNumRetunePresses;
				pRetune = curRadio;

				if(gStreamedSound == STREAMED_SOUND_WATER_AMBIENT)
					pRetune = STREAMED_SOUND_CITY_AMBIENT; // which is RADIO_OFF

				while(gRetuneCounter) {
					if(pRetune == RADIO_OFF) {
						pRetune = HEAD_RADIO;
					} else if(pRetune < USERTRACK) {
						pRetune = pRetune + 1;
					}
					if(pRetune == USERTRACK) pRetune = RADIO_OFF;

					--gRetuneCounter;
				}
			}
		}

		wchar *string;

		switch(pRetune) {
		case HEAD_RADIO: string = TheText.Get("FEA_FM0"); break;
		case DOUBLE_CLEF: string = TheText.Get("FEA_FM1"); break;
		case JAH_RADIO: string = TheText.Get("FEA_FM2"); break;
		case RISE_FM: string = TheText.Get("FEA_FM3"); break;
		case LIPS_106: string = TheText.Get("FEA_FM4"); break;
		case GAME_FM: string = TheText.Get("FEA_FM5"); break;
		case MSX_FM: string = TheText.Get("FEA_FM6"); break;
		case FLASHBACK: string = TheText.Get("FEA_FM7"); break;
		case CHATTERBOX: string = TheText.Get("FEA_FM8"); break;
		case USERTRACK:
			if (!SampleManager.IsMP3RadioChannelAvailable())
				return;
			string = TheText.Get("FEA_FM9"); break;
#ifdef RADIO_OFF_TEXT
		case RADIO_OFF: {
			extern wchar WideErrorString[];

			string = TheText.Get("FEA_FMN");
			if(string == WideErrorString) {
				pCurrentStation = nil;
				return;
			}
			break;
		}
#endif
		default: return;
		};

		if(pCurrentStation != string ||
			m_nNextTrack == STREAMED_SOUND_RADIO_MP3_PLAYER && m_nPlayingTrack != STREAMED_SOUND_RADIO_MP3_PLAYER) {
			pCurrentStation = string;
			cDisplay = 60;
		} else {
			if(cDisplay == 0) return;
#ifdef FIX_BUGS
			cDisplay -= CTimer::GetLogicalFramesPassed();
#else
			cDisplay--;
#endif
		}

		CFont::SetJustifyOff();
		CFont::SetBackgroundOff();
		CFont::SetScale(SCREEN_SCALE_X(0.8f), SCREEN_SCALE_Y(1.35f));
		CFont::SetPropOn();
		CFont::SetFontStyle(FONT_HEADING);
		CFont::SetCentreOn();
		// Reminder: Game doesn't have "scaling" at all, it just stretches, and it's team's decision here to not let centered text occupy all the screen.
		//				Disable ASPECT_RATIO_SCALE and it'll go back to default behaviour; stretching.
		CFont::SetCentreSize(SCREEN_SCALE_X(DEFAULT_SCREEN_WIDTH));
		CFont::SetColor(CRGBA(0, 0, 0, 255));
#ifdef FIX_BUGS
		CFont::PrintString(SCREEN_WIDTH / 2 + SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(22.0f) + SCREEN_SCALE_Y(2.0f), pCurrentStation);
#else
		CFont::PrintString(SCREEN_WIDTH / 2 + 2.0f, SCREEN_SCALE_Y(22.0f) + 2.0f, pCurrentStation);
#endif

		if(gNumRetunePresses)
			CFont::SetColor(CRGBA(102, 133, 143, 255));
		else
			CFont::SetColor(CRGBA(147, 196, 211, 255));

		CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_SCALE_Y(22.0f), pCurrentStation);
		CFont::DrawFonts();
	}
}

bool8
cMusicManager::Initialise()
{
	int pos;

	if (!IsInitialised()) {
		time_t timevalue = time(0);
		if (timevalue == -1) {
			pos = AudioManager.m_anRandomTable[0];
		} else {
			tm *pTm = localtime(&timevalue);
			if (pTm->tm_sec == 0)
				pTm->tm_sec = AudioManager.m_anRandomTable[0];
			if (pTm->tm_min == 0)
				pTm->tm_min = AudioManager.m_anRandomTable[1];
			if (pTm->tm_hour == 0)
				pTm->tm_hour = AudioManager.m_anRandomTable[2];
			if (pTm->tm_mday == 0)
				pTm->tm_mday = AudioManager.m_anRandomTable[3];
			if (pTm->tm_mon == 0)
				pTm->tm_mon = AudioManager.m_anRandomTable[4];
			if (pTm->tm_year == 0)
				pTm->tm_year = AudioManager.m_anRandomTable[3];
			if (pTm->tm_wday == 0)
				pTm->tm_wday = AudioManager.m_anRandomTable[2];
			pos = pTm->tm_yday
				* pTm->tm_wday
				* pTm->tm_year
				* pTm->tm_mon
				* pTm->tm_mday
				* pTm->tm_hour * pTm->tm_hour
				* pTm->tm_min * pTm->tm_min
				* pTm->tm_sec * pTm->tm_sec * pTm->tm_sec * pTm->tm_sec;
		}

		for (int i = 0; i < TOTAL_STREAMED_SOUNDS; i++) {
			m_aTracks[i].m_nLength = SampleManager.GetStreamedFileLength(i);
			m_aTracks[i].m_nPosition = pos * AudioManager.m_anRandomTable[i % 5] % m_aTracks[i].m_nLength;
			m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
		}

		m_bResetTimers = FALSE;
		m_nResetTime = 0;
		m_nTimer = m_nLastTrackServiceTime = CTimer::GetTimeInMillisecondsPauseMode();
		m_bDoTrackService = FALSE;
		m_bIgnoreTimeDelay = FALSE;
		m_bRadioSetByScript = FALSE;
		m_nRadioStationScript = HEAD_RADIO;
		m_nRadioPosition = -1;
		m_nRadioInCar = NO_TRACK;
		gNumRetunePresses = 0;
		gRetuneCounter = 0;
		m_bIsInitialised = TRUE;
	}
	return m_bIsInitialised;
}

void
cMusicManager::Terminate()
{
	if (!IsInitialised()) return;

	if (SampleManager.IsStreamPlaying()) {
		SampleManager.StopStreamedFile();
		m_nNextTrack = NO_TRACK;
		m_nPlayingTrack = NO_TRACK;
	}
	m_bIsInitialised = FALSE;
}

void
cMusicManager::ChangeMusicMode(uint8 mode)
{
	if (!IsInitialised()) return;

	uint8 mode2;
	switch (mode)
	{
	case MUSICMODE_FRONTEND:
		mode2 = MUSICMODE_FRONTEND;
#ifdef PAUSE_RADIO_IN_FRONTEND
		// rewind those streams we weren't listening right now
		for (uint32 i = STREAMED_SOUND_RADIO_HEAD; i < STREAMED_SOUND_CUTSCENE_LUIGI1_LG; i++) {
			m_aTracks[i].m_nPosition = GetTrackStartPos(i);
			m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
		}
#endif
		break;
	case MUSICMODE_GAME: mode2 = MUSICMODE_GAME; break;
	case MUSICMODE_CUTSCENE: mode2 = MUSICMODE_CUTSCENE; break;
	case MUSICMODE_DISABLE: mode2 = MUSICMODE_DISABLED; break;
	default: return;
	}

	if (mode2 != m_nMusicMode || mode == MUSICMODE_FRONTEND && mode2 == MUSICMODE_FRONTEND) {
		switch (mode)
		{
		case MUSICMODE_FRONTEND:
		case MUSICMODE_GAME:
		case MUSICMODE_CUTSCENE:
		case MUSICMODE_DISABLED:
			if (SampleManager.IsStreamPlaying()) {
				if (m_nNextTrack < TOTAL_STREAMED_SOUNDS) {
					m_aTracks[m_nNextTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
					m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
				}
				SampleManager.StopStreamedFile();
			}
			m_nNextTrack = NO_TRACK;
			m_nPlayingTrack = NO_TRACK;
			m_bFrontendTrackFinished = FALSE;
			m_bPlayInFrontend = FALSE;
			m_bSetNextStation = FALSE;
			m_bPreviousPlayerInCar = FALSE;
			m_bPlayerInCar = FALSE;
			m_bAnnouncementInProgress = FALSE;
			m_nTimer = m_nLastTrackServiceTime = CTimer::GetTimeInMillisecondsPauseMode();
			m_bDoTrackService = FALSE;
			m_bIgnoreTimeDelay = TRUE;
			m_bVerifyAmbienceTrackStartedToPlay = FALSE;
			m_nMusicMode = mode2;
			break;
		default: return;
		}
	}
}

uint8
cMusicManager::GetRadioInCar(void)
{
	if (!m_bIsInitialised) return HEAD_RADIO;
	if (PlayerInCar()) {
		CVehicle *veh = FindPlayerVehicle();
		if (veh != nil){
			if (UsesPoliceRadio(veh)) {
				if (m_nRadioInCar == NO_TRACK || (CReplay::IsPlayingBack() && !AudioManager.m_bIsPaused))
					return POLICE_RADIO;
				return m_nRadioInCar;
			} else return veh->m_nRadioStation;
		}
	}

	if (m_nRadioInCar == NO_TRACK || (CReplay::IsPlayingBack() && !AudioManager.m_bIsPaused))
		return RADIO_OFF;
	return m_nRadioInCar;
}

void
cMusicManager::SetRadioInCar(uint32 station)
{
	if (m_bIsInitialised) {
		if (!PlayerInCar()) {
			m_nRadioInCar = station;
			return;
		}
		CVehicle *veh = FindPlayerVehicle();
		if (veh == nil) return;
		if (UsesPoliceRadio(veh))
			m_nRadioInCar = station;
		else
			veh->m_nRadioStation = station;
	}
}

void
cMusicManager::SetRadioChannelByScript(uint8 station, int32 pos)
{
	if (m_bIsInitialised && station < RADIO_OFF) {
		m_bRadioSetByScript = TRUE;
		m_nRadioStationScript = station;
		m_nRadioPosition = pos == -1 ? -1 : pos % m_aTracks[station].m_nLength;
	}
}

void
cMusicManager::ResetMusicAfterReload()
{
	m_bRadioSetByScript = FALSE;
	m_nRadioStationScript = 0;
	m_nRadioPosition = -1;
	m_nAnnouncement = NO_TRACK;
	m_bAnnouncementInProgress = FALSE;
	m_bSetNextStation = FALSE;
	gRetuneCounter = 0;
	gNumRetunePresses = 0;
}

void
cMusicManager::ResetTimers(uint32 time)
{
	m_bResetTimers = TRUE;
	m_nResetTime = time;
}

void
cMusicManager::Service()
{
	if (m_bResetTimers) {
		m_bResetTimers = FALSE;
		m_nLastTrackServiceTime = m_nResetTime;
	}

	if (!m_bIsInitialised || m_bDisabled) return;

	if (m_nMusicMode == MUSICMODE_CUTSCENE) {
		SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, TRUE);
		return;
	}

	m_nTimer = CTimer::GetTimeInMillisecondsPauseMode();
	if (m_nTimer > (m_nLastTrackServiceTime + 2000) || m_bIgnoreTimeDelay) {
		m_bIgnoreTimeDelay = FALSE;
		m_bDoTrackService = TRUE;
		m_nLastTrackServiceTime = m_nTimer;
	} else m_bDoTrackService = FALSE;

	if (m_nNextTrack == NO_TRACK && SampleManager.IsStreamPlaying())
		SampleManager.StopStreamedFile();
	else switch (m_nMusicMode) {
		case MUSICMODE_FRONTEND: ServiceFrontEndMode(); break;
		case MUSICMODE_GAME: ServiceGameMode(); break;
		}
}

void
cMusicManager::ServiceFrontEndMode()
{
#ifdef PAUSE_RADIO_IN_FRONTEND
	// pause radio
	for (uint32 i = STREAMED_SOUND_RADIO_HEAD; i < STREAMED_SOUND_CUTSCENE_LUIGI1_LG; i++)
		m_aTracks[i].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
#endif

	if (m_nNextTrack < TOTAL_STREAMED_SOUNDS) {
		if (m_bFrontendTrackFinished) {
			if (!SampleManager.IsStreamPlaying()) {
				switch (m_nNextTrack)
				{
				case STREAMED_SOUND_MISSION_COMPLETED:
					if (!AudioManager.m_bIsPaused)
						ChangeMusicMode(MUSICMODE_GAME);
					break;
				case STREAMED_SOUND_GAME_COMPLETED:
					ChangeMusicMode(MUSICMODE_GAME);
					break;
				default:
					break;
				}
				m_nNextTrack = NO_TRACK;
				m_nPlayingTrack = NO_TRACK;
			}
		} else if (bHasStarted) {
			if (!SampleManager.IsStreamPlaying())
				SampleManager.StartStreamedFile(m_nNextTrack, 0);
		} else {
			SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE);
			if (!SampleManager.StartStreamedFile(m_nNextTrack, m_nNextTrack < NUM_RADIOS ? GetTrackStartPos(m_nNextTrack) : 0))
				return;
			SampleManager.SetStreamedVolumeAndPan(100, 63, FALSE);
			if (m_bPlayInFrontend) bHasStarted = TRUE;
			else m_bFrontendTrackFinished = TRUE;
		}
	}
	if (SampleManager.IsStreamPlaying())
		SampleManager.SetStreamedVolumeAndPan((CPad::GetPad(0)->bDisplayNoControllerMessage || CPad::GetPad(0)->bObsoleteControllerMessage) ? 0 : 100, 63, FALSE);
}

void
cMusicManager::ServiceGameMode()
{
	bool8 bRadioOff = FALSE;
	static int8 nFramesSinceCutsceneEnded = -1;
	uint8 volume;

	m_bPreviousPlayerInCar = m_bPlayerInCar;
	m_bPlayerInCar = PlayerInCar();
	m_nPlayingTrack = m_nNextTrack;
	if (m_bPlayerInCar) {
		if (FindPlayerPed() != nil
			&& !FindPlayerPed()->DyingOrDead()
			&& !CReplay::IsPlayingBack()
			&& FindPlayerVehicle() != nil
			&& !UsesPoliceRadio(FindPlayerVehicle())) {

				if (CPad::GetPad(0)->ChangeStationJustDown()) {
					gRetuneCounter = 30;
					gNumRetunePresses++;
					AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_CHANGE, 1.0f);
					// This needs loop, and this is not the right place. Now done elsewhere.
#ifndef FIX_BUGS
					if (SampleManager.IsMP3RadioChannelAvailable()) {
						if (gNumRetunePresses > RADIO_OFF)
							gNumRetunePresses -= RADIO_OFF;
					}
#endif
				}
#ifdef RADIO_SCROLL_TO_PREV_STATION
				else if(!CPad::GetPad(0)->ArePlayerControlsDisabled() && (CPad::GetPad(0)->GetMouseWheelDownJustDown() || CPad::GetPad(0)->GetMouseWheelUpJustDown())) {
					int scrollNext = ControlsManager.GetControllerKeyAssociatedWithAction(VEHICLE_CHANGE_RADIO_STATION, MOUSE);
					int scrollPrev = scrollNext == rsMOUSEWHEELUPBUTTON ? rsMOUSEWHEELDOWNBUTTON : scrollNext == rsMOUSEWHEELDOWNBUTTON ? rsMOUSEWHEELUPBUTTON : -1;

					if (scrollPrev != -1 && !ControlsManager.IsAnyVehicleActionAssignedToMouseKey(scrollPrev)) {
						gRetuneCounter = 30;
						gNumRetunePresses--;
						AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_CHANGE, 1.0f);
					}
				}
#endif
		}
	} else {
		nFramesSinceCutsceneEnded = -1;
	}

	if (AudioManager.m_bWasPaused)
		m_bPreviousPlayerInCar = FALSE;
	if (!m_bPlayerInCar) {
		if (m_bPreviousPlayerInCar) {
			if (m_nNextTrack != STREAMED_SOUND_RADIO_POLICE)
				m_nRadioInCar = m_nNextTrack;
		}
		ServiceAmbience();
		return;
	}

	if (m_bPreviousPlayerInCar) {
		if (m_nAnnouncement < TOTAL_STREAMED_SOUNDS
			&& (m_nNextTrack < RADIO_OFF || m_bAnnouncementInProgress)
			&& ServiceAnnouncement())
		{
			if (m_bAnnouncementInProgress) {
				m_bSetNextStation = FALSE;
				return;
			}
			m_nPlayingTrack = m_nNextTrack;
			m_nNextTrack = GetCarTuning();
		}
		if (SampleManager.IsMP3RadioChannelAvailable()
			&& m_nNextTrack != STREAMED_SOUND_RADIO_MP3_PLAYER
			&& ControlsManager.GetIsKeyboardKeyJustDown(rsF9))
		{
			m_nPlayingTrack = m_nNextTrack;
			m_nNextTrack = STREAMED_SOUND_RADIO_MP3_PLAYER;
			if (FindPlayerVehicle() != nil)
				FindPlayerVehicle()->m_nRadioStation = STREAMED_SOUND_RADIO_MP3_PLAYER;
			AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_CHANGE, 1.0f);
			gRetuneCounter = 0;
			gNumRetunePresses = 0;
			m_bSetNextStation = FALSE;
		}
		// Because when you switch radio back and forth, gNumRetunePresses will be 0 but gRetuneCounter won't.
#ifdef RADIO_SCROLL_TO_PREV_STATION
		if (gRetuneCounter != 0) {
			if (gRetuneCounter > 1) gRetuneCounter--;
			else if (gRetuneCounter == 1) gRetuneCounter = -1;
			else if (gRetuneCounter == -1) {
				m_bSetNextStation = TRUE;
				gRetuneCounter = 0;
			}
		}
#else
		if (gNumRetunePresses) {
			if (gRetuneCounter != 0) gRetuneCounter--;
			else m_bSetNextStation = TRUE;
		}
#endif
		if (gRetuneCounter)
			AudioManager.DoPoliceRadioCrackle();
		if (m_bSetNextStation) {
			m_bSetNextStation = FALSE;
			m_nPlayingTrack = m_nNextTrack;
			m_nNextTrack = GetNextCarTuning();
			if (m_nNextTrack == STREAMED_SOUND_CITY_AMBIENT || m_nNextTrack == STREAMED_SOUND_WATER_AMBIENT)
				bRadioOff = TRUE;

			if (m_nPlayingTrack == STREAMED_SOUND_CITY_AMBIENT || m_nPlayingTrack == STREAMED_SOUND_WATER_AMBIENT)
				AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_CHANGE, 0.0f);
		}
		if (m_nNextTrack < RADIO_OFF) {
			if (ChangeRadioChannel()) {
				ServiceTrack();
			} else {
				m_bPlayerInCar = FALSE;
				if (FindPlayerVehicle())
					FindPlayerVehicle()->m_nRadioStation = m_nNextTrack;
				m_nNextTrack = NO_TRACK;
			}
			if (CTimer::GetIsSlowMotionActive()) {
				if (TheCamera.pTargetEntity != nil) {
					float DistToTargetSq = (TheCamera.pTargetEntity->GetPosition() - TheCamera.GetPosition()).MagnitudeSqr();
					if (DistToTargetSq >= SQR(55.0f)) {
						SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE);
					} else if (DistToTargetSq >= SQR(10.0f)) {
						volume = ((45.0f - (Sqrt(DistToTargetSq) - 10.0f)) / 45.0f * 100.0f);
						uint8 pan;
						if (AudioManager.ShouldDuckMissionAudio())
							volume /= 4;
						if (volume > 0) {
							CVector panVec;
							AudioManager.TranslateEntity(&TheCamera.pTargetEntity->GetPosition(), &panVec);
							pan = AudioManager.ComputePan(55.0f, &panVec);
						} else {
							pan = 0;
						}
						if (gRetuneCounter)
							volume /= 4;
						SampleManager.SetStreamedVolumeAndPan(volume, pan, FALSE);
					} else if (AudioManager.ShouldDuckMissionAudio()) {
						SampleManager.SetStreamedVolumeAndPan(25, 63, FALSE);
					} else if (gRetuneCounter) {
						SampleManager.SetStreamedVolumeAndPan(25, 63, FALSE);
					} else {
						SampleManager.SetStreamedVolumeAndPan(100, 63, FALSE);
					}
				}
			} else if (AudioManager.ShouldDuckMissionAudio()) {
				SampleManager.SetStreamedVolumeAndPan(25, 63, FALSE);
				nFramesSinceCutsceneEnded = 0;
			} else {
				if (nFramesSinceCutsceneEnded == -1) {
					volume = 100;
				} else if (nFramesSinceCutsceneEnded < 20) {
					nFramesSinceCutsceneEnded++;
					volume = 25;
				} else if (nFramesSinceCutsceneEnded < 40) {
					volume = 3 * (nFramesSinceCutsceneEnded - 20) + 25;
					nFramesSinceCutsceneEnded++;
				} else {
					nFramesSinceCutsceneEnded = -1;
					volume = 100;
				}
				if (gRetuneCounter != 0)
					volume /= 4;
				SampleManager.SetStreamedVolumeAndPan(volume, 63, FALSE);
			}
			return;
		}
		if (bRadioOff) {
			m_nNextTrack = m_nPlayingTrack;
			if (FindPlayerVehicle() != nil)
				FindPlayerVehicle()->m_nRadioStation = RADIO_OFF;
			AudioManager.PlayOneShot(AudioManager.m_nFrontEndEntity, SOUND_FRONTEND_RADIO_TURN_OFF, 0.0f);
		}
		ServiceAmbience();
		return;
	}
	if (m_bRadioSetByScript) {
		if (UsesPoliceRadio(FindPlayerVehicle())) {
			m_nNextTrack = STREAMED_SOUND_RADIO_POLICE;
		} else {
			m_nNextTrack = m_nRadioStationScript;
			if (FindPlayerVehicle()->m_nRadioStation == m_nNextTrack) {
				m_nPlayingTrack = NO_TRACK;
				SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE);
				SampleManager.StopStreamedFile();
			}
			if (m_nRadioPosition != -1) {
				m_aTracks[m_nNextTrack].m_nPosition = m_nRadioPosition;
				m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
			}
		}
	} else {
		m_nNextTrack = GetCarTuning();
	}
	if (m_nNextTrack >= RADIO_OFF) {
		ServiceAmbience();
		return;
	}
	if (ChangeRadioChannel()) {
		if (m_bRadioSetByScript) {
			m_bRadioSetByScript = FALSE;
			FindPlayerVehicle()->m_nRadioStation = m_nNextTrack;
		}
	} else {
		m_bPlayerInCar = FALSE;
		m_nNextTrack = NO_TRACK;
	}
}

void
cMusicManager::StopFrontEndTrack()
{
	if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_FRONTEND && m_nNextTrack != NO_TRACK) {
		m_aTracks[m_nNextTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
		m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
		SampleManager.StopStreamedFile();
		m_nPlayingTrack = NO_TRACK;
		m_nNextTrack = NO_TRACK;
	}
}

void
cMusicManager::PlayAnnouncement(uint8 announcement)
{
	if (IsInitialised() && !m_bDisabled && !m_bAnnouncementInProgress)
		m_nAnnouncement = announcement;
}

void
cMusicManager::PlayFrontEndTrack(uint8 track, bool8 bPlayInFrontend)
{
	if (IsInitialised() && !m_bDisabled && track < TOTAL_STREAMED_SOUNDS) {
		if (m_nMusicMode == MUSICMODE_GAME) {
			if (m_nNextTrack != NO_TRACK) {
				if (m_bAnnouncementInProgress) {
					m_nAnnouncement = NO_TRACK;
					m_bAnnouncementInProgress = FALSE;
				}
				m_aTracks[m_nNextTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
				m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
			}
			SampleManager.StopStreamedFile();
		} else if (m_nMusicMode == MUSICMODE_FRONTEND) {
			if (m_nNextTrack != NO_TRACK) {
				m_aTracks[m_nNextTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
				m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
			}
			SampleManager.StopStreamedFile();
		}

		m_nPlayingTrack = m_nNextTrack;
		m_nNextTrack = track;
		m_bPlayInFrontend = bPlayInFrontend;
		m_bFrontendTrackFinished = FALSE;
		m_bDoTrackService = TRUE;
		bHasStarted = FALSE;
		if (m_nNextTrack < NUM_RADIOS) {
			gRetuneCounter = 0;
			gNumRetunePresses = 0;
		}
	}
}

void
cMusicManager::PreloadCutSceneMusic(uint8 track)
{
	if (IsInitialised() && !m_bDisabled && track < TOTAL_STREAMED_SOUNDS && m_nMusicMode == MUSICMODE_CUTSCENE) {
		AudioManager.ResetPoliceRadio();
		while (SampleManager.IsStreamPlaying())
			SampleManager.StopStreamedFile();
		SampleManager.PreloadStreamedFile(track);
		SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, TRUE);
		m_nNextTrack = track;
	}
}

void
cMusicManager::PlayPreloadedCutSceneMusic(void)
{
	if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE)
		SampleManager.StartPreloadedStreamedFile();
}

void
cMusicManager::StopCutSceneMusic(void)
{
	if (IsInitialised() && !m_bDisabled && m_nMusicMode == MUSICMODE_CUTSCENE) {
		SampleManager.StopStreamedFile();
		m_nNextTrack = NO_TRACK;
	}
}

uint32
cMusicManager::GetTrackStartPos(uint8 track)
{
	uint32 pos = m_aTracks[track].m_nPosition;
	if (CTimer::GetTimeInMillisecondsPauseMode() > m_aTracks[track].m_nLastPosCheckTimer)
		pos += Min(CTimer::GetTimeInMillisecondsPauseMode() - m_aTracks[track].m_nLastPosCheckTimer, 90000);
	else
		m_aTracks[track].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();

	if (pos > m_aTracks[track].m_nLength)
		pos %= m_aTracks[track].m_nLength;
	return pos;
}

bool8
cMusicManager::UsesPoliceRadio(CVehicle *veh)
{
	switch (veh->GetModelIndex())
	{
	case MI_FBICAR:
	case MI_POLICE:
	case MI_ENFORCER:
	case MI_PREDATOR:
	case MI_RHINO:
	case MI_BARRACKS:
		return TRUE;
	}
	return FALSE;
}

void
cMusicManager::ServiceAmbience()
{
	uint8 volume;

	if (m_bAnnouncementInProgress) {
		m_nAnnouncement = NO_TRACK;
		m_bAnnouncementInProgress = FALSE;
	}
	if (m_nNextTrack < RADIO_OFF) {
		if (SampleManager.IsStreamPlaying()) {
			m_aTracks[m_nNextTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
			m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
			SampleManager.StopStreamedFile();
			m_nNextTrack = NO_TRACK;
			return;
		}
		m_nNextTrack = RADIO_OFF;
	}
	if (CWorld::Players[CWorld::PlayerInFocus].m_WBState != WBSTATE_PLAYING && !SampleManager.IsStreamPlaying()) {
		m_nNextTrack = NO_TRACK;
		return;
	}

	m_nPlayingTrack = m_nNextTrack;
	m_nNextTrack = TheCamera.DistanceToWater <= 45.0f ? STREAMED_SOUND_WATER_AMBIENT : STREAMED_SOUND_CITY_AMBIENT;

	if (m_nNextTrack == m_nPlayingTrack) {
		ComputeAmbienceVol(FALSE, volume);
		SampleManager.SetStreamedVolumeAndPan(volume, 63, TRUE);
		if (m_bVerifyAmbienceTrackStartedToPlay) {
			if (SampleManager.IsStreamPlaying())
				m_bVerifyAmbienceTrackStartedToPlay = FALSE;
		} else ServiceTrack();
	} else {
		if (m_nPlayingTrack < TOTAL_STREAMED_SOUNDS) {
			m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
			m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
			SampleManager.StopStreamedFile();
		}
		uint32 pos = GetTrackStartPos(m_nNextTrack);
		SampleManager.SetStreamedVolumeAndPan(0, 63, TRUE);
		if (SampleManager.StartStreamedFile(m_nNextTrack, pos)) {
			ComputeAmbienceVol(TRUE, volume);
			SampleManager.SetStreamedVolumeAndPan(volume, 63, TRUE);
			m_bVerifyAmbienceTrackStartedToPlay = TRUE;
		} else
			m_nNextTrack = NO_TRACK;
	}
}

void
cMusicManager::ComputeAmbienceVol(bool8 reset, uint8 &outVolume)
{
	static float fVol = 0.0f;

	if (reset)
		fVol = 0.0f;
	else if (fVol < 60.0f)
		fVol += 1.0f;

	if (TheCamera.DistanceToWater > 70.0f)
		outVolume = fVol;
	else if (TheCamera.DistanceToWater > 45.0f)
		outVolume = (TheCamera.DistanceToWater - 45.0f) / 25.0f * fVol;
	else if (TheCamera.DistanceToWater > 20.0f)
		outVolume = (45.0f - TheCamera.DistanceToWater) / 25.0f * fVol;
	else
		outVolume = fVol;
}

void
cMusicManager::ServiceTrack()
{
	if (m_bDoTrackService) {
		if (!SampleManager.IsStreamPlaying())
			SampleManager.StartStreamedFile(m_nNextTrack, 0);
	}
}

bool8
cMusicManager::ServiceAnnouncement()
{
	static int8 cCheck = 0;
	if (m_bAnnouncementInProgress) {
		if (!SampleManager.IsStreamPlaying()) {
			m_nAnnouncement = NO_TRACK;
			m_bAnnouncementInProgress = FALSE;
		}
		return TRUE;
	}

	if (++cCheck >= 30) {
		cCheck = 0;
		int pos = SampleManager.GetStreamedFilePosition();
		if (SampleManager.IsStreamPlaying()) {
			if (m_nNextTrack != NO_TRACK) {
				m_aTracks[m_nNextTrack].m_nPosition = pos;
				m_aTracks[m_nNextTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
				SampleManager.StopStreamedFile();
			}
		}

		SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE);
		if (SampleManager.StartStreamedFile(m_nAnnouncement, 0)) {
			SampleManager.SetStreamedVolumeAndPan(AudioManager.ShouldDuckMissionAudio() ? 25 : 100, 63, FALSE);
			m_bAnnouncementInProgress = TRUE;
			m_nPlayingTrack = m_nNextTrack;
			m_nNextTrack = m_nAnnouncement;
			return TRUE;
		}

		if (cCheck != 0) cCheck--;
		else cCheck = 30;
		return FALSE;
	}

	return FALSE;
}

uint8
cMusicManager::GetCarTuning()
{
	CVehicle *veh = FindPlayerVehicle();
	if (veh == nil) return RADIO_OFF;
	if (UsesPoliceRadio(veh)) return POLICE_RADIO;
	if (veh->m_nRadioStation == USERTRACK && !SampleManager.IsMP3RadioChannelAvailable())
		veh->m_nRadioStation = AudioManager.m_anRandomTable[2] % USERTRACK;
	return veh->m_nRadioStation;
}

uint8
cMusicManager::GetNextCarTuning()
{
	CVehicle *veh = FindPlayerVehicle();
	if (veh == nil) return RADIO_OFF;
	if (UsesPoliceRadio(veh)) return POLICE_RADIO;
	if (gNumRetunePresses != 0) {
#ifdef RADIO_SCROLL_TO_PREV_STATION
		if (gNumRetunePresses < 0) {
			while (gNumRetunePresses < 0) {
				if(veh->m_nRadioStation == HEAD_RADIO) {
					veh->m_nRadioStation = RADIO_OFF;
				} else if(veh->m_nRadioStation == RADIO_OFF || veh->m_nRadioStation == NUM_RADIOS) {
					veh->m_nRadioStation = SampleManager.IsMP3RadioChannelAvailable() ? USERTRACK : USERTRACK - 1;
				} else
					veh->m_nRadioStation--;

				++gNumRetunePresses;
			}
		} else
#endif
		if (SampleManager.IsMP3RadioChannelAvailable()) {
			if (veh->m_nRadioStation == RADIO_OFF)
				veh->m_nRadioStation = NUM_RADIOS;
			veh->m_nRadioStation += gNumRetunePresses;
#ifdef FIX_BUGS
			while (veh->m_nRadioStation > NUM_RADIOS)
				veh->m_nRadioStation -= (NUM_RADIOS + 1);
#endif
			if (veh->m_nRadioStation == NUM_RADIOS)
				veh->m_nRadioStation = RADIO_OFF;
#ifndef FIX_BUGS
			else if (veh->m_nRadioStation > NUM_RADIOS)
				veh->m_nRadioStation -= (NUM_RADIOS + 1);
#endif
		} else if (gNumRetunePresses + veh->m_nRadioStation >= USERTRACK) {
			while (gNumRetunePresses) {
				if (veh->m_nRadioStation == RADIO_OFF)
					veh->m_nRadioStation = HEAD_RADIO;
				else if (veh->m_nRadioStation < USERTRACK)
					++veh->m_nRadioStation;

				if (veh->m_nRadioStation == USERTRACK)
					veh->m_nRadioStation = RADIO_OFF;
				--gNumRetunePresses;
			}
		} else
			veh->m_nRadioStation += gNumRetunePresses;
		gNumRetunePresses = 0;
	}
	return veh->m_nRadioStation;
}

bool8
cMusicManager::ChangeRadioChannel()
{
	if (m_nNextTrack != m_nPlayingTrack) {
		if (m_nPlayingTrack < TOTAL_STREAMED_SOUNDS) {
			m_aTracks[m_nPlayingTrack].m_nPosition = SampleManager.GetStreamedFilePosition();
			m_aTracks[m_nPlayingTrack].m_nLastPosCheckTimer = CTimer::GetTimeInMillisecondsPauseMode();
			SampleManager.SetStreamedVolumeAndPan(0, 63, FALSE);
			SampleManager.StopStreamedFile();
		}
		if (SampleManager.IsStreamPlaying())
			return FALSE;
		if (!SampleManager.StartStreamedFile(m_nNextTrack, GetTrackStartPos(m_nNextTrack)))
			return FALSE;
		SampleManager.SetStreamedVolumeAndPan(AudioManager.ShouldDuckMissionAudio() ? 25 : 100, 63, FALSE);
	}
	return TRUE;
}


code:
#pragma once

#include "audio_enums.h"

class tStreamedSample
{
public:
	uint32 m_nLength;
	uint32 m_nPosition;
	uint32 m_nLastPosCheckTimer;
};

class CVehicle;

class cMusicManager
{
public:
	bool8 m_bIsInitialised;
	bool8 m_bDisabled;
	uint8 m_nMusicMode;
	uint8 m_nNextTrack;
	uint8 m_nPlayingTrack;
	bool8 m_bFrontendTrackFinished;
	bool8 m_bPlayInFrontend;
	bool8 m_bSetNextStation;
	uint8 m_nAnnouncement;
	bool8 m_bPreviousPlayerInCar;
	bool8 m_bPlayerInCar;
	bool8 m_bAnnouncementInProgress;
	tStreamedSample m_aTracks[TOTAL_STREAMED_SOUNDS];
	bool8 m_bResetTimers;
	uint32 m_nResetTime;
	uint32 m_nLastTrackServiceTime;
	uint32 m_nTimer;
	bool8 m_bDoTrackService;
	bool8 m_bIgnoreTimeDelay;
	bool8 m_bVerifyAmbienceTrackStartedToPlay;
	bool8 m_bRadioSetByScript;
	uint8 m_nRadioStationScript;
	int32 m_nRadioPosition;
	uint8 m_nRadioInCar;

public:
	cMusicManager();
	bool8 IsInitialised() { return m_bIsInitialised; }
	uint32 GetMusicMode() { return m_nMusicMode; }
	uint8 GetNextTrack() { return m_nNextTrack; }

	bool8 Initialise();
	void Terminate();

	void ChangeMusicMode(uint8 mode);
	void StopFrontEndTrack();

	bool8 PlayerInCar();
	void DisplayRadioStationName();

	void PlayAnnouncement(uint8);
	void PlayFrontEndTrack(uint8, bool8);
	void PreloadCutSceneMusic(uint8);
	void PlayPreloadedCutSceneMusic(void);
	void StopCutSceneMusic(void);
	uint8 GetRadioInCar(void);
	void SetRadioInCar(uint32);
	void SetRadioChannelByScript(uint8, int32);

	void ResetMusicAfterReload();

	void ResetTimers(uint32);
	void Service();
	void ServiceFrontEndMode();
	void ServiceGameMode();
	void ServiceAmbience();
	void ServiceTrack();

	bool8 UsesPoliceRadio(CVehicle *veh);
	uint32 GetTrackStartPos(uint8);

	void ComputeAmbienceVol(bool8 reset, uint8& outVolume);
	bool8 ServiceAnnouncement();

	uint8 GetCarTuning();
	uint8 GetNextCarTuning();
	bool8 ChangeRadioChannel();
};

VALIDATE_SIZE(cMusicManager, 0x95C);

extern cMusicManager MusicManager;


code:
#include "common.h"

#include "DMAudio.h"

#include "AudioManager.h"

#include "AudioSamples.h"
#include "MusicManager.h"
#include "PlayerPed.h"
#include "PolRadio.h"
#include "Replay.h"
#include "Vehicle.h"
#include "World.h"
#include "Zones.h"
#include "sampman.h"
#include "Wanted.h"

struct tPoliceRadioZone {
	char m_aName[8];
	uint32 m_nSampleIndex;
	int32 field_12;
};

tPoliceRadioZone ZoneSfx[NUMAUDIOZONES];
char SubZo2Label[8];
char SubZo3Label[8];

uint32 g_nMissionAudioSfx = TOTAL_AUDIO_SAMPLES;
int8 g_nMissionAudioPlayingStatus = PLAY_STATUS_FINISHED;
bool8 gSpecialSuspectLastSeenReport;
uint32 gMinTimeToNextReport[NUM_CRIME_TYPES];

void
cAudioManager::InitialisePoliceRadioZones()
{
	for (int32 i = 0; i < NUMAUDIOZONES; i++)
		memset(ZoneSfx[i].m_aName, 0, 8);

#define SETZONESFX(i, name, sample) \
	strcpy(ZoneSfx[i].m_aName, name); \
	ZoneSfx[i].m_nSampleIndex = sample;

	SETZONESFX(0, "HOSPI_2", SFX_POLICE_RADIO_ROCKFORD);
	SETZONESFX(1, "CONSTRU", SFX_POLICE_RADIO_FORT_STAUNTON);
	SETZONESFX(2, "STADIUM", SFX_POLICE_RADIO_ASPATRIA);
	SETZONESFX(3, "YAKUSA", SFX_POLICE_RADIO_TORRINGTON);
	SETZONESFX(4, "SHOPING", SFX_POLICE_RADIO_BEDFORD_POINT);
	SETZONESFX(5, "COM_EAS", SFX_POLICE_RADIO_NEWPORT);
	SETZONESFX(6, "PARK", SFX_POLICE_RADIO_BELLEVILLE_PARK);
	SETZONESFX(7, "UNIVERS", SFX_POLICE_RADIO_LIBERTY_CAMPUS);
	SETZONESFX(8, "BIG_DAM", SFX_POLICE_RADIO_COCHRANE_DAM);
	SETZONESFX(9, "SUB_IND", SFX_POLICE_RADIO_PIKE_CREEK);
	SETZONESFX(10, "SWANKS", SFX_POLICE_RADIO_CEDAR_GROVE);
	SETZONESFX(11, "PROJECT", SFX_POLICE_RADIO_WICHITA_GARDENS);
	SETZONESFX(12, "AIRPORT", SFX_POLICE_RADIO_FRANCIS_INTERNATIONAL_AIRPORT);
	SETZONESFX(13, "PORT_W", SFX_POLICE_RADIO_CALLAHAN_POINT);
	SETZONESFX(14, "PORT_S", SFX_POLICE_RADIO_ATLANTIC_QUAYS);
	SETZONESFX(15, "PORT_E", SFX_POLICE_RADIO_PORTLAND_HARBOUR);
	SETZONESFX(16, "PORT_I", SFX_POLICE_RADIO_TRENTON);
	SETZONESFX(17, "CHINA", SFX_POLICE_RADIO_CHINATOWN);
	SETZONESFX(18, "REDLIGH", SFX_POLICE_RADIO_RED_LIGHT_DISTRICT);
	SETZONESFX(19, "TOWERS", SFX_POLICE_RADIO_HEPBURN_HEIGHTS);
	SETZONESFX(20, "LITTLEI", SFX_POLICE_RADIO_SAINT_MARKS);
	SETZONESFX(21, "HARWOOD", SFX_POLICE_RADIO_HARWOOD);
	SETZONESFX(22, "EASTBAY", SFX_POLICE_RADIO_PORTLAND_BEACH);
	SETZONESFX(23, "S_VIEW", SFX_POLICE_RADIO_PORTLAND_STRAIGHTS);
	SETZONESFX(24, "CITYZON", SFX_POLICE_RADIO_LIBERTY_CITY);
	SETZONESFX(25, "IND_ZON", SFX_POLICE_RADIO_PORTLAND);
	SETZONESFX(26, "COM_ZON", SFX_POLICE_RADIO_STAUNTON_ISLAND);
	SETZONESFX(27, "SUB_ZON", SFX_POLICE_RADIO_SHORESIDE_VALE);
	SETZONESFX(28, "SUB_ZO2", SFX_POLICE_RADIO_SHORESIDE_VALE);
	SETZONESFX(29, "SUB_ZO3", SFX_POLICE_RADIO_SHORESIDE_VALE);
	SETZONESFX(30, "A", SFX_POLICE_RADIO_ROCKFORD);
	SETZONESFX(31, "A", SFX_POLICE_RADIO_ROCKFORD);
	SETZONESFX(32, "A", SFX_POLICE_RADIO_ROCKFORD);
	SETZONESFX(33, "A", SFX_POLICE_RADIO_ROCKFORD);
	SETZONESFX(34, "A", SFX_POLICE_RADIO_ROCKFORD);

#undef SETZONESFX

	strcpy(SubZo2Label, "SUB_ZO2");
	strcpy(SubZo3Label, "SUB_ZO3");
}

void
cAudioManager::InitialisePoliceRadio()
{
	m_sPoliceRadioQueue.Reset();
	for (int32 i = 0; i < ARRAY_SIZE(m_aCrimes); i++)
		m_aCrimes[i].type = CRIME_NONE;

	SampleManager.SetChannelReverbFlag(CHANNEL_POLICE_RADIO, FALSE);
	gSpecialSuspectLastSeenReport = FALSE;
	for (int32 i = 0; i < ARRAY_SIZE(gMinTimeToNextReport); i++)
		gMinTimeToNextReport[i] = m_FrameCounter;
}

void
cAudioManager::ResetPoliceRadio()
{
	if (!m_bIsInitialised) return;
	if (SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) SampleManager.StopChannel(CHANNEL_POLICE_RADIO);
	InitialisePoliceRadio();
}

void
cAudioManager::SetMissionScriptPoliceAudio(uint32 sfx)
{
	if (!m_bIsInitialised) return;
	if (g_nMissionAudioPlayingStatus != PLAY_STATUS_PLAYING) {
		g_nMissionAudioPlayingStatus = PLAY_STATUS_STOPPED;
		g_nMissionAudioSfx = sfx;
	}
}

int8
cAudioManager::GetMissionScriptPoliceAudioPlayingStatus()
{
	return g_nMissionAudioPlayingStatus;
}

void
cAudioManager::DoPoliceRadioCrackle()
{
	m_sQueueSample.m_nEntityIndex = m_nPoliceChannelEntity;
	m_sQueueSample.m_nCounter = 0;
	m_sQueueSample.m_nSampleIndex = SFX_POLICE_RADIO_CRACKLE;
	m_sQueueSample.m_nBankIndex = SFX_BANK_0;
	m_sQueueSample.m_bIs2D = TRUE;
	m_sQueueSample.m_nPriority = 10;
	m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(SFX_POLICE_RADIO_CRACKLE);
	m_sQueueSample.m_nVolume = m_anRandomTable[2] % 20 + 15;
	m_sQueueSample.m_nLoopCount = 0;
	SET_EMITTING_VOLUME(m_sQueueSample.m_nVolume);
	SET_LOOP_OFFSETS(SFX_POLICE_RADIO_CRACKLE)
	m_sQueueSample.m_bStatic = FALSE;
	m_sQueueSample.m_bReverb = FALSE;
	m_sQueueSample.m_nPan = 63;
	m_sQueueSample.m_nFramesToPlay = 3;
	SET_SOUND_REFLECTION(FALSE);
	AddSampleToRequestedQueue();
}

void
cAudioManager::ServicePoliceRadio()
{
	int32 wantedLevel = 0; // uninitialized variable
	static uint32 nLastSeen = 300;

	if(!m_bIsInitialised) return;

	if(!m_bIsPaused) {
		bool8 crimeReport = SetupCrimeReport();
#ifdef FIX_BUGS // Crash at 0x5fe6ef
		if(CReplay::IsPlayingBack() || !FindPlayerPed() || !FindPlayerPed()->m_pWanted)
			return;
#endif
		wantedLevel = FindPlayerPed()->m_pWanted->GetWantedLevel();
		if (!crimeReport) {
			if (wantedLevel != 0) {
				if (nLastSeen != 0) {
#ifdef FIX_BUGS
					nLastSeen -= CTimer::GetLogicalFramesPassed();
#else
					nLastSeen--;
#endif
				} else {
					nLastSeen = m_anRandomTable[1] % 1000 + 2000;
					SetupSuspectLastSeenReport();
				}
			}
		}
	}
	ServicePoliceRadioChannel(wantedLevel);
}

void
cAudioManager::ServicePoliceRadioChannel(uint8 wantedLevel)
{
	bool8 processed = FALSE;
	uint32 sample;
	uint32 freq;

	static int cWait = 0;
	static bool8 bChannelOpen = FALSE;
	static uint8 bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_STOPPED;
	static uint32 PoliceChannelFreq = 5500;

	if (!m_bIsInitialised) return;

	if (m_bIsPaused) {
#ifdef GTA_PS2
		if (SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO))
			SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, 0);
#else
		if (SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) SampleManager.StopChannel(CHANNEL_POLICE_RADIO);
		if (g_nMissionAudioSfx != TOTAL_AUDIO_SAMPLES && bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_PLAYING &&
			SampleManager.IsStreamPlaying(1))
			SampleManager.PauseStream(TRUE, 1);
#endif
	} else {
#ifdef GTA_PS2
		if (m_bWasPaused)
			SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, PoliceChannelFreq);
#else
		if (m_bWasPaused && g_nMissionAudioSfx != TOTAL_AUDIO_SAMPLES &&
			bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_PLAYING)
			SampleManager.PauseStream(FALSE, 1);
#endif
		if (m_sPoliceRadioQueue.m_nSamplesInQueue == 0) bChannelOpen = FALSE;
		if (cWait) {
#ifdef FIX_BUGS
			cWait -= CTimer::GetLogicalFramesPassed();
#else
			--cWait;
#endif
			return;
		}
		if (g_nMissionAudioSfx != TOTAL_AUDIO_SAMPLES && !bChannelOpen) {
			if (g_nMissionAudioPlayingStatus != PLAY_STATUS_STOPPED) {
				if (g_nMissionAudioPlayingStatus == PLAY_STATUS_PLAYING && bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_STOPPED &&
#ifdef GTA_PS2
					SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) {
#else
					SampleManager.IsStreamPlaying(1)) {
#endif
					bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_PLAYING;
				}
				if (bMissionAudioPhysicalPlayingStatus == PLAY_STATUS_PLAYING) {
#ifdef GTA_PS2
					if (SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) {
#else
					if (SampleManager.IsStreamPlaying(1)) {
#endif
						DoPoliceRadioCrackle();
					} else {
						bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_FINISHED;
						g_nMissionAudioPlayingStatus = PLAY_STATUS_FINISHED;
						g_nMissionAudioSfx = TOTAL_AUDIO_SAMPLES;
						cWait = 30;
					}
					return;
				}
			} else if (!SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO)) {
#ifdef GTA_PS2
				SampleManager.InitialiseChannel(CHANNEL_POLICE_RADIO, g_nMissionAudioSfx, SFX_BANK_PED_COMMENTS);
				PoliceChannelFreq = SampleManager.GetSampleBaseFrequency(g_nMissionAudioSfx);
				SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, PoliceChannelFreq);
				SampleManager.SetChannelVolume(CHANNEL_POLICE_RADIO, MAX_VOLUME);
				SampleManager.SetChannelPan(CHANNEL_POLICE_RADIO, 63);
				SampleManager.StartChannel(CHANNEL_POLICE_RADIO);
#else
				SampleManager.PreloadStreamedFile(g_nMissionAudioSfx, 1);
				SampleManager.SetStreamedVolumeAndPan(MAX_VOLUME, 63, TRUE, 1);
				SampleManager.StartPreloadedStreamedFile(1);
#endif
				g_nMissionAudioPlayingStatus = PLAY_STATUS_PLAYING;
				bMissionAudioPhysicalPlayingStatus = PLAY_STATUS_STOPPED;
				return;
			}
		}
		if (bChannelOpen) DoPoliceRadioCrackle();
		if ((g_nMissionAudioSfx == TOTAL_AUDIO_SAMPLES || g_nMissionAudioPlayingStatus != PLAY_STATUS_PLAYING) &&
			!SampleManager.GetChannelUsedFlag(CHANNEL_POLICE_RADIO) && m_sPoliceRadioQueue.m_nSamplesInQueue != 0) {
			sample = m_sPoliceRadioQueue.Remove();
			if (wantedLevel == 0) {
				if (gSpecialSuspectLastSeenReport) {
					gSpecialSuspectLastSeenReport = FALSE;
				} else if (((sample >= SFX_POLICE_RADIO_MESSAGE_NOISE_1) && (sample <= SFX_POLICE_RADIO_MESSAGE_NOISE_3)) || sample == TOTAL_AUDIO_SAMPLES) {
					bChannelOpen = FALSE;
					processed = TRUE;
				}
			}
			if (sample == TOTAL_AUDIO_SAMPLES) {
				if (!processed) cWait = 30;
			} else {
				SampleManager.InitialiseChannel(CHANNEL_POLICE_RADIO, sample, SFX_BANK_0);
				switch (sample) {
				case SFX_POLICE_RADIO_MESSAGE_NOISE_1:
				case SFX_POLICE_RADIO_MESSAGE_NOISE_2:
				case SFX_POLICE_RADIO_MESSAGE_NOISE_3:
					freq = m_anRandomTable[4] % 2000 + 10025;
					bChannelOpen = bChannelOpen == FALSE;
					break;
				default: freq = SampleManager.GetSampleBaseFrequency(sample); break;
				}
				PoliceChannelFreq = freq;
#ifdef USE_TIME_SCALE_FOR_AUDIO
				SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, freq * CTimer::GetTimeScale());
#else
				SampleManager.SetChannelFrequency(CHANNEL_POLICE_RADIO, freq);
#endif
				SampleManager.SetChannelVolume(CHANNEL_POLICE_RADIO, 100);
				SampleManager.SetChannelPan(CHANNEL_POLICE_RADIO, 63);
				SampleManager.SetChannelLoopCount(CHANNEL_POLICE_RADIO, 1);
#ifndef GTA_PS2
				SampleManager.SetChannelLoopPoints(CHANNEL_POLICE_RADIO, 0, -1);
#endif
				SampleManager.StartChannel(CHANNEL_POLICE_RADIO);
			}
			if (processed) ResetPoliceRadio();
		}
	}
}

bool8
cAudioManager::SetupCrimeReport()
{
	int16 audioZoneId;
	CZone *zone;
	float rangeX;
	float rangeY;
	float halfX;
	float halfY;
	float quarterX;
	float quarterY;
	int i;
	uint32 sampleIndex;
	bool8 processed = FALSE;

	if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) return FALSE;

	if (POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue <= 9) {
		AgeCrimes();
		return TRUE;
	}

	for (i = 0; i < ARRAY_SIZE(m_aCrimes); i++) {
		if (m_aCrimes[i].type != CRIME_NONE)
			break;
	}

	if (i == ARRAY_SIZE(m_aCrimes)) return FALSE;
	audioZoneId = CTheZones::FindAudioZone(&m_aCrimes[i].position);
	if (audioZoneId >= 0 && audioZoneId < NUMAUDIOZONES) {
		zone = CTheZones::GetAudioZone(audioZoneId);
		for (int j = 0; j < NUMAUDIOZONES; j++) {
			if (strcmp(zone->name, ZoneSfx[j].m_aName) == 0) {
				sampleIndex = ZoneSfx[j].m_nSampleIndex;
				m_sPoliceRadioQueue.Add(m_anRandomTable[4] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
				m_sPoliceRadioQueue.Add(m_anRandomTable[0] % 3 + SFX_WEVE_GOT);
				m_sPoliceRadioQueue.Add(m_anRandomTable[1] % 2 + SFX_A_10_1);
				switch (m_aCrimes[i].type) {
				case CRIME_PED_BURNED: m_aCrimes[i].type = CRIME_HIT_PED; break;
				case CRIME_COP_BURNED: m_aCrimes[i].type = CRIME_HIT_COP; break;
				case CRIME_VEHICLE_BURNED: m_aCrimes[i].type = CRIME_STEAL_CAR; break;
				case CRIME_DESTROYED_CESSNA: m_aCrimes[i].type = CRIME_SHOOT_HELI; break;
				default: break;
				}
				m_sPoliceRadioQueue.Add(m_aCrimes[i].type + SFX_CRIME_1 - 1);
				m_sPoliceRadioQueue.Add(SFX_IN);
				if (sampleIndex == SFX_POLICE_RADIO_SHORESIDE_VALE &&
					(strcmp(zone->name, SubZo2Label) == 0 || strcmp(zone->name, SubZo3Label) == 0)) {
					m_sPoliceRadioQueue.Add(SFX_NORTH);
					m_sPoliceRadioQueue.Add(SFX_EAST);
				} else {
					rangeX = zone->maxx - zone->minx;
					rangeY = zone->maxy - zone->miny;
					halfX = 0.5f * rangeX + zone->minx;
					halfY = 0.5f * rangeY + zone->miny;
					quarterX = 0.25f * rangeX;
					quarterY = 0.25f * rangeY;

					if (m_aCrimes[i].position.y > halfY + quarterY) {
						m_sPoliceRadioQueue.Add(SFX_NORTH);
						processed = TRUE;
					} else if (m_aCrimes[i].position.y < halfY - quarterY) {
						m_sPoliceRadioQueue.Add(SFX_SOUTH);
						processed = TRUE;
					}

					if (m_aCrimes[i].position.x > halfX + quarterX)
						m_sPoliceRadioQueue.Add(SFX_EAST);
					else if (m_aCrimes[i].position.x < halfX - quarterX)
						m_sPoliceRadioQueue.Add(SFX_WEST);
					else if (!processed)
						m_sPoliceRadioQueue.Add(SFX_CENTRAL);

					m_sPoliceRadioQueue.Add(sampleIndex);
					m_sPoliceRadioQueue.Add(m_anRandomTable[2] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
					m_sPoliceRadioQueue.Add(TOTAL_AUDIO_SAMPLES);
				}
				break;
			}
		}
	}
	m_aCrimes[i].type = CRIME_NONE;
	AgeCrimes();
	return TRUE;
}

Const uint32 gCarColourTable[][3] = {
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLACK, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_WHITE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_BRIGHT, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_BLUE, SFX_POLICE_RADIO_GREY},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_RED, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_ORANGE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_YELLOW, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_GREEN, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_BLUE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_PURPLE, SFX_POLICE_RADIO_BLUE},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, SFX_POLICE_RADIO_GREY},
#else
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_PURPLE, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
#else
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
#endif
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
#ifdef FIX_BUGS
	{SFX_POLICE_RADIO_LIGHT, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
#else
	{TOTAL_AUDIO_SAMPLES, SFX_POLICE_RADIO_SILVER, TOTAL_AUDIO_SAMPLES},
#endif
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_LIGHT, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES},
	{SFX_POLICE_RADIO_DARK, TOTAL_AUDIO_SAMPLES, TOTAL_AUDIO_SAMPLES}
};

void
cAudioManager::SetupSuspectLastSeenReport()
{
	CVehicle *veh;
	uint8 color1;
	uint32 main_color;
	uint32 sample;

	uint32 color_pre_modifier;
	uint32 color_post_modifier;

	if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
		veh = FindPlayerVehicle();
		if (veh != nil) {
			if (POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue > 9) {
				color1 = veh->m_currentColour1;
				if (color1 >= ARRAY_SIZE(gCarColourTable)) {
					debug("\n *** UNKNOWN CAR COLOUR %d *** ", color1);
				} else {
					main_color = gCarColourTable[color1][1];
					color_pre_modifier = gCarColourTable[color1][0];
					color_post_modifier = gCarColourTable[color1][2];
					switch (veh->GetModelIndex()) {
#ifdef FIX_BUGS
					case MI_COLUMB:
						main_color = SFX_POLICE_RADIO_BLUE;
						color_pre_modifier = color_post_modifier = TOTAL_AUDIO_SAMPLES;
#endif
					case MI_LANDSTAL:
					case MI_BLISTA: sample = SFX_POLICE_RADIO_CRUISER; break;
#ifdef FIX_BUGS
					case MI_YARDIE:
						color_pre_modifier = TOTAL_AUDIO_SAMPLES;
						main_color = SFX_POLICE_RADIO_RED;
						color_post_modifier = SFX_POLICE_RADIO_YELLOW;
						sample = SFX_POLICE_RADIO_CONVERTIBLE; break;
					case MI_DIABLOS:
						main_color = SFX_POLICE_RADIO_BLACK;
#endif
					case MI_IDAHO:
					case MI_STALLION: sample = SFX_POLICE_RADIO_CONVERTIBLE; break;
#ifdef FIX_BUGS
					case MI_YAKUZA:
						color_pre_modifier = TOTAL_AUDIO_SAMPLES;
						main_color = SFX_POLICE_RADIO_SILVER;
						color_post_modifier = SFX_POLICE_RADIO_RED;
#endif
					case MI_STINGER:
					case MI_INFERNUS:
					case MI_CHEETAH:
					case MI_BANSHEE: sample = SFX_POLICE_RADIO_SPORTS_CAR; break;
#ifdef FIX_BUGS
					case MI_MAFIA:
						color_pre_modifier = color_post_modifier = TOTAL_AUDIO_SAMPLES;
						main_color = SFX_POLICE_RADIO_GREY;
					case MI_KURUMA:
#endif
					case MI_PEREN:
					case MI_SENTINEL:
					case MI_FBICAR: sample = SFX_POLICE_RADIO_SALOON; break;
					case MI_PATRIOT:
					case MI_BOBCAT: sample = SFX_POLICE_RADIO_PICKUP; break;
					case MI_FIRETRUCK: sample = SFX_POLICE_RADIO_FIRE_TRUCK; break;
#ifdef FIX_BUGS
					case MI_LINERUN:
					case MI_FLATBED:
#endif
					case MI_TRASH:
					case MI_BARRACKS: sample = SFX_POLICE_RADIO_TRUCK; break;
					case MI_STRETCH: sample = SFX_POLICE_RADIO_LIMO; break;
#ifdef FIX_BUGS
					case MI_CORPSE:
#endif
					case MI_MANANA:
					case MI_ESPERANT: sample = SFX_POLICE_RADIO_2_DOOR; break;
#ifdef FIX_BUGS
					case MI_HOODS:
						color_pre_modifier = TOTAL_AUDIO_SAMPLES;
						main_color = SFX_POLICE_RADIO_BLUE;
						color_post_modifier = SFX_POLICE_RADIO_GREEN;
					case MI_BELLYUP:
					case MI_YANKEE:
					case MI_TOYZ:
					case MI_MRWONGS:
					case MI_PANLANT:
#endif
					case MI_PONY:
					case MI_MULE:
					case MI_MOONBEAM:
					case MI_ENFORCER:
					case MI_SECURICA:
					case MI_RUMPO: sample = SFX_POLICE_RADIO_VAN; break;
					case MI_AMBULAN: sample = SFX_POLICE_RADIO_AMBULANCE; break;
					case MI_TAXI:
					case MI_CABBIE:
					case MI_BORGNINE: sample = SFX_POLICE_RADIO_TAXI; break;
					case MI_MRWHOOP:
						sample = SFX_POLICE_RADIO_ICE_CREAM_VAN;
						break;
					case MI_BFINJECT: sample = SFX_POLICE_RADIO_BUGGY; break;
					case MI_POLICE: sample = SFX_POLICE_RADIO_POLICE_CAR; break;
#ifdef FIX_BUGS
					case MI_SPEEDER:
					case MI_REEFER:
					case MI_GHOST:
#endif
					case MI_PREDATOR: sample = SFX_POLICE_RADIO_BOAT; break;
					case MI_BUS:
					case MI_COACH: sample = SFX_POLICE_RADIO_BUS; break;
					case MI_RHINO:
						sample = SFX_POLICE_RADIO_TANK;
						main_color = TOTAL_AUDIO_SAMPLES;
						color_post_modifier = TOTAL_AUDIO_SAMPLES;
						break;
					case MI_TRAIN:
						sample = SFX_POLICE_RADIO_SUBWAY_CAR;
						main_color = TOTAL_AUDIO_SAMPLES;
						color_post_modifier = TOTAL_AUDIO_SAMPLES;

						break;
					default:
						debug("\n *** UNKNOWN CAR MODEL INDEX %d *** ", veh->GetModelIndex());
						return;
					}
					m_sPoliceRadioQueue.Add(m_anRandomTable[4] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
					m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT);
					if (m_anRandomTable[3] % 2)
						m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_LAST_SEEN);
#ifdef FIX_BUGS
					if (main_color == SFX_POLICE_RADIO_ORANGE && color_pre_modifier == TOTAL_AUDIO_SAMPLES)
#else
					if (main_color == SFX_POLICE_RADIO_ORANGE)
#endif
						m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_IN_AN);
					else
						m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_IN_A);
					if (color_pre_modifier != TOTAL_AUDIO_SAMPLES)
						m_sPoliceRadioQueue.Add(color_pre_modifier);
					if (main_color != TOTAL_AUDIO_SAMPLES)
						m_sPoliceRadioQueue.Add(main_color);
					if (color_post_modifier != TOTAL_AUDIO_SAMPLES)
						m_sPoliceRadioQueue.Add(color_post_modifier);
					m_sPoliceRadioQueue.Add(sample);
					m_sPoliceRadioQueue.Add(m_anRandomTable[0] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
					m_sPoliceRadioQueue.Add(TOTAL_AUDIO_SAMPLES);
				}
			}
		} else if (POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue > 4) {
			m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_MESSAGE_NOISE_1);
			m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT);
			m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_ON_FOOT);
			m_sPoliceRadioQueue.Add(m_anRandomTable[0] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
			m_sPoliceRadioQueue.Add(TOTAL_AUDIO_SAMPLES);
		}
	}
}

void
cAudioManager::ReportCrime(eCrimeType type, const CVector &pos)
{
	int32 lastCrime = ARRAY_SIZE(m_aCrimes);
	if (m_bIsInitialised && MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE && FindPlayerPed()->m_pWanted->GetWantedLevel() > 0 &&
		(type > CRIME_NONE || type < NUM_CRIME_TYPES) && m_FrameCounter >= gMinTimeToNextReport[type]) {
		for (int32 i = 0; i < ARRAY_SIZE(m_aCrimes); i++) {
			if (m_aCrimes[i].type != CRIME_NONE) {
				if (m_aCrimes[i].type == type) {
					m_aCrimes[i].position = pos;
					m_aCrimes[i].timer = 0;
					return;
				}
			} else {
				lastCrime = i;
			}
		}

		if (lastCrime < ARRAY_SIZE(m_aCrimes)) {
			m_aCrimes[lastCrime].type = type;
			m_aCrimes[lastCrime].position = pos;
			m_aCrimes[lastCrime].timer = 0;
			gMinTimeToNextReport[type] = m_FrameCounter + 500;
		}
	}
}

void
cAudioManager::PlaySuspectLastSeen(float x, float y, float z)
{
	int16 audioZone;
	CZone *zone;
	float rangeX;
	float rangeY;
	float halfX;
	float halfY;
	float quarterX;
	float quarterY;
	uint32 sample;
	bool8 processed = FALSE;
	CVector vec = CVector(x, y, z);

	if (!m_bIsInitialised) return;

	if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE && POLICE_RADIO_QUEUE_MAX_SAMPLES - m_sPoliceRadioQueue.m_nSamplesInQueue > 9) {
		audioZone = CTheZones::FindAudioZone(&vec);
		if (audioZone >= 0 && audioZone < NUMAUDIOZONES) {
			zone = CTheZones::GetAudioZone(audioZone);
			for (int i = 0; i < NUMAUDIOZONES; i++) {
				if (strcmp(zone->name, ZoneSfx[i].m_aName) == 0) {
					sample = ZoneSfx[i].m_nSampleIndex;
					m_sPoliceRadioQueue.Add(m_anRandomTable[4] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
					m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_SUSPECT);
					m_sPoliceRadioQueue.Add(SFX_POLICE_RADIO_LAST_SEEN);
					m_sPoliceRadioQueue.Add(SFX_IN);
					if (sample == SFX_POLICE_RADIO_SHORESIDE_VALE &&
						(strcmp(zone->name, SubZo2Label) == 0 ||
							strcmp(zone->name, SubZo3Label) == 0)) {
						m_sPoliceRadioQueue.Add(SFX_NORTH);
						m_sPoliceRadioQueue.Add(SFX_EAST);
					} else {
						rangeX = zone->maxx - zone->minx;
						rangeY = zone->maxy - zone->miny;
						halfX = 0.5f * rangeX + zone->minx;
						halfY = 0.5f * rangeY + zone->miny;
						quarterX = 0.25f * rangeX;
						quarterY = 0.25f * rangeY;

						if (vec.y > halfY + quarterY) {
							m_sPoliceRadioQueue.Add(SFX_NORTH);
							processed = TRUE;
						} else if (vec.y < halfY - quarterY) {
							m_sPoliceRadioQueue.Add(SFX_SOUTH);
							processed = TRUE;
						}

						if (vec.x > halfX + quarterX)
							m_sPoliceRadioQueue.Add(SFX_EAST);
						else if (vec.x < halfX - quarterX)
							m_sPoliceRadioQueue.Add(SFX_WEST);
						else if (!processed)
							m_sPoliceRadioQueue.Add(SFX_CENTRAL);
					}
					m_sPoliceRadioQueue.Add(sample);
					m_sPoliceRadioQueue.Add(m_anRandomTable[2] % 3 + SFX_POLICE_RADIO_MESSAGE_NOISE_1);
					m_sPoliceRadioQueue.Add(TOTAL_AUDIO_SAMPLES);
					gSpecialSuspectLastSeenReport = TRUE;
					break;
				}
			}
		}
	}
}

void
cAudioManager::AgeCrimes()
{
	for (uint8 i = 0; i < ARRAY_SIZE(m_aCrimes); i++) {
		if (m_aCrimes[i].type != CRIME_NONE) {
			if (++m_aCrimes[i].timer > 1500) m_aCrimes[i].type = CRIME_NONE;
		}
	}
}


code:
#pragma once

#include "Crime.h"
#include "AudioSamples.h"

struct cAMCrime {
	int32 type;
	CVector position;
	uint16 timer;

	cAMCrime()
	{
		type = CRIME_NONE;
		position = CVector(0.0f, 0.0f, 0.0f);
		timer = 0;
	}
};

VALIDATE_SIZE(cAMCrime, 20);

#define POLICE_RADIO_QUEUE_MAX_SAMPLES 60

class cPoliceRadioQueue
{
public:
	uint32 m_aSamples[POLICE_RADIO_QUEUE_MAX_SAMPLES];
	uint8 m_nSamplesInQueue;
	uint8 m_nAddOffset;
	uint8 m_nRemoveOffset;

	cPoliceRadioQueue()
	{
		Reset();
	}

	void Reset()
	{
		m_nAddOffset = 0;
		m_nRemoveOffset = 0;
		m_nSamplesInQueue = 0;
	}

	bool8 Add(uint32 sample)
	{
		if (m_nSamplesInQueue != POLICE_RADIO_QUEUE_MAX_SAMPLES) {
			m_aSamples[m_nAddOffset] = sample;
			m_nSamplesInQueue++;
			m_nAddOffset = (m_nAddOffset + 1) % POLICE_RADIO_QUEUE_MAX_SAMPLES;
			return TRUE;
		}
		return FALSE;
	}

	uint32 Remove()
	{
		if (m_nSamplesInQueue != 0) {
			uint32 sample = m_aSamples[m_nRemoveOffset];
			m_nSamplesInQueue--;
			m_nRemoveOffset = (m_nRemoveOffset + 1) % POLICE_RADIO_QUEUE_MAX_SAMPLES;
			return sample;
		}
		return TOTAL_AUDIO_SAMPLES;
	}
};

VALIDATE_SIZE(cPoliceRadioQueue, 244);


code:
#pragma once

enum eRadioStation
{
	HEAD_RADIO,
	DOUBLE_CLEF,
	JAH_RADIO,
	RISE_FM,
	LIPS_106,
	GAME_FM,
	MSX_FM,
	FLASHBACK,
	CHATTERBOX,
	USERTRACK,
	POLICE_RADIO = 10,
	NUM_RADIOS = 10,
	RADIO_OFF = 11,
};

enum eMusicMode
{
	MUSICMODE_FRONTEND = 0,
	MUSICMODE_GAME,
	MUSICMODE_CUTSCENE,
	MUSICMODE_DISABLE,
	MUSICMODE_DISABLED,
};

enum eStreamedSounds
{
	STREAMED_SOUND_RADIO_HEAD,
	STREAMED_SOUND_RADIO_CLASSIC,
	STREAMED_SOUND_RADIO_KJAH,
	STREAMED_SOUND_RADIO_RISE,
	STREAMED_SOUND_RADIO_LIPS,
	STREAMED_SOUND_RADIO_GAME,
	STREAMED_SOUND_RADIO_MSX,
	STREAMED_SOUND_RADIO_FLASH,
	STREAMED_SOUND_RADIO_CHAT,
	STREAMED_SOUND_RADIO_MP3_PLAYER,
	STREAMED_SOUND_RADIO_POLICE,
	STREAMED_SOUND_CITY_AMBIENT,
	STREAMED_SOUND_WATER_AMBIENT,
	STREAMED_SOUND_ANNOUNCE_COMMERCIAL_OPEN,
	STREAMED_SOUND_ANNOUNCE_SUBURBAN_OPEN,
	STREAMED_SOUND_NEWS_INTRO,
	STREAMED_SOUND_BANK_INTRO,
	STREAMED_SOUND_CUTSCENE_LUIGI1_LG,
	STREAMED_SOUND_CUTSCENE_LUIGI2_DSB,
	STREAMED_SOUND_CUTSCENE_LUIGI3_DM,
	STREAMED_SOUND_CUTSCENE_LUIGI4_PAP,
	STREAMED_SOUND_CUTSCENE_LUIGI5_TFB,
	STREAMED_SOUND_CUTSCENE_JOEY0_DM2,
	STREAMED_SOUND_CUTSCENE_JOEY1_LFL,
	STREAMED_SOUND_CUTSCENE_JOEY2_KCL,
	STREAMED_SOUND_CUTSCENE_JOEY3_VH,
	STREAMED_SOUND_CUTSCENE_JOEY4_ETH,
	STREAMED_SOUND_CUTSCENE_JOEY5_DST,
	STREAMED_SOUND_CUTSCENE_JOEY6_TBJ,
	STREAMED_SOUND_CUTSCENE_TONI1_TOL,
	STREAMED_SOUND_CUTSCENE_TONI2_TPU,
	STREAMED_SOUND_CUTSCENE_TONI3_MAS,
	STREAMED_SOUND_CUTSCENE_TONI4_TAT,
	STREAMED_SOUND_CUTSCENE_TONI5_BF,
	STREAMED_SOUND_CUTSCENE_SAL0_MAS,
	STREAMED_SOUND_CUTSCENE_SAL1_PF,
	STREAMED_SOUND_CUTSCENE_SAL2_CTG,
	STREAMED_SOUND_CUTSCENE_SAL3_RTC,
	STREAMED_SOUND_CUTSCENE_SAL5_LRQ,
	STREAMED_SOUND_CUTSCENE_SAL4_BDBA,
	STREAMED_SOUND_CUTSCENE_SAL4_BDBB,
	STREAMED_SOUND_CUTSCENE_SAL2_CTG2,
	STREAMED_SOUND_CUTSCENE_SAL4_BDBD,
	STREAMED_SOUND_CUTSCENE_SAL5_LRQB,
	STREAMED_SOUND_CUTSCENE_SAL5_LRQC,
	STREAMED_SOUND_CUTSCENE_ASUKA_1_SSO,
	STREAMED_SOUND_CUTSCENE_ASUKA_2_PP,
	STREAMED_SOUND_CUTSCENE_ASUKA_3_SS,
	STREAMED_SOUND_CUTSCENE_ASUKA_4_PDR,
	STREAMED_SOUND_CUTSCENE_ASUKA_5_K2FT,
	STREAMED_SOUND_CUTSCENE_KENJI1_KBO,
	STREAMED_SOUND_CUTSCENE_KENJI2_GIS,
	STREAMED_SOUND_CUTSCENE_KENJI3_DS,
	STREAMED_SOUND_CUTSCENE_KENJI4_SHI,
	STREAMED_SOUND_CUTSCENE_KENJI5_SD,
	STREAMED_SOUND_CUTSCENE_RAY0_PDR2,
	STREAMED_SOUND_CUTSCENE_RAY1_SW,
	STREAMED_SOUND_CUTSCENE_RAY2_AP,
	STREAMED_SOUND_CUTSCENE_RAY3_ED,
	STREAMED_SOUND_CUTSCENE_RAY4_GF,
	STREAMED_SOUND_CUTSCENE_RAY5_PB,
	STREAMED_SOUND_CUTSCENE_RAY6_MM,
	STREAMED_SOUND_CUTSCENE_DONALD1_STOG,
	STREAMED_SOUND_CUTSCENE_DONALD2_KK,
	STREAMED_SOUND_CUTSCENE_DONALD3_ADO,
	STREAMED_SOUND_CUTSCENE_DONALD5_ES,
	STREAMED_SOUND_CUTSCENE_DONALD7_MLD,
	STREAMED_SOUND_CUTSCENE_DONALD4_GTA,
	STREAMED_SOUND_CUTSCENE_DONALD4_GTA2,
	STREAMED_SOUND_CUTSCENE_DONALD6_STS,
	STREAMED_SOUND_CUTSCENE_ASUKA6_BAIT,
	STREAMED_SOUND_CUTSCENE_ASUKA7_ETG,
	STREAMED_SOUND_CUTSCENE_ASUKA8_PS,
	STREAMED_SOUND_CUTSCENE_ASUKA9_ASD,
	STREAMED_SOUND_CUTSCENE_KENJI4_SHI2,
	STREAMED_SOUND_CUTSCENE_CATALINA1_TEX,
	STREAMED_SOUND_CUTSCENE_ELBURRO1_PH1,
	STREAMED_SOUND_CUTSCENE_ELBURRO2_PH2,
	STREAMED_SOUND_CUTSCENE_ELBURRO3_PH3,
	STREAMED_SOUND_CUTSCENE_ELBURRO4_PH4,
	STREAMED_SOUND_CUTSCENE_YARDIE_PH1,
	STREAMED_SOUND_CUTSCENE_YARDIE_PH2,
	STREAMED_SOUND_CUTSCENE_YARDIE_PH3,
	STREAMED_SOUND_CUTSCENE_YARDIE_PH4,
	STREAMED_SOUND_CUTSCENE_HOODS_PH1,
	STREAMED_SOUND_CUTSCENE_HOODS_PH2,
	STREAMED_SOUND_CUTSCENE_HOODS_PH3,
	STREAMED_SOUND_CUTSCENE_HOODS_PH4,
	STREAMED_SOUND_CUTSCENE_HOODS_PH5,
	STREAMED_SOUND_CUTSCENE_MARTY_PH1,
	STREAMED_SOUND_CUTSCENE_MARTY_PH2,
	STREAMED_SOUND_CUTSCENE_MARTY_PH3,
	STREAMED_SOUND_CUTSCENE_MARTY_PH4,
	STREAMED_SOUND_MISSION_COMPLETED,
	STREAMED_SOUND_GAME_COMPLETED,
#ifndef GTA_PS2
	SFX_MISSION_LIB_A1,
	SFX_MISSION_LIB_A2,
	SFX_MISSION_LIB_A,
	SFX_MISSION_LIB_B,
	SFX_MISSION_LIB_C,
	SFX_MISSION_LIB_D,
	SFX_MISSION_L2_A,
	SFX_MISSION_J4T_1,
	SFX_MISSION_J4T_2,
	SFX_MISSION_J4T_3,
	SFX_MISSION_J4T_4,
	SFX_MISSION_J4_A,
	SFX_MISSION_J4_B,
	SFX_MISSION_J4_C,
	SFX_MISSION_J4_D,
	SFX_MISSION_J4_E,
	SFX_MISSION_J4_F,
	SFX_MISSION_J6_1,
	SFX_MISSION_J6_A,
	SFX_MISSION_J6_B,
	SFX_MISSION_J6_C,
	SFX_MISSION_J6_D,
	SFX_MISSION_T4_A,
	SFX_MISSION_S1_A,
	SFX_MISSION_S1_A1,
	SFX_MISSION_S1_B,
	SFX_MISSION_S1_C,
	SFX_MISSION_S1_C1,
	SFX_MISSION_S1_D,
	SFX_MISSION_S1_E,
	SFX_MISSION_S1_F,
	SFX_MISSION_S1_G,
	SFX_MISSION_S1_H,
	SFX_MISSION_S1_I,
	SFX_MISSION_S1_J,
	SFX_MISSION_S1_K,
	SFX_MISSION_S1_L,
	SFX_MISSION_S3_A,
	SFX_MISSION_S3_B,
	SFX_MISSION_EL3_A,
	SFX_MISSION_MF1_A,
	SFX_MISSION_MF2_A,
	SFX_MISSION_MF3_A,
	SFX_MISSION_MF3_B,
	SFX_MISSION_MF3_B1,
	SFX_MISSION_MF3_C,
	SFX_MISSION_MF4_A,
	SFX_MISSION_MF4_B,
	SFX_MISSION_MF4_C,
	SFX_MISSION_A1_A,
	SFX_MISSION_A3_A,
	SFX_MISSION_A5_A,
	SFX_MISSION_A4_A,
	SFX_MISSION_A4_B,
	SFX_MISSION_A4_C,
	SFX_MISSION_A4_D,
	SFX_MISSION_K1_A,
	SFX_MISSION_K3_A,
	SFX_MISSION_R1_A,
	SFX_MISSION_R2_A,
	SFX_MISSION_R2_B,
	SFX_MISSION_R2_C,
	SFX_MISSION_R2_D,
	SFX_MISSION_R2_E,
	SFX_MISSION_R2_F,
	SFX_MISSION_R2_G,
	SFX_MISSION_R2_H,
	SFX_MISSION_R5_A,
	SFX_MISSION_R6_A,
	SFX_MISSION_R6_A1,
	SFX_MISSION_R6_B,
	SFX_MISSION_LO2_A,
	SFX_MISSION_LO6_A,
	SFX_MISSION_YD2_A,
	SFX_MISSION_YD2_B,
	SFX_MISSION_YD2_C,
	SFX_MISSION_YD2_C1,
	SFX_MISSION_YD2_D,
	SFX_MISSION_YD2_E,
	SFX_MISSION_YD2_F,
	SFX_MISSION_YD2_G,
	SFX_MISSION_YD2_H,
	SFX_MISSION_YD2_ASS,
	SFX_MISSION_YD2_OK,
	SFX_MISSION_H5_A,
	SFX_MISSION_H5_B,
	SFX_MISSION_H5_C,
	SFX_MISSION_AMMU_A,
	SFX_MISSION_AMMU_B,
	SFX_MISSION_AMMU_C,
	SFX_MISSION_DOOR_1,
	SFX_MISSION_DOOR_2,
	SFX_MISSION_DOOR_3,
	SFX_MISSION_DOOR_4,
	SFX_MISSION_DOOR_5,
	SFX_MISSION_DOOR_6,
	SFX_MISSION_T3_A,
	SFX_MISSION_T3_B,
	SFX_MISSION_T3_C,
	SFX_MISSION_K1_B,
	SFX_MISSION_CAT1,
#endif
	TOTAL_STREAMED_SOUNDS,
	NO_TRACK,
};

enum AudioEntityHandle {
	AEHANDLE_NONE = -5,
	AEHANDLE_ERROR_NOAUDIOSYS = -4,
	AEHANDLE_ERROR_NOFREESLOT = -3,
	AEHANDLE_ERROR_NOENTITY = -2,
	AEHANDLE_ERROR_BADAUDIOTYPE = -1,
};

enum eAudioType
{
	AUDIOTYPE_PHYSICAL = 0,
	AUDIOTYPE_EXPLOSION,
	AUDIOTYPE_FIRE,
	AUDIOTYPE_WEATHER,
	AUDIOTYPE_CRANE,
	AUDIOTYPE_SCRIPTOBJECT,
	AUDIOTYPE_BRIDGE,
	AUDIOTYPE_COLLISION,
	AUDIOTYPE_FRONTEND,
	AUDIOTYPE_PROJECTILE,
	AUDIOTYPE_GARAGE,
	AUDIOTYPE_FIREHYDRANT,
	AUDIOTYPE_WATERCANNON,
	AUDIOTYPE_POLICERADIO,
	TOTAL_AUDIO_TYPES,
};

#ifdef GTA_PS2
enum
{
	NUM_CHANNELS_GENERIC = 43,
	CHANNEL_POLICE_RADIO = NUM_CHANNELS_GENERIC,
	CHANNEL_MISSION_AUDIO,
	CHANNEL_PLAYER_VEHICLE_ENGINE,
	NUM_CHANNELS
};
#else
enum
{
#ifdef PS2_AUDIO_CHANNELS
	NUM_CHANNELS_GENERIC = 43,
#else
	NUM_CHANNELS_GENERIC = 27,
#endif
	CHANNEL_POLICE_RADIO,
	NUM_CHANNELS
};
#endif