code:
#define WITHWINDOWS
#include "common.h"

#ifdef AUDIO_MSS
#include <shlobj.h>
#include <shlguid.h>

#include <time.h>

#include "eax.h"
#include "eax-util.h"
#include "mss.h"

#include "sampman.h"
#include "AudioManager.h"
#include "MusicManager.h"
#include "Frontend.h"
#include "Timer.h"

#pragma comment( lib, "mss32.lib" )

cSampleManager SampleManager;
uint32 BankStartOffset[MAX_SFX_BANKS];
///////////////////////////////////////////////////////////////

char SampleBankDescFilename[] = "AUDIO\\SFX.SDT";
char SampleBankDataFilename[] = "AUDIO\\SFX.RAW";

FILE *fpSampleDescHandle;
FILE *fpSampleDataHandle;
int8  gBankLoaded                  [MAX_SFX_BANKS];
int32 nSampleBankDiscStartOffset   [MAX_SFX_BANKS];
int32 nSampleBankSize              [MAX_SFX_BANKS];
int32 nSampleBankMemoryStartAddress[MAX_SFX_BANKS];
int32 _nSampleDataEndOffset;

int32 nPedSlotSfx    [MAX_PEDSFX];
int32 nPedSlotSfxAddr[MAX_PEDSFX];
uint8 nCurrentPedSlot;

uint8 nChannelVolume[MAXCHANNELS+MAX2DCHANNELS];

uint32 nStreamLength[TOTAL_STREAMED_SOUNDS];

///////////////////////////////////////////////////////////////
struct tMP3Entry
{
	char aFilename[MAX_PATH];

	uint32 nTrackLength;
	uint32 nTrackStreamPos;

	tMP3Entry *pNext;
	char *pLinkPath;
};

uint32 nNumMP3s;
tMP3Entry *_pMP3List;
char _mp3DirectoryPath[MAX_PATH];
HSTREAM mp3Stream [MAX_STREAMS];
int8 nStreamPan   [MAX_STREAMS];
int8 nStreamVolume[MAX_STREAMS];
uint32 _CurMP3Index;
int32 _CurMP3Pos;
bool8 _bIsMp3Active;

#if GTA_VERSION >= GTA3_PC_11 || defined(NO_CDCHECK)
bool8 _bUseHDDAudio;
char _aHDDPath[MAX_PATH];
#endif
///////////////////////////////////////////////////////////////

bool8 _bSampmanInitialised = FALSE;
#ifdef EXTERNAL_3D_SOUND
//
// Miscellaneous globals / defines

//	Env		Size	Diffus	Room	RoomHF	RoomLF	DecTm	DcHF	DcLF	Refl	RefDel	Ref Pan				Revb	RevDel		Rev Pan				EchTm	EchDp	ModTm	ModDp	AirAbs	HFRef		LFRef	RRlOff	FLAGS

EAXLISTENERPROPERTIES StartEAX3 =
	{26,	1.7f,	0.8f,	-1000,	-1000,	-100,	4.42f,	0.14f,	1.00f,	429,	0.014f,	0.00f,0.00f,0.00f,	1023,	0.021f,		0.00f,0.00f,0.00f,	0.250f,	0.000f,	0.250f,	0.000f,	-5.0f,	2727.1f,	250.0f,	0.00f,	0x3f };

EAXLISTENERPROPERTIES FinishEAX3 =
	{26,	100.0f,	1.0f,	0,		-1000,	-2200,	20.0f,	1.39f,	1.00f,	1000,	0.069f,	0.00f,0.00f,0.00f,	400,	0.100f,		0.00f,0.00f,0.00f,	0.250f,	1.000f,	3.982f,	0.000f,	-18.0f,	3530.8f,	417.9f,	6.70f,	0x3f };

EAXLISTENERPROPERTIES EAX3Params;

S32         prevprovider=-1;
S32         curprovider=-1;
S32         usingEAX=0;
S32         usingEAX3=0;
HPROVIDER   opened_provider=0;
H3DSAMPLE   opened_samples[MAXCHANNELS] = {0};
#endif
HSAMPLE     opened_2dsamples[MAX2DCHANNELS] = {0};
HDIGDRIVER  DIG;
#ifdef EXTERNAL_3D_SOUND
S32         speaker_type=0;
U32 _maxSamples;
float _fPrevEaxRatioDestination;
bool8 _usingMilesFast2D;
float _fEffectsLevel;

struct
{
	HPROVIDER id;
	char name[80];
}providers[MAXPROVIDERS];

typedef struct provider_stuff
{
  char* name;
  HPROVIDER id;
} provider_stuff;

static int __cdecl comp(const provider_stuff*s1,const provider_stuff*s2)
{
  return( _stricmp(s1->name,s2->name) );
}

static void
add_providers()
{
	provider_stuff pi[MAXPROVIDERS];
	U32   n,i,j;

	SampleManager.SetNum3DProvidersAvailable(0);

	HPROENUM next = HPROENUM_FIRST;

	n=0;
	while (AIL_enumerate_3D_providers(&next, &pi[n].id, &pi[n].name) && (n<MAXPROVIDERS))
	{
		++n;
	}

	qsort(pi,n,sizeof(pi[0]), (int(__cdecl*)(void const*, void const*))comp);

	for(i=0;i<n;i++)
	{
		providers[i].id=pi[i].id;
		strcpy(providers[i].name, pi[i].name);
		SampleManager.Set3DProviderName(i, providers[i].name);
	}

	SampleManager.SetNum3DProvidersAvailable(n);

	for(j=n;j<MAXPROVIDERS;j++)
		SampleManager.Set3DProviderName(j, NULL);
}

static void
release_existing()
{
	for ( U32 i = 0; i < _maxSamples; i++ )
	{
		if ( opened_samples[i] )
		{
			AIL_release_3D_sample_handle(opened_samples[i]);
			opened_samples[i] = NULL;
		}
	}

	if ( opened_provider )
	{
		AIL_close_3D_provider(opened_provider);
		opened_provider = NULL;
	}

	_fPrevEaxRatioDestination = 0.0f;
	_usingMilesFast2D = FALSE;
	_fEffectsLevel = 0.0f;
}

static bool8
set_new_provider(S32 index)
{
	DWORD result;

	if ( curprovider == index )
		return TRUE;

	//close the already opened provider
	curprovider = index;

	release_existing();

	if ( curprovider != -1 )
	{
		if ( !strcmp(providers[index].name, "Dolby Surround") )
			_maxSamples = MAXCHANNELS_SURROUND;
		else
			_maxSamples = MAXCHANNELS;

		AIL_set_3D_provider_preference(providers[index].id, "Maximum supported samples", &_maxSamples);

		//load the new provider
		result = AIL_open_3D_provider(providers[index].id);

		if (result != M3D_NOERR)
		{
			curprovider=-1;

			OutputDebugStringA(AIL_last_error());

			release_existing();

			return FALSE;
		}
		else
		{
			opened_provider=providers[index].id;

			//see if we're running under an EAX compatible provider

			if ( !strcmp(providers[index].name, "Creative Labs EAX 3 (TM)") )
			{
				usingEAX = 1;
				usingEAX3 = 1;
			}
			else
			{
				usingEAX3 = 0;

				result=AIL_3D_room_type(opened_provider);
				usingEAX=(((S32)result)!=-1)?1:0; // will be something other than -1 on EAX
			}

			if ( usingEAX3 )
			{
				OutputDebugString("DOING SPECIAL EAX 3 STUFF!");
				AIL_set_3D_provider_preference(opened_provider, "EAX all parameters", &FinishEAX3);
			}
			else if ( usingEAX )
			{
				AIL_set_3D_room_type(opened_provider, ENVIRONMENT_CAVE);

				if ( !strcmp(providers[index].name, "Miles Fast 2D Positional Audio") )
					_usingMilesFast2D = TRUE;
			}

			AIL_3D_provider_attribute(opened_provider, "Maximum supported samples", &_maxSamples);

			if ( _maxSamples > MAXCHANNELS )
				_maxSamples = MAXCHANNELS;

			SampleManager.SetSpeakerConfig(speaker_type);

			//obtain a 3D sample handles
			for ( U32 i = 0; i < _maxSamples; ++i )
			{
				opened_samples[i] = AIL_allocate_3D_sample_handle(opened_provider);
				if ( opened_samples[i] != NULL )
					AIL_set_3D_sample_effects_level(opened_samples[i], 0.0f);
			}

			return TRUE;
		}
	}

	return FALSE;
}
#endif

cSampleManager::cSampleManager(void) :
	m_nNumberOfProviders(0)
{
	;
}

cSampleManager::~cSampleManager(void)
{

}

#ifdef EXTERNAL_3D_SOUND
void
cSampleManager::SetSpeakerConfig(int32 which)
{
	switch ( which )
	{
		case 1:
			speaker_type=AIL_3D_2_SPEAKER;
			break;

		case 2:
			speaker_type=AIL_3D_HEADPHONE;
			break;

		case 3:
			speaker_type=AIL_3D_4_SPEAKER;
			break;

		default:
			return;
			break;
	}

	if (opened_provider)
		AIL_set_3D_speaker_type(opened_provider, speaker_type);
}

uint32
cSampleManager::GetMaximumSupportedChannels(void)
{
	if ( _maxSamples > MAXCHANNELS )
		return MAXCHANNELS;

	return _maxSamples;
}

uint32 cSampleManager::GetNum3DProvidersAvailable()
{
	return m_nNumberOfProviders;
}

void cSampleManager::SetNum3DProvidersAvailable(uint32 num)
{
	m_nNumberOfProviders = num;
}

char *cSampleManager::Get3DProviderName(uint8 id)
{
	return m_aAudioProviders[id];
}

void cSampleManager::Set3DProviderName(uint8 id, char *name)
{
	m_aAudioProviders[id] = name;
}

int8
cSampleManager::GetCurrent3DProviderIndex(void)
{
	return curprovider;
}

int8
cSampleManager::SetCurrent3DProvider(uint8 nProvider)
{
	S32 savedprovider = curprovider;

	if ( nProvider < m_nNumberOfProviders )
	{
		if ( set_new_provider(nProvider) )
			return curprovider;
		else if ( savedprovider != -1 && savedprovider < m_nNumberOfProviders && set_new_provider(savedprovider) )
			return curprovider;
		else
			return -1;
	}
	else
		return curprovider;
}
#endif

static bool8
_ResolveLink(char const *path, char *out)
{
	IShellLink* psl;
	WIN32_FIND_DATA fd;
	char filepath[MAX_PATH];

	CoInitialize(NULL);

	if (SUCCEEDED( CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl ) ))
	{
		IPersistFile *ppf;

		if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
		{
			WCHAR wpath[MAX_PATH];

			MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH);

			if (SUCCEEDED(ppf->Load(wpath, STGM_READ)))
			{
				/* Resolve the link */
				if (SUCCEEDED(psl->Resolve(NULL, SLR_ANY_MATCH|SLR_NO_UI|SLR_NOSEARCH)))
				{
					strcpy(filepath, path);

					if (SUCCEEDED(psl->GetPath(filepath, MAX_PATH, &fd, SLGP_UNCPRIORITY)))
					{
						OutputDebugString(fd.cFileName);

						strcpy(out, filepath);
						// FIX: Release the objects. Taken from SA.
#ifdef FIX_BUGS
						ppf->Release();
						psl->Release();
#endif
						return TRUE;
					}
				}
			}

			ppf->Release();
		}
		psl->Release();
	}

	return FALSE;
}

static void
_FindMP3s(void)
{
	tMP3Entry *pList;
	bool8 bShortcut;
	bool8 bInitFirstEntry;
	HANDLE hFind;
	char path[MAX_PATH];
	char filepath[MAX_PATH*2];
	S32 total_ms;
	WIN32_FIND_DATA fd;

	if ( GetCurrentDirectory(MAX_PATH, _mp3DirectoryPath) == 0 )
	{
		GetLastError();
		return;
	}

	OutputDebugString("Finding MP3s...");
	strcpy(path, _mp3DirectoryPath);
	strcat(path, "\\MP3\\");

	strcpy(_mp3DirectoryPath, path);
	OutputDebugString(_mp3DirectoryPath);

	strcat(path, "*");

	hFind = FindFirstFile(path, &fd);

	if ( hFind == INVALID_HANDLE_VALUE )
	{
		GetLastError();
		return;
	}

	strcpy(filepath, _mp3DirectoryPath);
	strcat(filepath, fd.cFileName);

	int32 filepathlen = strlen(filepath);

	if ( filepathlen <= 0)
	{
		FindClose(hFind);
		return;
	}

	FILE *f = fopen("MP3\\MP3Report.txt", "w");

	if ( f )
	{
		fprintf(f, "MP3 Report File\n\n");
		fprintf(f, "\"%s\"", fd.cFileName);
	}

	if ( filepathlen > 4 )
	{
		if ( !strcmp(&filepath[filepathlen - 4], ".lnk") )
		{
			if ( _ResolveLink(filepath, filepath) )
			{
				OutputDebugString("Resolving Link");
				OutputDebugString(filepath);

				if ( f ) fprintf(f, " - shortcut to \"%s\"", filepath);
			}
			else
			{
				if ( f ) fprintf(f, " - couldn't resolve shortcut");
			}

			bShortcut = TRUE;
		}
		else
			bShortcut = FALSE;
	}

	mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
	if ( mp3Stream[0] )
	{
		AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL);

		AIL_close_stream(mp3Stream[0]);
		mp3Stream[0] = NULL;

		OutputDebugString(fd.cFileName);

		_pMP3List = new tMP3Entry;

		if ( _pMP3List == NULL )
		{
			FindClose(hFind);

			if ( f )
				fclose(f);

			return;
		}

		nNumMP3s = 1;

		strcpy(_pMP3List->aFilename, fd.cFileName);

		_pMP3List->nTrackLength = total_ms;

		_pMP3List->pNext = NULL;

		pList = _pMP3List;

		if ( bShortcut )
		{
			_pMP3List->pLinkPath = new char[MAX_PATH*2];
			strcpy(_pMP3List->pLinkPath, filepath);
		}
		else
		{
			_pMP3List->pLinkPath = NULL;
		}

		if ( f ) fprintf(f, " - OK\n");

		bInitFirstEntry = FALSE;
	}
	else
	{
		strcat(filepath, " - NOT A VALID MP3");

		OutputDebugString(filepath);

		if ( f ) fprintf(f, " - not an MP3 or supported MP3 type\n");

		bInitFirstEntry = TRUE;
	}

	while ( TRUE )
	{
		if ( !FindNextFile(hFind, &fd) )
			break;

		if ( bInitFirstEntry )
		{
			strcpy(filepath, _mp3DirectoryPath);
			strcat(filepath, fd.cFileName);

			int32 filepathlen = strlen(filepath);

			if ( f ) fprintf(f, "\"%s\"", fd.cFileName);

			if ( filepathlen > 0 )
			{
				if ( filepathlen > 4 )
				{
					if ( !strcmp(&filepath[filepathlen - 4], ".lnk") )
					{
						if ( _ResolveLink(filepath, filepath) )
						{
							OutputDebugString("Resolving Link");
							OutputDebugString(filepath);

							if ( f ) fprintf(f, " - shortcut to \"%s\"", filepath);
						}
						else
						{
							if ( f ) fprintf(f, " - couldn't resolve shortcut");
						}

						bShortcut = TRUE;
					}
					else
					{
						bShortcut = FALSE;

						if ( filepathlen > MAX_PATH )
						{
							if ( f ) fprintf(f, " - Filename and path too long - %s - IGNORED)\n", filepath);

							continue;
						}
					}
				}

				mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
				if ( mp3Stream[0] )
				{
					AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL);

					AIL_close_stream(mp3Stream[0]);
					mp3Stream[0] = NULL;

					OutputDebugString(fd.cFileName);

					_pMP3List = new tMP3Entry;

					if ( _pMP3List  == NULL)
						break;

					nNumMP3s = 1;

					strcpy(_pMP3List->aFilename, fd.cFileName);

					_pMP3List->nTrackLength = total_ms;
					_pMP3List->pNext = NULL;

					if ( bShortcut )
					{
						_pMP3List->pLinkPath = new char [MAX_PATH*2];
						strcpy(_pMP3List->pLinkPath, filepath);
					}
					else
					{
						_pMP3List->pLinkPath = NULL;
					}

					pList = _pMP3List;

					if ( f ) fprintf(f, " - OK\n");

					bInitFirstEntry = FALSE;
				}
				else
				{
					strcat(filepath, " - NOT A VALID MP3");
					OutputDebugString(filepath);

					if ( f ) fprintf(f, " - not an MP3 or supported MP3 type\n");
				}
			}
		}
		else
		{
			strcpy(filepath, _mp3DirectoryPath);
			strcat(filepath, fd.cFileName);

			int32 filepathlen = strlen(filepath);

			if ( filepathlen > 0 )
			{
				if ( f ) fprintf(f, "\"%s\"", fd.cFileName);

				if ( filepathlen > 4 )
				{
					if ( !strcmp(&filepath[filepathlen - 4], ".lnk") )
					{
						if ( _ResolveLink(filepath, filepath) )
						{
							OutputDebugString("Resolving Link");
							OutputDebugString(filepath);

							if ( f ) fprintf(f, " - shortcut to \"%s\"", filepath);
						}
						else
						{
							if ( f ) fprintf(f, " - couldn't resolve shortcut");
						}

						bShortcut = TRUE;
					}
					else
					{
						bShortcut = FALSE;
					}
				}

				mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
				if ( mp3Stream[0] )
				{
					AIL_stream_ms_position(mp3Stream[0], &total_ms, NULL);

					AIL_close_stream(mp3Stream[0]);
					mp3Stream[0] = NULL;

					pList->pNext = new tMP3Entry;

					tMP3Entry *e = pList->pNext;

					if ( e == NULL )
						break;

					pList = pList->pNext;

					strcpy(e->aFilename, fd.cFileName);
					e->nTrackLength = total_ms;
					e->pNext = NULL;

					if ( bShortcut )
					{
						e->pLinkPath = new char [MAX_PATH*2];
						strcpy(e->pLinkPath, filepath);
					}
					else
					{
						e->pLinkPath = NULL;
					}

					nNumMP3s++;

					OutputDebugString(fd.cFileName);

					if ( f ) fprintf(f, " - OK\n");
				}
				else
				{
					strcat(filepath, " - NOT A VALID MP3");
					OutputDebugString(filepath);

					if ( f ) fprintf(f, " - not an MP3 or supported MP3 type\n");
				}
			}
		}
	}

	if ( f )
	{
		fprintf(f, "\nTOTAL SUPPORTED MP3s: %d\n", nNumMP3s);
		fclose(f);
	}

	FindClose(hFind);
}

static void
_DeleteMP3Entries(void)
{
	tMP3Entry *e = _pMP3List;

	while ( e != NULL )
	{
		tMP3Entry *next = e->pNext;

		if ( next == NULL )
			next = NULL;

		if ( e->pLinkPath != NULL )
		{
#ifndef FIX_BUGS
			delete   e->pLinkPath; // BUG: should be delete []
#else
			delete[] e->pLinkPath;
#endif
			e->pLinkPath = NULL;
		}

		delete e;

		if ( next )
			e = next;
		else
			e = NULL;

		nNumMP3s--;
	}

	if ( nNumMP3s != 0 )
	{
		OutputDebugString("Not all MP3 entries were deleted");
		nNumMP3s = 0;
	}

	_pMP3List = NULL;
}

static tMP3Entry *
_GetMP3EntryByIndex(uint32 idx)
{
	uint32 n = ( idx < nNumMP3s ) ? idx : 0;

	if ( _pMP3List != NULL )
	{
		tMP3Entry *e = _pMP3List;

		for ( uint32 i = 0; i < n; i++ )
			e = e->pNext;

		return e;

	}

	return NULL;
}

static inline bool8
_GetMP3PosFromStreamPos(uint32 *pPosition, tMP3Entry **pEntry)
{
	_CurMP3Index = 0;

	for ( *pEntry = _pMP3List; *pEntry != NULL; *pEntry = (*pEntry)->pNext )
	{
		if (   *pPosition >= (*pEntry)->nTrackStreamPos
			&& *pPosition <  (*pEntry)->nTrackLength + (*pEntry)->nTrackStreamPos )
		{
			*pPosition -= (*pEntry)->nTrackStreamPos;
			_CurMP3Pos = *pPosition;

			return TRUE;
		}

		_CurMP3Index++;
	}

	*pPosition = 0;
	*pEntry = _pMP3List;
	_CurMP3Pos = 0;
	_CurMP3Index = 0;

	return FALSE;
}

bool8
cSampleManager::IsMP3RadioChannelAvailable(void)
{
	return nNumMP3s != 0;
}

void
cSampleManager::ReleaseDigitalHandle(void)
{
	if ( DIG )
	{
#ifdef EXTERNAL_3D_SOUND
		prevprovider = curprovider;
		release_existing();
		curprovider = -1;
#endif
		AIL_digital_handle_release(DIG);
	}
}

void
cSampleManager::ReacquireDigitalHandle(void)
{
	if ( DIG )
	{
		AIL_digital_handle_reacquire(DIG);
#ifdef EXTERNAL_3D_SOUND
		if ( prevprovider != -1 )
			set_new_provider(prevprovider);
#endif
	}
}

bool8
cSampleManager::Initialise(void)
{
	TRACE("start");

	if ( _bSampmanInitialised )
		return TRUE;

	{
		for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ )
		{
			m_aSamples[i].nOffset    = 0;
			m_aSamples[i].nSize      = 0;
			m_aSamples[i].nFrequency = 22050;
			m_aSamples[i].nLoopStart = 0;
			m_aSamples[i].nLoopEnd   = -1;
		}

		m_nEffectsVolume     = MAX_VOLUME;
		m_nMusicVolume       = MAX_VOLUME;
		m_nEffectsFadeVolume = MAX_VOLUME;
		m_nMusicFadeVolume   = MAX_VOLUME;

		m_nMonoMode = 0;
	}

#ifdef EXTERNAL_3D_SOUND
	// miles
	TRACE("MILES");
	{
		curprovider = -1;
		prevprovider = -1;

		_usingMilesFast2D = FALSE;
		usingEAX=0;
		usingEAX3=0;

		_fEffectsLevel = 0.0f;

		_maxSamples = 0;

		opened_provider = NULL;
		DIG = NULL;

		for ( int32 i = 0; i < MAXCHANNELS; i++ )
			opened_samples[i] = NULL;
	}
#endif

	// banks
	TRACE("banks");
	{
		fpSampleDescHandle = NULL;
		fpSampleDataHandle = NULL;

		_nSampleDataEndOffset = 0;

		for ( int32 i = 0; i < MAX_SFX_BANKS; i++ )
		{
			gBankLoaded[i]                   = LOADING_STATUS_NOT_LOADED;
			nSampleBankDiscStartOffset[i]    = 0;
			nSampleBankSize[i]               = 0;
			nSampleBankMemoryStartAddress[i] = 0;
		}
	}

	// pedsfx
	TRACE("pedsfx");
	{
		for ( int32 i = 0; i < MAX_PEDSFX; i++ )
		{
			nPedSlotSfx[i]     = NO_SAMPLE;
			nPedSlotSfxAddr[i] = 0;
		}

		nCurrentPedSlot = 0;
	}

	// channel volume
	TRACE("vol");
	{
		for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ )
			nChannelVolume[i] = 0;
	}

	TRACE("mss");
	{
		AIL_set_redist_directory( "mss" );

		AIL_startup();

		AIL_set_preference(DIG_MIXER_CHANNELS, MAX_DIGITAL_MIXER_CHANNELS);

		DIG = AIL_open_digital_driver(DIGITALRATE, DIGITALBITS, DIGITALCHANNELS, 0);
		if ( DIG == NULL )
		{
			OutputDebugString(AIL_last_error());
			Terminate();
			return FALSE;
		}

#ifdef EXTERNAL_3D_SOUND
		add_providers();
#endif

		if ( !InitialiseSampleBanks() )
		{
			Terminate();
			return FALSE;
		}

		nSampleBankMemoryStartAddress[SFX_BANK_0] = (int32)AIL_mem_alloc_lock(nSampleBankSize[SFX_BANK_0]);
		if ( !nSampleBankMemoryStartAddress[SFX_BANK_0] )
		{
			Terminate();
			return FALSE;
		}

		nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = (int32)AIL_mem_alloc_lock(PED_BLOCKSIZE*MAX_PEDSFX);

	}

#ifdef AUDIO_CACHE
	TRACE("cache");
	FILE *cacheFile = fopen("audio\\sound.cache", "rb");
	if (cacheFile) {
		fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
		fclose(cacheFile);
		m_bInitialised = TRUE;
	}else {
#endif
	TRACE("cdrom");

	S32 tatalms;
	char filepath[MAX_PATH];

	{
		m_bInitialised = FALSE;

		while (TRUE)
		{
			int32 drive = 'C';

			do
			{
				char latter[2];

				latter[0] = drive;
				latter[1] = '\0';

				strcpy(m_szCDRomRootPath, latter);
				strcat(m_szCDRomRootPath, ":\\");

				if ( GetDriveType(m_szCDRomRootPath) == DRIVE_CDROM )
				{
					FILE *f;
#ifdef PS2_AUDIO_PATHS
					strcpy(filepath, m_szCDRomRootPath);
					strcat(filepath, PS2StreamedNameTable[0]);
					f = fopen(filepath, "rb");

					if ( !f )
#endif
					{
						strcpy(filepath, m_szCDRomRootPath);
						strcat(filepath, StreamedNameTable[0]);

						f = fopen(filepath, "rb");
					}
					if ( f )
					{
						fclose(f);

						bool8 bFileNotFound = FALSE;

						for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
						{
#ifdef PS2_AUDIO_PATHS
							strcpy(filepath, m_szCDRomRootPath);
							strcat(filepath, PS2StreamedNameTable[i]);

							mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
							if ( !mp3Stream[0] )
#endif
							{
								strcpy(filepath, m_szCDRomRootPath);
								strcat(filepath, StreamedNameTable[i]);

								mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
							}

							if ( mp3Stream[0] )
							{
								AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL);

								AIL_close_stream(mp3Stream[0]);
								mp3Stream[0] = NULL;

								nStreamLength[i] = tatalms;
							}
							else
							{
								bFileNotFound = TRUE;
								break;
							}
						}

						if ( !bFileNotFound )
						{
							m_bInitialised = TRUE;
							break;
						}
						else
						{
							m_bInitialised = FALSE;
							continue;
						}
					}
				}

			} while ( ++drive <= 'Z' );

			if ( !m_bInitialised )
			{
#if GTA_VERSION < GTA3_PC_STEAM && !defined(NO_CDCHECK)
				FrontEndMenuManager.WaitForUserCD();
				if ( FrontEndMenuManager.m_bQuitGameNoCD )
				{
					Terminate();
					return FALSE;
				}
				continue;
#else
				m_bInitialised = TRUE;
#endif
			}

			break;
		}
	}

#if GTA_VERSION >= GTA3_PC_11 || defined(NO_CDCHECK)
	// hddaudio
	/**
		Option for user to play audio files directly from hard disk.
		Copy the contents of the PLAY discs Audio directory into your installed Grand Theft Auto III Audio directory.
		Grand Theft Auto III still requires the presence of the PLAY disc when started.
		This may give better performance on some machines (though worse on others).
	**/
	TRACE("hddaudio 1.1 patch");
	{
		int32 streamLength[TOTAL_STREAMED_SOUNDS];

		bool8 bFileNotFound = FALSE;
		char rootpath[MAX_PATH];

		strcpy(_aHDDPath, m_szCDRomRootPath);
		rootpath[0] = '\0';

		FILE *f;

#ifdef PS2_AUDIO_PATHS
		f = fopen(PS2StreamedNameTable[0], "rb");
		if (!f)
#endif

			f = fopen(StreamedNameTable[0], "rb");

		if ( f )
		{
			fclose(f);

			for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
			{
#ifdef PS2_AUDIO_PATHS
				strcpy(filepath, rootpath);
				strcat(filepath, PS2StreamedNameTable[i]);

				mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
				if ( !mp3Stream[0] )
#endif
				{
					strcpy(filepath, rootpath);
					strcat(filepath, StreamedNameTable[i]);

					mp3Stream[0] = AIL_open_stream(DIG, filepath, 0);
				}

				if ( mp3Stream[0] )
				{
					AIL_stream_ms_position(mp3Stream[0], &tatalms, NULL);

					AIL_close_stream(mp3Stream[0]);
					mp3Stream[0] = NULL;

					streamLength[i] = tatalms;
				}
				else
				{
					bFileNotFound = TRUE;
					break;
				}
			}

		}
		else
			bFileNotFound = TRUE;

		if ( !bFileNotFound )
		{
			strcpy(m_szCDRomRootPath, rootpath);

			for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
				nStreamLength[i] = streamLength[i];

			_bUseHDDAudio = TRUE;
		}
		else
			_bUseHDDAudio = FALSE;
	}
#endif
#ifdef AUDIO_CACHE
	cacheFile = fopen("audio\\sound.cache", "wb");
	fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
	fclose(cacheFile);
	}
#endif

	TRACE("stream");
	{
		for ( int32 i = 0; i < MAX_STREAMS; i++ )
		{
			mp3Stream    [i] = NULL;
			nStreamPan   [i] = 63;
			nStreamVolume[i] = 100;
		}
	}

	for ( int32 i = 0; i < MAX2DCHANNELS; i++ )
	{
		opened_2dsamples[i] = AIL_allocate_sample_handle(DIG);
		if ( opened_2dsamples[i] )
		{
			AIL_init_sample(opened_2dsamples[i]);
			AIL_set_sample_type(opened_2dsamples[i], DIG_F_MONO_16, DIG_PCM_SIGN);
		}
	}

	TRACE("providerset");
	{
		_bSampmanInitialised = TRUE;

#ifdef EXTERNAL_3D_SOUND
		U32 n = 0;

		while ( n < m_nNumberOfProviders )
		{
			if ( !strcmp(providers[n].name, "Miles Fast 2D Positional Audio") )
			{
				set_new_provider(n);
				break;
			}
			n++;
		}

		if ( n == m_nNumberOfProviders )
		{
			Terminate();
			return FALSE;
		}
#endif
	}

	TRACE("bank");

	LoadSampleBank(SFX_BANK_0);

	// mp3
	TRACE("mp3");
	{
		nNumMP3s = 0;

		_pMP3List = NULL;

		_FindMP3s();

		if ( nNumMP3s != 0 )
		{
			nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] = 0;

			for ( tMP3Entry *e = _pMP3List; e != NULL; e = e->pNext )
			{
				e->nTrackStreamPos = nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER];
				nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] += e->nTrackLength;
			}

			time_t t = time(NULL);
			tm *localtm;
			bool8 bUseRandomTable;

			if ( t == -1 )
				bUseRandomTable = TRUE;
			else
			{
				bUseRandomTable = FALSE;
				localtm = localtime(&t);
			}

			int32 randval;
			if ( bUseRandomTable )
				randval = AudioManager.m_anRandomTable[1];
			else
				randval = localtm->tm_sec * localtm->tm_min;

			_CurMP3Index = randval % nNumMP3s;

			tMP3Entry *randmp3 = _pMP3List;
			for ( int32 i = randval % nNumMP3s; i > 0; --i)
				randmp3 = randmp3->pNext;

			if ( bUseRandomTable )
				_CurMP3Pos = AudioManager.m_anRandomTable[0]     % randmp3->nTrackLength;
			else
			{
				if ( localtm->tm_sec > 0 )
				{
					int32 s = localtm->tm_sec;
					_CurMP3Pos = s*s*s*s*s*s*s*s                 % randmp3->nTrackLength;
				}
				else
					_CurMP3Pos = AudioManager.m_anRandomTable[0] % randmp3->nTrackLength;
			}
		}
		else
			_CurMP3Pos = 0;

		_bIsMp3Active = FALSE;
	}

	TRACE("end");

	return TRUE;
}

void
cSampleManager::Terminate(void)
{
	for ( int32 i = 0; i < MAX_STREAMS; i++ )
	{
		if ( mp3Stream[i] )
		{
			AIL_pause_stream(mp3Stream[i], 1);
			AIL_close_stream(mp3Stream[i]);
			mp3Stream[i] = NULL;
		}
	}

	for ( int32 i = 0; i < MAX2DCHANNELS; i++ )
	{
		if ( opened_2dsamples[i] )
		{
			AIL_release_sample_handle(opened_2dsamples[i]);
			opened_2dsamples[i] = NULL;
		}
	}

#ifdef EXTERNAL_3D_SOUND
	release_existing();
#endif

	_DeleteMP3Entries();

	if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 )
	{
		AIL_mem_free_lock((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]);
		nSampleBankMemoryStartAddress[SFX_BANK_0] = 0;
	}

	if ( nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0 )
	{
		AIL_mem_free_lock((void *)nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS]);
		nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = 0;
	}

	if ( DIG )
	{
		AIL_close_digital_driver(DIG);
		DIG = NULL;
	}

	AIL_shutdown();

	_bSampmanInitialised = FALSE;
}

bool8
cSampleManager::CheckForAnAudioFileOnCD(void)
{
#if GTA_VERSION < GTA3_PC_STEAM && !defined(NO_CDCHECK)
	char filepath[MAX_PATH];
	FILE *f;

#ifdef PS2_AUDIO_PATHS
#if GTA_VERSION >= GTA3_PC_11
	if(_bUseHDDAudio)
		strcpy(filepath, _aHDDPath);
	else
		strcpy(filepath, m_szCDRomRootPath);
#else
	strcpy(filepath, m_szCDRomRootPath);
#endif // #if GTA_VERSION >= GTA3_PC_11

	strcat(filepath, PS2StreamedNameTable[AudioManager.m_anRandomTable[1] % TOTAL_STREAMED_SOUNDS]);

	f = fopen(filepath, "rb");
	if ( !f )
#endif // PS2_AUDIO_PATHS
	{
#if GTA_VERSION >= GTA3_PC_11
		if (_bUseHDDAudio)
			strcpy(filepath, _aHDDPath);
		else
			strcpy(filepath, m_szCDRomRootPath);
#else
		strcpy(filepath, m_szCDRomRootPath);
#endif // #if GTA_VERSION >= GTA3_PC_11

		strcat(filepath, StreamedNameTable[AudioManager.m_anRandomTable[1] % TOTAL_STREAMED_SOUNDS]);

		f = fopen(filepath, "rb");
	}
	if ( f )
	{
		fclose(f);

		return TRUE;
	}

	return FALSE;

#else
	return TRUE;
#endif // #if GTA_VERSION < GTA3_PC_STEAM && !defined(NO_CDCHECK)
}

char
cSampleManager::GetCDAudioDriveLetter(void)
{
#if GTA_VERSION >= GTA3_PC_11 || defined(NO_CDCHECK)
	if (_bUseHDDAudio)
	{
		if ( strlen(_aHDDPath) != 0 )
			return _aHDDPath[0];
		else
			return '\0';
	}
	else
	{
		if ( strlen(m_szCDRomRootPath) != 0 )
			return m_szCDRomRootPath[0];
		else
			return '\0';
	}
#else
	if ( strlen(m_szCDRomRootPath) != 0 )
		return m_szCDRomRootPath[0];
	else
		return '\0';
#endif
}

void
cSampleManager::UpdateEffectsVolume(void) //[Y], cSampleManager::UpdateSoundBuffers ?
{
	if ( _bSampmanInitialised )
	{
		for ( int32 i = 0; i < MAXCHANNELS+MAX2DCHANNELS; i++ )
		{
#ifdef EXTERNAL_3D_SOUND
			if ( i < MAXCHANNELS )
			{
				if ( opened_samples[i] && GetChannelUsedFlag(i) )
				{
					if ( nChannelVolume[i] )
					{
						AIL_set_3D_sample_volume(opened_samples[i],
								m_nEffectsFadeVolume * nChannelVolume[i] * m_nEffectsVolume >> 14);
					}
				}
			}
			else
#endif
			{
				if ( opened_2dsamples[i - MAXCHANNELS] )
				{
					if ( GetChannelUsedFlag(i - MAXCHANNELS) )
					{
						if ( nChannelVolume[i - MAXCHANNELS] )
						{
							AIL_set_sample_volume(opened_2dsamples[i - MAXCHANNELS],
									m_nEffectsFadeVolume * nChannelVolume[i - MAXCHANNELS] * m_nEffectsVolume >> 14);
						}
					}
				}
			}
		}
	}
}

void
cSampleManager::SetEffectsMasterVolume(uint8 nVolume)
{
	m_nEffectsVolume = nVolume;
	UpdateEffectsVolume();
}

void
cSampleManager::SetMusicMasterVolume(uint8 nVolume)
{
	m_nMusicVolume = nVolume;
}

void
cSampleManager::SetEffectsFadeVolume(uint8 nVolume)
{
	m_nEffectsFadeVolume = nVolume;
	UpdateEffectsVolume();
}

void
cSampleManager::SetMusicFadeVolume(uint8 nVolume)
{
	m_nMusicFadeVolume = nVolume;
}

void
cSampleManager::SetMonoMode(bool8 nMode)
{
	m_nMonoMode = nMode;
}

bool8
cSampleManager::LoadSampleBank(uint8 nBank)
{
	if ( CTimer::GetIsCodePaused() )
		return FALSE;

	if ( MusicManager.IsInitialised()
		&& MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE
		&& nBank != SFX_BANK_0 )
	{
		return FALSE;
	}

	if ( fseek(fpSampleDataHandle, nSampleBankDiscStartOffset[nBank], SEEK_SET) != 0 )
		return FALSE;

	if ( fread((void *)nSampleBankMemoryStartAddress[nBank], 1, nSampleBankSize[nBank],fpSampleDataHandle) != nSampleBankSize[nBank] )
		return FALSE;

	gBankLoaded[nBank] = LOADING_STATUS_LOADED;

	return TRUE;
}

void
cSampleManager::UnloadSampleBank(uint8 nBank)
{
	gBankLoaded[nBank] = LOADING_STATUS_NOT_LOADED;
}

int8
cSampleManager::IsSampleBankLoaded(uint8 nBank)
{
	return gBankLoaded[nBank];
}

uint8
cSampleManager::IsPedCommentLoaded(uint32 nComment)
{
	int8 slot;

	for ( int32 i = 0; i < _TODOCONST(3); i++ )
	{
		slot = nCurrentPedSlot - i - 1;
#ifdef FIX_BUGS
		if (slot < 0)
			slot += ARRAY_SIZE(nPedSlotSfx);
#endif
		if ( nComment == nPedSlotSfx[slot] )
			return LOADING_STATUS_LOADED;
	}

	return LOADING_STATUS_NOT_LOADED;
}

int32
cSampleManager::_GetPedCommentSlot(uint32 nComment)
{
	int8 slot;

	for ( int32 i = 0; i < _TODOCONST(3); i++ )
	{
		slot = nCurrentPedSlot - i - 1;
#ifdef FIX_BUGS
		if (slot < 0)
			slot += ARRAY_SIZE(nPedSlotSfx);
#endif
		if ( nComment == nPedSlotSfx[slot] )
			return slot;
	}

	return -1;
}

bool8
cSampleManager::LoadPedComment(uint32 nComment)
{
	if ( CTimer::GetIsCodePaused() )
		return FALSE;

	// no talking peds during cutsenes or the game end
	if ( MusicManager.IsInitialised() )
	{
		switch ( MusicManager.GetMusicMode() )
		{
			case MUSICMODE_CUTSCENE:
			{
				return FALSE;

				break;
			}

			case MUSICMODE_FRONTEND:
			{
				if ( MusicManager.GetNextTrack() == STREAMED_SOUND_GAME_COMPLETED )
					return FALSE;

				break;
			}
		}
	}

	if ( fseek(fpSampleDataHandle, m_aSamples[nComment].nOffset, SEEK_SET) != 0 )
		return FALSE;

	if ( fread((void *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot), 1, m_aSamples[nComment].nSize, fpSampleDataHandle) != m_aSamples[nComment].nSize )
		return FALSE;

	nPedSlotSfxAddr[nCurrentPedSlot] = nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot;
	nPedSlotSfx    [nCurrentPedSlot] = nComment;

	if ( ++nCurrentPedSlot >= MAX_PEDSFX )
		nCurrentPedSlot = 0;

	return TRUE;
}

int32
cSampleManager::GetBankContainingSound(uint32 offset)
{
	if ( offset >= BankStartOffset[SFX_BANK_PED_COMMENTS] )
		return SFX_BANK_PED_COMMENTS;

	if ( offset >= BankStartOffset[SFX_BANK_0] )
		return SFX_BANK_0;

	return INVALID_SFX_BANK;
}

uint32
cSampleManager::GetSampleBaseFrequency(uint32 nSample)
{
	return m_aSamples[nSample].nFrequency;
}

uint32
cSampleManager::GetSampleLoopStartOffset(uint32 nSample)
{
	return m_aSamples[nSample].nLoopStart;
}

int32
cSampleManager::GetSampleLoopEndOffset(uint32 nSample)
{
	return m_aSamples[nSample].nLoopEnd;
}

uint32
cSampleManager::GetSampleLength(uint32 nSample)
{
	return m_aSamples[nSample].nSize >> 1;
}

bool8
cSampleManager::UpdateReverb(void)
{
#ifdef EXTERNAL_3D_SOUND
	if ( !usingEAX )
		return FALSE;

	if ( AudioManager.m_FrameCounter & 15 )
		return FALSE;

#ifdef AUDIO_REFLECTIONS
	float y = AudioManager.m_afReflectionsDistances[REFLECTION_TOP]  + AudioManager.m_afReflectionsDistances[REFLECTION_BOTTOM];
	float x = AudioManager.m_afReflectionsDistances[REFLECTION_LEFT] + AudioManager.m_afReflectionsDistances[REFLECTION_RIGHT];
	float z = AudioManager.m_afReflectionsDistances[REFLECTION_UP];
#else
	float x = 0.0f;
	float y = 0.0f;
	float z = 0.0f;
#endif

	float normy = norm(y, 5.0f, 40.0f);
	float normx = norm(x, 5.0f, 40.0f);
	float normz = norm(z, 5.0f, 40.0f);

	float fRatio;

	if ( normy == 0.0f )
	{
		if ( normx == 0.0f )
		{
			if ( normz == 0.0f )
				fRatio = 0.3f;
			else
				fRatio = 0.5f;
		}
		else
		{
			fRatio = 0.3f;
		}
	}
	else
	{
		if ( normx == 0.0f )
		{
			if ( normz == 0.0f )
				fRatio = 0.3f;
			else
				fRatio = 0.5f;
		}
		else
		{
			if ( normz == 0.0f )
				fRatio = 0.3f;
			else
				fRatio = (normy+normx+normz) / 3.0f;
		}
	}

	fRatio = Clamp(fRatio, usingEAX3==1 ? 0.0f : 0.30f, 1.0f);

	if ( fRatio == _fPrevEaxRatioDestination )
		return FALSE;

	if ( usingEAX3 )
	{
		if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) )
		{
			AIL_set_3D_provider_preference(opened_provider, "EAX all parameters", &EAX3Params);
			_fEffectsLevel = 1.0f - fRatio * 0.5f;
		}
	}
	else
	{
		if ( _usingMilesFast2D )
			_fEffectsLevel = (1.0f - fRatio) * 0.4f;
		else
			_fEffectsLevel = (1.0f - fRatio) * 0.7f;
	}

	_fPrevEaxRatioDestination = fRatio;

	return TRUE;
#endif
	return FALSE;
}

void
cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( usingEAX )
	{
		if ( nReverbFlag != FALSE )
		{
			if ( !b2d )
				AIL_set_3D_sample_effects_level(opened_samples[nChannel], _fEffectsLevel);
		}
		else
		{
			if ( !b2d )
				AIL_set_3D_sample_effects_level(opened_samples[nChannel], 0.0f);
		}
	}
#endif
}

bool8
cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}
#endif

	int32 addr;

	if ( nSfx < SAMPLEBANK_MAX )
	{
		if ( !IsSampleBankLoaded(nBank) )
			return FALSE;

		addr = nSampleBankMemoryStartAddress[nBank] + m_aSamples[nSfx].nOffset - m_aSamples[BankStartOffset[nBank]].nOffset;
	}
	else
	{
		int32 i;
		for ( i = 0; i < _TODOCONST(3); i++ )
		{
			int32 slot = nCurrentPedSlot - i - 1;
#ifdef FIX_BUGS
			if (slot < 0)
				slot += ARRAY_SIZE(nPedSlotSfx);
#endif
			if ( nSfx == nPedSlotSfx[slot] )
			{
				addr = nPedSlotSfxAddr[slot];
				break;
			}
		}

		if (i == _TODOCONST(3))
			return FALSE;
	}

#ifdef EXTERNAL_3D_SOUND
	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
		{
			AIL_set_sample_address(opened_2dsamples[nChannel - MAXCHANNELS], (void *)addr, m_aSamples[nSfx].nSize);
			return TRUE;
		}
		else
			return FALSE;
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		AILSOUNDINFO info;

		info.format   = WAVE_FORMAT_PCM;
		info.data_ptr = (void *)addr;
		info.channels = 1;
		info.data_len = m_aSamples[nSfx].nSize;
		info.rate     = m_aSamples[nSfx].nFrequency;
		info.bits     = 16;

		if ( AIL_set_3D_sample_info(opened_samples[nChannel], &info) == 0 )
		{
			OutputDebugString(AIL_last_error());
			return FALSE;
		}

		return TRUE;
	}
#endif
}

#ifdef EXTERNAL_3D_SOUND
void
cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume)
{
	uint32 vol = nVolume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

	nChannelVolume[nChannel] = vol;

	// increase the volume for JB.MP3 and S4_BDBD.MP3
	if (   MusicManager.GetMusicMode()    == MUSICMODE_CUTSCENE
		&& MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO
		&& MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD )
	{
		nChannelVolume[nChannel] >>= 2;
	}

	if ( opened_samples[nChannel] )
		AIL_set_3D_sample_volume(opened_samples[nChannel], m_nEffectsFadeVolume*nChannelVolume[nChannel]*m_nEffectsVolume >> 14);

}

void
cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ)
{
	if ( opened_samples[nChannel] )
		AIL_set_3D_position(opened_samples[nChannel], -fX, fY, fZ);
}

void
cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin)
{
	if ( opened_samples[nChannel] )
		AIL_set_3D_sample_distances(opened_samples[nChannel], fMax, fMin);
}
#endif

void
cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume)
{
	uint32 vol = nVolume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

#ifdef EXTERNAL_3D_SOUND
	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
#endif
			nChannelVolume[nChannel] = vol;

			// increase the volume for JB.MP3 and S4_BDBD.MP3
			if (   MusicManager.GetMusicMode()    == MUSICMODE_CUTSCENE
				&& MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO
				&& MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD )
			{
				nChannelVolume[nChannel] >>= 2;
			}

			if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			{
				AIL_set_sample_volume(opened_2dsamples[nChannel - MAXCHANNELS],
						m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14);
			}

#ifdef EXTERNAL_3D_SOUND
			break;
		}
	}
#endif
}

void
cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan)
{
#ifdef EXTERNAL_3D_SOUND
	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
#endif
#if !defined(FIX_BUGS) && defined(EXTERNAL_3D_SOUND)
			if ( opened_samples[nChannel - MAXCHANNELS] ) // BUG
#else
			if ( opened_2dsamples[nChannel - MAXCHANNELS] )
#endif
				AIL_set_sample_pan(opened_2dsamples[nChannel - MAXCHANNELS], nPan);

#ifdef EXTERNAL_3D_SOUND
			break;
		}
	}
#endif
}

void
cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			AIL_set_sample_playback_rate(opened_2dsamples[nChannel - MAXCHANNELS], nFreq);
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		if ( opened_samples[nChannel] )
			AIL_set_3D_sample_playback_rate(opened_samples[nChannel], nFreq);
	}
#endif
}

void
cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			AIL_set_sample_loop_block(opened_2dsamples[nChannel - MAXCHANNELS], nLoopStart, nLoopEnd);
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		if ( opened_samples[nChannel] )
			AIL_set_3D_sample_loop_block(opened_samples[nChannel], nLoopStart, nLoopEnd);
	}
#endif
}

void
cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			AIL_set_sample_loop_count(opened_2dsamples[nChannel - MAXCHANNELS], nLoopCount);
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		if ( opened_samples[nChannel] )
			AIL_set_3D_sample_loop_count(opened_samples[nChannel], nLoopCount);
	}
#endif
}

bool8
cSampleManager::GetChannelUsedFlag(uint32 nChannel)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			return AIL_sample_status(opened_2dsamples[nChannel - MAXCHANNELS]) == SMP_PLAYING;
		else
			return FALSE;
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		if ( opened_samples[nChannel] )
			return AIL_3D_sample_status(opened_samples[nChannel]) == SMP_PLAYING;
		else
			return FALSE;
	}
#endif

}

void
cSampleManager::StartChannel(uint32 nChannel)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			AIL_start_sample(opened_2dsamples[nChannel - MAXCHANNELS]);
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		if ( opened_samples[nChannel] )
			AIL_start_3D_sample(opened_samples[nChannel]);
	}
#endif
}

void
cSampleManager::StopChannel(uint32 nChannel)
{
#ifdef EXTERNAL_3D_SOUND
	bool8 b2d = FALSE;

	switch ( nChannel )
	{
		case CHANNEL_POLICE_RADIO:
		{
			b2d = TRUE;
			break;
		}
	}

	if ( b2d )
	{
#endif
		if ( opened_2dsamples[nChannel - MAXCHANNELS] )
			AIL_end_sample(opened_2dsamples[nChannel - MAXCHANNELS]);
#ifdef EXTERNAL_3D_SOUND
	}
	else
	{
		if ( opened_samples[nChannel] )
		{
			if ( AIL_3D_sample_status(opened_samples[nChannel]) == SMP_PLAYING )
				AIL_end_3D_sample(opened_samples[nChannel]);
		}
	}
#endif
}

void
cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
{
	if ( m_bInitialised  )
	{
		if ( nFile < TOTAL_STREAMED_SOUNDS )
		{
			if ( mp3Stream[nStream] )
			{
				AIL_pause_stream(mp3Stream[nStream], 1);
				AIL_close_stream(mp3Stream[nStream]);
			}

			char filepath[MAX_PATH];
#ifdef PS2_AUDIO_PATHS
			strcpy(filepath, m_szCDRomRootPath);
			strcat(filepath, PS2StreamedNameTable[nFile]);

			mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0);
			if ( !mp3Stream[nStream] )
#endif
			{
				strcpy(filepath, m_szCDRomRootPath);
				strcat(filepath, StreamedNameTable[nFile]);

				mp3Stream[nStream] = AIL_open_stream(DIG, filepath, 0);
			}

			if ( mp3Stream[nStream] )
			{
				AIL_set_stream_loop_count(mp3Stream[nStream], 1);
				AIL_service_stream(mp3Stream[nStream], 1);
			}
			else
				OutputDebugString(AIL_last_error());
		}
	}
}

void
cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream)
{
	if ( m_bInitialised )
	{
		if ( mp3Stream[nStream] )
			AIL_pause_stream(mp3Stream[nStream], nPauseFlag != FALSE);
	}
}

void
cSampleManager::StartPreloadedStreamedFile(uint8 nStream)
{
	if ( m_bInitialised )
	{
		if ( mp3Stream[nStream] )
			AIL_start_stream(mp3Stream[nStream]);
	}
}

bool8
cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
	uint32 i = 0;
	uint32 position = nPos;
	char filename[MAX_PATH];

	if ( !m_bInitialised || nFile >= TOTAL_STREAMED_SOUNDS )
		return FALSE;

	if ( mp3Stream[nStream] )
	{
		AIL_pause_stream(mp3Stream[nStream], 1);
		AIL_close_stream(mp3Stream[nStream]);
	}
	if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER )
	{
		do
		{
			// Just switched to MP3 player
			if ( !_bIsMp3Active && i == 0 )
			{
				if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] )
					position = 0;
				tMP3Entry *e = _pMP3List;

				// Try to continue from previous song, if already started
				if(!_GetMP3PosFromStreamPos(&position, &e) && !e) {
					nFile = 0;
#ifdef PS2_AUDIO_PATHS
					strcpy(filename, m_szCDRomRootPath);
					strcat(filename, PS2StreamedNameTable[nFile]);

					mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
					if ( !mp3Stream[nStream] )
#endif
					{
						strcpy(filename, m_szCDRomRootPath);
						strcat(filename, StreamedNameTable[nFile]);

						mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
					}
					if ( mp3Stream[nStream] )
					{
						AIL_set_stream_loop_count(mp3Stream[nStream], 1);
						AIL_set_stream_ms_position(mp3Stream[nStream], position);
						AIL_pause_stream(mp3Stream[nStream], 0);
						return TRUE;
					}
					return FALSE;

				} else {
					if ( e->pLinkPath != NULL )
						mp3Stream[nStream] = AIL_open_stream(DIG, e->pLinkPath, 0);
					else {
						strcpy(filename, _mp3DirectoryPath);
						strcat(filename, e->aFilename);

						mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
					}

					if ( mp3Stream[nStream] ) {
						AIL_set_stream_loop_count(mp3Stream[nStream], 1);
						AIL_set_stream_ms_position(mp3Stream[nStream], position);
						AIL_pause_stream(mp3Stream[nStream], 0);

						_bIsMp3Active = TRUE;

						return TRUE;
					}
					// fall through, start playing from another song
				}
			} else {
				if(++_CurMP3Index >= nNumMP3s) _CurMP3Index = 0;

				_CurMP3Pos = 0;

				tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index);
				if ( !mp3 )
				{
					mp3 = _pMP3List;
					if ( !_pMP3List )
					{
						nFile = 0;
						_bIsMp3Active = FALSE;
#ifdef PS2_AUDIO_PATHS
						strcpy(filename, m_szCDRomRootPath);
						strcat(filename, PS2StreamedNameTable[nFile]);

						mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
						if ( !mp3Stream[nStream] )
#endif
						{
							strcpy(filename, m_szCDRomRootPath);
							strcat(filename, StreamedNameTable[nFile]);

							mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
						}
						if ( mp3Stream[nStream] )
						{
							AIL_set_stream_loop_count(mp3Stream[nStream], 1);
							AIL_set_stream_ms_position(mp3Stream[nStream], position);
							AIL_pause_stream(mp3Stream[nStream], 0);
							return TRUE;
						}
						return FALSE;
					}
				}
				if(mp3->pLinkPath != NULL)
					mp3Stream[nStream] = AIL_open_stream(DIG, mp3->pLinkPath, 0);
				else {
					strcpy(filename, _mp3DirectoryPath);
					strcat(filename, mp3->aFilename);

					mp3Stream[nStream] =
					    AIL_open_stream(DIG, filename, 0);
				}

				if(mp3Stream[nStream]) {
					AIL_set_stream_loop_count(mp3Stream[nStream], 1);
					AIL_set_stream_ms_position(mp3Stream[nStream], 0);
					AIL_pause_stream(mp3Stream[nStream], 0);
#ifdef FIX_BUGS
					_bIsMp3Active = TRUE;
#endif
					return TRUE;
				}

			}
			_bIsMp3Active = FALSE;
		}
		while ( ++i < nNumMP3s );
		position = 0;
		nFile = 0;
	}
#ifdef PS2_AUDIO_PATHS
	strcpy(filename, m_szCDRomRootPath);
	strcat(filename, PS2StreamedNameTable[nFile]);

	mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
	if ( !mp3Stream[nStream] )
#endif
	{
		strcpy(filename, m_szCDRomRootPath);
		strcat(filename, StreamedNameTable[nFile]);

		mp3Stream[nStream] = AIL_open_stream(DIG, filename, 0);
	}
	if ( mp3Stream[nStream] )
	{
		AIL_set_stream_loop_count(mp3Stream[nStream], 1);
		AIL_set_stream_ms_position(mp3Stream[nStream], position);
		AIL_pause_stream(mp3Stream[nStream], 0);
		return TRUE;
	}
	return FALSE;
}

void
cSampleManager::StopStreamedFile(uint8 nStream)
{
	if ( m_bInitialised )
	{
		if ( mp3Stream[nStream] )
		{
			AIL_pause_stream(mp3Stream[nStream], 1);

			AIL_close_stream(mp3Stream[nStream]);
			mp3Stream[nStream] = NULL;

			if ( nStream == 0 )
				_bIsMp3Active = FALSE;
		}
	}
}

int32
cSampleManager::GetStreamedFilePosition(uint8 nStream)
{
	S32 currentms;

	if ( m_bInitialised )
	{
		if ( mp3Stream[nStream] )
		{
			if ( _bIsMp3Active )
			{
				tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index);

				if ( mp3 != NULL )
				{
					AIL_stream_ms_position(mp3Stream[nStream], NULL, &currentms);
					return currentms + mp3->nTrackStreamPos;
				}
				else
					return 0;
			}
			else
			{
				AIL_stream_ms_position(mp3Stream[nStream], NULL, &currentms);
				return currentms;
			}
		}
	}

	return 0;
}

void
cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, bool8 nEffectFlag, uint8 nStream)
{
	uint8 vol = nVolume;

	if ( m_bInitialised )
	{
		if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;
		if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

		nStreamVolume[nStream] = vol;
		nStreamPan[nStream]    = nPan;

		if ( mp3Stream[nStream] )
		{
			if ( nEffectFlag )
				AIL_set_stream_volume(mp3Stream[nStream], m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14);
			else
				AIL_set_stream_volume(mp3Stream[nStream], m_nMusicFadeVolume*vol*m_nMusicVolume >> 14);

			AIL_set_stream_pan(mp3Stream[nStream], nPan);
		}
	}
}

int32
cSampleManager::GetStreamedFileLength(uint8 nStream)
{
	if ( m_bInitialised )
		return nStreamLength[nStream];

	return 0;
}

bool8
cSampleManager::IsStreamPlaying(uint8 nStream)
{
	if ( m_bInitialised )
	{
		if ( mp3Stream[nStream] )
		{
			if ( AIL_stream_status(mp3Stream[nStream]) == SMP_PLAYING )
				return TRUE;
			else
				return FALSE;
		}
	}

	return FALSE;
}

bool8
cSampleManager::InitialiseSampleBanks(void)
{
	int32 nBank = SFX_BANK_0;

	fpSampleDescHandle = fopen(SampleBankDescFilename, "rb");
	if ( fpSampleDescHandle == NULL )
		return FALSE;

	fpSampleDataHandle = fopen(SampleBankDataFilename, "rb");
	if ( fpSampleDataHandle == NULL )
	{
		fclose(fpSampleDescHandle);
		fpSampleDescHandle = NULL;

		return FALSE;
	}

	fseek(fpSampleDataHandle, 0, SEEK_END);
	_nSampleDataEndOffset = ftell(fpSampleDataHandle);
	rewind(fpSampleDataHandle);

	fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle);

	fclose(fpSampleDescHandle);
	fpSampleDescHandle = NULL;

	for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ )
	{
#ifdef FIX_BUGS
		if (nBank >= MAX_SFX_BANKS) break;
#endif
		if ( BankStartOffset[nBank] == BankStartOffset[SFX_BANK_0] + i )
		{
			nSampleBankDiscStartOffset[nBank] = m_aSamples[i].nOffset;
			nBank++;
		}
	}

	nSampleBankSize[SFX_BANK_0] = nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS] - nSampleBankDiscStartOffset[SFX_BANK_0];
	nSampleBankSize[SFX_BANK_PED_COMMENTS] = _nSampleDataEndOffset                       - nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS];

	return TRUE;
}

#endif


code:
#include "common.h"
#if !defined(AUDIO_OAL) &&  !defined(AUDIO_MSS)
#include "sampman.h"
#include "AudioManager.h"

cSampleManager SampleManager;
bool8 _bSampmanInitialised = FALSE;

uint32 BankStartOffset[MAX_SFX_BANKS];
uint32     nNumMP3s;

cSampleManager::cSampleManager(void)
{
	;
}

cSampleManager::~cSampleManager(void)
{

}

#ifdef EXTERNAL_3D_SOUND
void cSampleManager::SetSpeakerConfig(int32 nConfig)
{

}

uint32 cSampleManager::GetMaximumSupportedChannels(void)
{
	return MAXCHANNELS;
}

uint32 cSampleManager::GetNum3DProvidersAvailable()
{
	return 1;
}

void cSampleManager::SetNum3DProvidersAvailable(uint32 num)
{

}

char *cSampleManager::Get3DProviderName(uint8 id)
{
	static char name[64] = "NULL";
	return name;
}

void cSampleManager::Set3DProviderName(uint8 id, char *name)
{

}

int8 cSampleManager::GetCurrent3DProviderIndex(void)
{
	return 0;
}

int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider)
{
	return 0;
}
#endif

bool8
cSampleManager::IsMP3RadioChannelAvailable(void)
{
	return nNumMP3s != 0;
}

void cSampleManager::ReleaseDigitalHandle(void)
{
}

void cSampleManager::ReacquireDigitalHandle(void)
{
}

bool8
cSampleManager::Initialise(void)
{
	return TRUE;
}

void
cSampleManager::Terminate(void)
{

}

bool8 cSampleManager::CheckForAnAudioFileOnCD(void)
{
	return TRUE;
}

char cSampleManager::GetCDAudioDriveLetter(void)
{
	return '\0';
}

void
cSampleManager::UpdateEffectsVolume(void)
{

}

void
cSampleManager::SetEffectsMasterVolume(uint8 nVolume)
{
}

void
cSampleManager::SetMusicMasterVolume(uint8 nVolume)
{
}

void
cSampleManager::SetEffectsFadeVolume(uint8 nVolume)
{
}

void
cSampleManager::SetMusicFadeVolume(uint8 nVolume)
{
}

void
cSampleManager::SetMonoMode(uint8 nMode)
{
}

bool8
cSampleManager::LoadSampleBank(uint8 nBank)
{
	ASSERT( nBank < MAX_SFX_BANKS );
	return FALSE;
}

void
cSampleManager::UnloadSampleBank(uint8 nBank)
{
	ASSERT( nBank < MAX_SFX_BANKS );
}

int8
cSampleManager::IsSampleBankLoaded(uint8 nBank)
{
	ASSERT( nBank < MAX_SFX_BANKS );

	return LOADING_STATUS_NOT_LOADED;
}

uint8
cSampleManager::IsPedCommentLoaded(uint32 nComment)
{
	ASSERT( nComment < TOTAL_AUDIO_SAMPLES );

	return LOADING_STATUS_NOT_LOADED;
}

int32
cSampleManager::_GetPedCommentSlot(uint32 nComment)
{
	return -1;
}

bool8
cSampleManager::LoadPedComment(uint32 nComment)
{
	ASSERT( nComment < TOTAL_AUDIO_SAMPLES );
	return FALSE;
}

int32
cSampleManager::GetBankContainingSound(uint32 offset)
{
	return INVALID_SFX_BANK;
}

uint32
cSampleManager::GetSampleBaseFrequency(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return 0;
}

uint32
cSampleManager::GetSampleLoopStartOffset(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return 0;
}

int32
cSampleManager::GetSampleLoopEndOffset(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return 0;
}

uint32
cSampleManager::GetSampleLength(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return 0;
}

bool8 cSampleManager::UpdateReverb(void)
{
	return FALSE;
}

void
cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

bool8
cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
	return FALSE;
}

#ifdef EXTERNAL_3D_SOUND
void
cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume)
{
	ASSERT( nChannel < MAXCHANNELS );
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ)
{
	ASSERT( nChannel < MAXCHANNELS );
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin)
{
	ASSERT( nChannel < MAXCHANNELS );
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}
#endif

void
cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume)
{
	ASSERT( nChannel >= MAXCHANNELS );
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan)
{
	ASSERT( nChannel >= MAXCHANNELS );
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

bool8
cSampleManager::GetChannelUsedFlag(uint32 nChannel)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );

	return FALSE;
}

void
cSampleManager::StartChannel(uint32 nChannel)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::StopChannel(uint32 nChannel)
{
	ASSERT( nChannel < MAXCHANNELS+MAX2DCHANNELS );
}

void
cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );
}

void
cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );
}

void
cSampleManager::StartPreloadedStreamedFile(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );
}

bool8
cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	return FALSE;
}

void
cSampleManager::StopStreamedFile(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );
}

int32
cSampleManager::GetStreamedFilePosition(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	return 0;
}

void
cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );
}

int32
cSampleManager::GetStreamedFileLength(uint8 nStream)
{
	ASSERT( nStream < TOTAL_STREAMED_SOUNDS );

	return 1;
}

bool8
cSampleManager::IsStreamPlaying(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	return FALSE;
}

bool8
cSampleManager::InitialiseSampleBanks(void)
{

	return TRUE;
}

#endif

license: mit


code:
//#define JUICY_OAL

#ifdef AUDIO_OAL
#include <time.h>

#include "eax.h"
#include "eax-util.h"

#ifdef _WIN32
#include <io.h>
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#include <AL/efx.h>
#include <AL/efx-presets.h>

// for user MP3s
#include <direct.h>
#include <shlobj.h>
#include <shlguid.h>
#else
#define _getcwd getcwd
#endif

#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
#pragma comment( lib, "OpenAL32.lib" )
#endif

#include "common.h"
#include "crossplatform.h"

#include "sampman.h"

#include "oal/oal_utils.h"
#include "oal/aldlist.h"
#include "oal/channel.h"

#include <utility>
#ifdef MULTITHREADED_AUDIO
#include <mutex>
#include <queue>
#include <condition_variable>
#endif
#include "oal/stream.h"

#include "AudioManager.h"
#include "MusicManager.h"
#include "Frontend.h"
#include "Timer.h"
#ifdef AUDIO_OAL_USE_OPUS
#include <opusfile.h>
#endif

//TODO: fix eax3 reverb

cSampleManager SampleManager;
bool8 _bSampmanInitialised = FALSE;

uint32 BankStartOffset[MAX_SFX_BANKS];

int           prevprovider=-1;
int           curprovider=-1;
int           usingEAX=0;
int           usingEAX3=0;
//int         speaker_type=0;
ALCdevice    *ALDevice = NULL;
ALCcontext   *ALContext = NULL;
unsigned int _maxSamples;
float        _fPrevEaxRatioDestination;
bool         _effectsSupported = false;
bool         _usingEFX;
float        _fEffectsLevel;
ALuint       ALEffect = AL_EFFECT_NULL;
ALuint       ALEffectSlot = AL_EFFECTSLOT_NULL;
struct
{
	const char *id;
	char name[256];
	int sources;
	bool bSupportsFx;
}providers[MAXPROVIDERS];

int defaultProvider;

char SampleBankDescFilename[] = "audio/sfx.SDT";
char SampleBankDataFilename[] = "audio/sfx.RAW";

FILE *fpSampleDescHandle;
#ifdef OPUS_SFX
OggOpusFile *fpSampleDataHandle;
#else
FILE *fpSampleDataHandle;
#endif
int8  gBankLoaded                  [MAX_SFX_BANKS];
int32 nSampleBankDiscStartOffset   [MAX_SFX_BANKS];
int32 nSampleBankSize              [MAX_SFX_BANKS];
uintptr nSampleBankMemoryStartAddress[MAX_SFX_BANKS];
int32 _nSampleDataEndOffset;

int32 nPedSlotSfx    [MAX_PEDSFX];
int32 nPedSlotSfxAddr[MAX_PEDSFX];
uint8 nCurrentPedSlot;

CChannel aChannel[NUM_CHANNELS];
uint8 nChannelVolume[NUM_CHANNELS];

uint32 nStreamLength[TOTAL_STREAMED_SOUNDS];
ALuint ALStreamSources[MAX_STREAMS][2];
ALuint ALStreamBuffers[MAX_STREAMS][NUM_STREAMBUFFERS];

struct tMP3Entry
{
	char aFilename[MAX_PATH];

	uint32 nTrackLength;
	uint32 nTrackStreamPos;

	tMP3Entry* pNext;
	char* pLinkPath;
};

uint32 nNumMP3s;
tMP3Entry* _pMP3List;
char _mp3DirectoryPath[MAX_PATH];
CStream    *aStream[MAX_STREAMS];
uint8      nStreamPan   [MAX_STREAMS];
uint8      nStreamVolume[MAX_STREAMS];
uint32 _CurMP3Index;
int32 _CurMP3Pos;
bool8 _bIsMp3Active;
///////////////////////////////////////////////////////////////
//	Env		Size	Diffus	Room	RoomHF	RoomLF	DecTm	DcHF	DcLF	Refl	RefDel	Ref Pan				Revb	RevDel		Rev Pan				EchTm	EchDp	ModTm	ModDp	AirAbs	HFRef		LFRef	RRlOff	FLAGS
EAXLISTENERPROPERTIES StartEAX3 =
	{26,	1.7f,	0.8f,	-1000,	-1000,	-100,	4.42f,	0.14f,	1.00f,	429,	0.014f,	0.00f,0.00f,0.00f,	1023,	0.021f,		0.00f,0.00f,0.00f,	0.250f,	0.000f,	0.250f,	0.000f,	-5.0f,	2727.1f,	250.0f,	0.00f,	0x3f };

EAXLISTENERPROPERTIES FinishEAX3 =
	{26,	100.0f,	1.0f,	0,		-1000,	-2200,	20.0f,	1.39f,	1.00f,	1000,	0.069f,	0.00f,0.00f,0.00f,	400,	0.100f,		0.00f,0.00f,0.00f,	0.250f,	1.000f,	3.982f,	0.000f,	-18.0f,	3530.8f,	417.9f,	6.70f,	0x3f };

EAXLISTENERPROPERTIES EAX3Params;

bool IsFXSupported()
{
	return _effectsSupported; // usingEAX || usingEAX3 || _usingEFX;
}

void EAX_SetAll(const EAXLISTENERPROPERTIES *allparameters)
{
	if ( usingEAX || usingEAX3 )
		EAX3_Set(ALEffect, allparameters);
	else
		EFX_Set(ALEffect, allparameters);
}

static void
add_providers()
{
	SampleManager.SetNum3DProvidersAvailable(0);

	static ALDeviceList DeviceList;
	ALDeviceList *pDeviceList = &DeviceList;

	if ((pDeviceList) && (pDeviceList->GetNumDevices()))
	{
		const int devNumber = Min(pDeviceList->GetNumDevices(), MAXPROVIDERS);
		int n = 0;

		//for (int i = 0; i < devNumber; i++)
		int i = pDeviceList->GetDefaultDevice();
		{
			if ( n < MAXPROVIDERS )
			{
				providers[n].id = pDeviceList->GetDeviceName(i);
				strcpy(providers[n].name, "OPENAL SOFT");
				providers[n].sources = pDeviceList->GetMaxNumSources(i);
				SampleManager.Set3DProviderName(n, providers[n].name);
				n++;
			}

			if ( alGetEnumValue("AL_EFFECT_EAXREVERB") != 0
				|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX2)
				|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX3)
				|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX4)
				|| pDeviceList->IsExtensionSupported(i, ADEXT_EAX5) )
			{
				providers[n - 1].bSupportsFx = true;
				if ( n < MAXPROVIDERS )
				{
					providers[n].id = pDeviceList->GetDeviceName(i);
					strcpy(providers[n].name, "OPENAL SOFT EAX");
					providers[n].sources = pDeviceList->GetMaxNumSources(i);
					providers[n].bSupportsFx = true;
					SampleManager.Set3DProviderName(n, providers[n].name);
					n++;
				}

				if ( n < MAXPROVIDERS )
				{
					providers[n].id = pDeviceList->GetDeviceName(i);
					strcpy(providers[n].name, "OPENAL SOFT EAX3");
					providers[n].sources = pDeviceList->GetMaxNumSources(i);
					providers[n].bSupportsFx = true;
					SampleManager.Set3DProviderName(n, providers[n].name);
					n++;
				}
			}
		}
		SampleManager.SetNum3DProvidersAvailable(n);

		for(int j=n;j<MAXPROVIDERS;j++)
			SampleManager.Set3DProviderName(j, NULL);

		// devices are gone now
		//defaultProvider = pDeviceList->GetDefaultDevice();
		//if ( defaultProvider > MAXPROVIDERS )
		defaultProvider = 0;
	}
}

static void
release_existing()
{
	if ( IsFXSupported() )
	{
		if ( alIsEffect(ALEffect) )
		{
			alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL);
		}

		if (alIsAuxiliaryEffectSlot(ALEffectSlot))
		{
			alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);
		}
	}

	DEV("release_existing()\n");
}

static bool8
set_new_provider(int index)
{
	if ( curprovider == index )
		return TRUE;

	curprovider = index;

	release_existing();

	if ( curprovider != -1 )
	{
		DEV("set_new_provider()\n");

		usingEAX = 0;
		usingEAX3 = 0;
		_usingEFX = false;

		if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX3")], " EAX3")
				&& alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
		{

			usingEAX = 1;
			usingEAX3 = 1;
			alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
			EAX_SetAll(&FinishEAX3);

			DEV("EAX3\n");
		}
		else if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
		{

			if ( !strcmp(&providers[index].name[strlen(providers[index].name) - strlen(" EAX")], " EAX"))
			{
				usingEAX = 1;
				DEV("EAX1\n");
			}
			else
			{
				_usingEFX = true;
				DEV("EFX\n");
			}
			alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
			EAX_SetAll(&EAX30_ORIGINAL_PRESETS[EAX_ENVIRONMENT_CAVE]);
		}

		//SampleManager.SetSpeakerConfig(speaker_type);

		if ( IsFXSupported() )
		{
			for ( int32 i = 0; i < MAXCHANNELS; i++ )
				aChannel[i].SetReverbMix(ALEffectSlot, 0.0f);
		}

		return TRUE;
	}

	return FALSE;
}

static bool8
IsThisTrackAt16KHz(uint32 track)
{
	return track == STREAMED_SOUND_RADIO_CHAT;
}

cSampleManager::cSampleManager(void)
{
	;
}

cSampleManager::~cSampleManager(void)
{

}

void cSampleManager::SetSpeakerConfig(int32 nConfig)
{

}

uint32 cSampleManager::GetMaximumSupportedChannels(void)
{
	if ( _maxSamples > MAXCHANNELS )
		return MAXCHANNELS;

	return _maxSamples;
}

uint32 cSampleManager::GetNum3DProvidersAvailable()
{
	return m_nNumberOfProviders;
}

void cSampleManager::SetNum3DProvidersAvailable(uint32 num)
{
	m_nNumberOfProviders = num;
}

char *cSampleManager::Get3DProviderName(uint8 id)
{
	return m_aAudioProviders[id];
}

void cSampleManager::Set3DProviderName(uint8 id, char *name)
{
	m_aAudioProviders[id] = name;
}

int8 cSampleManager::GetCurrent3DProviderIndex(void)
{
	return curprovider;
}

int8 cSampleManager::SetCurrent3DProvider(uint8 nProvider)
{
	int savedprovider = curprovider;

	nProvider = Clamp(nProvider, 0, m_nNumberOfProviders - 1);

	if ( set_new_provider(nProvider) )
		return curprovider;
	else if ( savedprovider != -1 && savedprovider < m_nNumberOfProviders && set_new_provider(savedprovider) )
		return curprovider;
	else
		return curprovider;
}

static bool8
_ResolveLink(char const *path, char *out)
{
#ifdef _WIN32
	size_t len = strlen(path);
	if (len < 4 || strcmp(&path[len - 4], ".lnk") != 0)
		return FALSE;

	IShellLink* psl;
	WIN32_FIND_DATA fd;
	char filepath[MAX_PATH];

	CoInitialize(NULL);

	if (SUCCEEDED( CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl ) ))
	{
		IPersistFile *ppf;

		if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
		{
			WCHAR wpath[MAX_PATH];

			MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH);

			if (SUCCEEDED(ppf->Load(wpath, STGM_READ)))
			{
				/* Resolve the link */
				if (SUCCEEDED(psl->Resolve(NULL, SLR_ANY_MATCH|SLR_NO_UI|SLR_NOSEARCH)))
				{
					strcpy(filepath, path);

					if (SUCCEEDED(psl->GetPath(filepath, MAX_PATH, &fd, SLGP_UNCPRIORITY)))
					{
						OutputDebugString(fd.cFileName);

						strcpy(out, filepath);
						// FIX: Release the objects. Taken from SA.
#ifdef FIX_BUGS
						ppf->Release();
						psl->Release();
#endif
						return TRUE;
					}
				}
			}

			ppf->Release();
		}
		psl->Release();
	}

	return FALSE;
#else
	struct stat sb;

	if (lstat(path, &sb) == -1) {
		perror("lstat: ");
		return FALSE;
	}

	if (S_ISLNK(sb.st_mode)) {
		char* linkname = (char*)alloca(sb.st_size + 1);
		if (linkname == NULL) {
			fprintf(stderr, "insufficient memory\n");
			return FALSE;
		}

		if (readlink(path, linkname, sb.st_size + 1) < 0) {
			perror("readlink: ");
			return FALSE;
		}
		linkname[sb.st_size] = '\0';
		strcpy(out, linkname);
		return TRUE;
	} else {
		return FALSE;
	}
#endif
}

static void
_FindMP3s(void)
{
	tMP3Entry *pList;
	bool8 bShortcut;
	bool8 bInitFirstEntry;
	HANDLE hFind;
	char path[MAX_PATH];
	int total_ms;
	WIN32_FIND_DATA fd;
	char filepath[MAX_PATH + sizeof(fd.cFileName)];

	if (getcwd(_mp3DirectoryPath, MAX_PATH) == NULL) {
		perror("getcwd: ");
		return;
	}

	if (strlen(_mp3DirectoryPath) + 1 > MAX_PATH - 10) {
		// This is not gonna end well
		printf("MP3 folder path is too long, no place left for file names. MP3 finding aborted.\n");
		return;
	}

	OutputDebugString("Finding MP3s...");
	strcpy(path, _mp3DirectoryPath);
	strcat(path, "\\MP3\\");

#if !defined(_WIN32)
	char *actualPath = casepath(path);
	if (actualPath) {
		strcpy(path, actualPath);
		free(actualPath);
	}
#endif

	strcpy(_mp3DirectoryPath, path);
	OutputDebugString(_mp3DirectoryPath);

	strcat(path, "*");

	hFind = FindFirstFile(path, &fd);

	if ( hFind == INVALID_HANDLE_VALUE )
	{
		return;
	}

	bShortcut = FALSE;
	bInitFirstEntry = TRUE;

	do
	{
		strcpy(filepath, _mp3DirectoryPath);
		strcat(filepath, fd.cFileName);

		if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))
			continue;

		size_t filepathlen = strlen(filepath);

		if ( bInitFirstEntry )
		{
			if (filepathlen > 0)
			{
				if (_ResolveLink(filepath, filepath))
				{
					OutputDebugString("Resolving Link");
					OutputDebugString(filepath);
					bShortcut = TRUE;
				}
				else
				{
					bShortcut = FALSE;
					if (filepathlen > MAX_PATH) {
						continue;
					}
				}
				if (aStream[0] && aStream[0]->Open(filepath))
				{
					total_ms = aStream[0]->GetLengthMS();
					aStream[0]->Close();

					OutputDebugString(fd.cFileName);

					_pMP3List = new tMP3Entry;

					if (_pMP3List == NULL)
						break;

					nNumMP3s = 1;

					strcpy(_pMP3List->aFilename, fd.cFileName);

					_pMP3List->nTrackLength = total_ms;
					_pMP3List->pNext = NULL;

					if (bShortcut)
					{
						_pMP3List->pLinkPath = new char[MAX_PATH + sizeof(fd.cFileName)];
						strcpy(_pMP3List->pLinkPath, filepath);
					}
					else
					{
						_pMP3List->pLinkPath = NULL;
					}

					pList = _pMP3List;

					bInitFirstEntry = FALSE;
				}
				else
				{
					strcat(filepath, " - NOT A VALID MP3");
					OutputDebugString(filepath);
				}
			}
			else
				break;
		}
		else
		{
			if ( filepathlen > 0 )
			{
				if ( _ResolveLink(filepath, filepath) )
				{
					OutputDebugString("Resolving Link");
					OutputDebugString(filepath);
					bShortcut = TRUE;
				}
				else
					bShortcut = FALSE;

				if (aStream[0] && aStream[0]->Open(filepath))
				{
					total_ms = aStream[0]->GetLengthMS();
					aStream[0]->Close();

					OutputDebugString(fd.cFileName);

					pList->pNext = new tMP3Entry;

					tMP3Entry *e = pList->pNext;

					if ( e == NULL )
						break;

					pList = pList->pNext;

					strcpy(e->aFilename, fd.cFileName);
					e->nTrackLength = total_ms;
					e->pNext = NULL;

					if ( bShortcut )
					{
						e->pLinkPath = new char [MAX_PATH + sizeof(fd.cFileName)];
						strcpy(e->pLinkPath, filepath);
					}
					else
					{
						e->pLinkPath = NULL;
					}

					nNumMP3s++;

					OutputDebugString(fd.cFileName);
				}
				else
				{
					strcat(filepath, " - NOT A VALID MP3");
					OutputDebugString(filepath);
				}
			}
		}
	} while (FindNextFile(hFind, &fd));

	FindClose(hFind);
}

static void
_DeleteMP3Entries(void)
{
	tMP3Entry *e = _pMP3List;

	while ( e != NULL )
	{
		tMP3Entry *next = e->pNext;

		if ( next == NULL )
			next = NULL;

		if ( e->pLinkPath != NULL )
		{
#ifndef FIX_BUGS
			delete   e->pLinkPath; // BUG: should be delete []
#else
			delete[] e->pLinkPath;
#endif
			e->pLinkPath = NULL;
		}

		delete e;

		if ( next )
			e = next;
		else
			e = NULL;

		nNumMP3s--;
	}

	if ( nNumMP3s != 0 )
	{
		OutputDebugString("Not all MP3 entries were deleted");
		nNumMP3s = 0;
	}

	_pMP3List = NULL;
}

static tMP3Entry *
_GetMP3EntryByIndex(uint32 idx)
{
	uint32 n = ( idx < nNumMP3s ) ? idx : 0;

	if ( _pMP3List != NULL )
	{
		tMP3Entry *e = _pMP3List;

		for ( uint32 i = 0; i < n; i++ )
			e = e->pNext;

		return e;

	}

	return NULL;
}

static inline bool8
_GetMP3PosFromStreamPos(uint32 *pPosition, tMP3Entry **pEntry)
{
	_CurMP3Index = 0;

	for ( *pEntry = _pMP3List; *pEntry != NULL; *pEntry = (*pEntry)->pNext )
	{
		if (   *pPosition >= (*pEntry)->nTrackStreamPos
			&& *pPosition <  (*pEntry)->nTrackLength + (*pEntry)->nTrackStreamPos )
		{
			*pPosition -= (*pEntry)->nTrackStreamPos;
			_CurMP3Pos = *pPosition;

			return TRUE;
		}

		_CurMP3Index++;
	}

	*pPosition = 0;
	*pEntry = _pMP3List;
	_CurMP3Pos = 0;
	_CurMP3Index = 0;

	return FALSE;
}

bool8
cSampleManager::IsMP3RadioChannelAvailable(void)
{
	return nNumMP3s != 0;
}

void cSampleManager::ReleaseDigitalHandle(void)
{
	// TODO? alcSuspendContext
}

void cSampleManager::ReacquireDigitalHandle(void)
{
	// TODO? alcProcessContext
}

bool8
cSampleManager::Initialise(void)
{
	if ( _bSampmanInitialised )
		return TRUE;

	EFXInit();

	for(int i = 0; i < MAX_STREAMS; i++)
		aStream[i] = new CStream(ALStreamSources[i], ALStreamBuffers[i]);

	CStream::Initialise();

	{
		for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ )
		{
			m_aSamples[i].nOffset    = 0;
			m_aSamples[i].nSize      = 0;
			m_aSamples[i].nFrequency = 22050;
			m_aSamples[i].nLoopStart = 0;
			m_aSamples[i].nLoopEnd   = -1;
		}

		m_nEffectsVolume     = MAX_VOLUME;
		m_nMusicVolume       = MAX_VOLUME;
		m_nEffectsFadeVolume = MAX_VOLUME;
		m_nMusicFadeVolume   = MAX_VOLUME;

		m_nMonoMode = 0;
	}

	{
		curprovider = -1;
		prevprovider = -1;

		_usingEFX = false;
		usingEAX =0;
		usingEAX3=0;

		_fEffectsLevel = 0.0f;

		_maxSamples = 0;

		ALDevice = NULL;
		ALContext = NULL;
	}

	{
		fpSampleDescHandle = NULL;
		fpSampleDataHandle = NULL;

		for ( int32 i = 0; i < MAX_SFX_BANKS; i++ )
		{
			gBankLoaded[i]                   = LOADING_STATUS_NOT_LOADED;
			nSampleBankDiscStartOffset[i]    = 0;
			nSampleBankSize[i]               = 0;
			nSampleBankMemoryStartAddress[i] = 0;
		}
	}

	{
		for ( int32 i = 0; i < MAX_PEDSFX; i++ )
		{
			nPedSlotSfx[i]     = NO_SAMPLE;
			nPedSlotSfxAddr[i] = 0;
		}

		nCurrentPedSlot = 0;
	}

	{
		for ( int32 i = 0; i < NUM_CHANNELS; i++ )
			nChannelVolume[i] = 0;
	}

	add_providers();

	{
		int index = 0;
		_maxSamples = Min(MAXCHANNELS, providers[index].sources);

		ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ,
						ALC_MONO_SOURCES, MAX_DIGITAL_MIXER_CHANNELS - MAX2DCHANNELS,
						ALC_STEREO_SOURCES, MAX2DCHANNELS,
						0,
						};

		ALDevice  = alcOpenDevice(providers[index].id);
		ASSERT(ALDevice != NULL);

		ALContext = alcCreateContext(ALDevice, attr);
		ASSERT(ALContext != NULL);

		alcMakeContextCurrent(ALContext);

		const char* ext=(const char*)alGetString(AL_EXTENSIONS);
		ASSERT(strstr(ext,"AL_SOFT_loop_points")!=NULL);
		if ( strstr(ext,"AL_SOFT_loop_points")==NULL )
		{
			Terminate();
			return FALSE;
		}

		alListenerf (AL_GAIN,     1.0f);
		alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
		alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
		ALfloat orientation[6] = { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f };
		alListenerfv(AL_ORIENTATION, orientation);

		alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);

		if ( alcIsExtensionPresent(ALDevice, (ALCchar*)ALC_EXT_EFX_NAME) )
		{
			_effectsSupported = providers[index].bSupportsFx;
			alGenAuxiliaryEffectSlots(1, &ALEffectSlot);
			alGenEffects(1, &ALEffect);
		}

		alGenSources(MAX_STREAMS*2, ALStreamSources[0]);
		for ( int32 i = 0; i < MAX_STREAMS; i++ )
		{
			alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
			alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE);
			alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f);
			alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f);
			alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE);
			alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f);
			alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f);
		}

		CChannel::InitChannels();

		for ( int32 i = 0; i < MAXCHANNELS; i++ )
			aChannel[i].Init(i);
		for ( int32 i = 0; i < MAX2DCHANNELS; i++ )
			aChannel[MAXCHANNELS+i].Init(MAXCHANNELS+i, true);

		if ( IsFXSupported() )
		{
			/**/
			alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);
			/**/

			for ( int32 i = 0; i < MAXCHANNELS; i++ )
				aChannel[i].SetReverbMix(ALEffectSlot, 0.0f);
		}
	}

	{
		for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
			nStreamLength[i] = 0;
	}

#ifdef AUDIO_CACHE
	FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb");
	if (cacheFile) {
		debug("Loadind audio cache (If game crashes around here, then your cache is corrupted, remove audio/sound.cache)\n");
		fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
		fclose(cacheFile);
	} else
	{
		debug("Cannot load audio cache\n");
#endif

		for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
		{
			if(aStream[0] && (
#ifdef PS2_AUDIO_PATHS
				aStream[0]->Open(PS2StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000) ||
#endif
				aStream[0]->Open(StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000)))
			{
				uint32 tatalms = aStream[0]->GetLengthMS();
				aStream[0]->Close();
				nStreamLength[i] = tatalms;
			} else
				USERERROR("Can't open '%s'\n", StreamedNameTable[i]);
		}
#ifdef AUDIO_CACHE
		cacheFile = fcaseopen("audio\\sound.cache", "wb");
		if(cacheFile) {
			debug("Saving audio cache\n");
			fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
			fclose(cacheFile);
		} else {
			debug("Cannot save audio cache\n");
		}
	}
#endif

	{
		if ( !InitialiseSampleBanks() )
		{
			Terminate();
			return FALSE;
		}

		nSampleBankMemoryStartAddress[SFX_BANK_0] = (uintptr)malloc(nSampleBankSize[SFX_BANK_0]);
		ASSERT(nSampleBankMemoryStartAddress[SFX_BANK_0] != 0);

		if ( nSampleBankMemoryStartAddress[SFX_BANK_0] == 0 )
		{
			Terminate();
			return FALSE;
		}

		nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = (uintptr)malloc(PED_BLOCKSIZE*MAX_PEDSFX);
		ASSERT(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0);

		LoadSampleBank(SFX_BANK_0);
	}

	{
		for ( int32 i = 0; i < MAX_STREAMS; i++ )
		{
			aStream[i]->Close();

			nStreamVolume[i] = 100;
			nStreamPan[i]    = 63;
		}
	}

	{
		_bSampmanInitialised = TRUE;

		if ( defaultProvider >= 0 && defaultProvider < m_nNumberOfProviders )
		{
			set_new_provider(defaultProvider);
		}
		else
		{
			Terminate();
			return FALSE;
		}
	}

	{
		nNumMP3s = 0;

		_pMP3List = NULL;

		_FindMP3s();

		if ( nNumMP3s != 0 )
		{
			nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] = 0;

			for ( tMP3Entry *e = _pMP3List; e != NULL; e = e->pNext )
			{
				e->nTrackStreamPos = nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER];
				nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] += e->nTrackLength;
			}

			time_t t = time(NULL);
			tm *localtm;
			bool8 bUseRandomTable;

			if ( t == -1 )
				bUseRandomTable = TRUE;
			else
			{
				bUseRandomTable = FALSE;
				localtm = localtime(&t);
			}

			int32 randval;
			if ( bUseRandomTable )
				randval = AudioManager.m_anRandomTable[1];
			else
				randval = localtm->tm_sec * localtm->tm_min;

			_CurMP3Index = randval % nNumMP3s;

			tMP3Entry *randmp3 = _pMP3List;
			for ( int32 i = randval % nNumMP3s; i > 0; --i)
				randmp3 = randmp3->pNext;

			if ( bUseRandomTable )
				_CurMP3Pos = AudioManager.m_anRandomTable[0]     % randmp3->nTrackLength;
			else
			{
				if ( localtm->tm_sec > 0 )
				{
					int32 s = localtm->tm_sec;
					_CurMP3Pos = s*s*s*s*s*s*s*s                 % randmp3->nTrackLength;
				}
				else
					_CurMP3Pos = AudioManager.m_anRandomTable[0] % randmp3->nTrackLength;
			}
		}
		else
			_CurMP3Pos = 0;

		_bIsMp3Active = FALSE;
	}

	return TRUE;
}

void
cSampleManager::Terminate(void)
{
	for (int32 i = 0; i < MAX_STREAMS; i++)
		aStream[i]->Close();

	for ( int32 i = 0; i < NUM_CHANNELS; i++ )
		aChannel[i].Term();

	if ( IsFXSupported() )
	{
		if ( alIsEffect(ALEffect) )
		{
			alEffecti(ALEffect, AL_EFFECT_TYPE, AL_EFFECT_NULL);
			alDeleteEffects(1, &ALEffect);
			ALEffect = AL_EFFECT_NULL;
		}

		if (alIsAuxiliaryEffectSlot(ALEffectSlot))
		{
			alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, AL_EFFECT_NULL);

			alDeleteAuxiliaryEffectSlots(1, &ALEffectSlot);
			ALEffectSlot = AL_EFFECTSLOT_NULL;
		}
	}

	for ( int32 i = 0; i < MAX_STREAMS; i++ )
	{
		alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
	}
	alDeleteSources(MAX_STREAMS*2, ALStreamSources[0]);

	CChannel::DestroyChannels();

	if ( ALContext )
	{
		alcMakeContextCurrent(NULL);
		alcSuspendContext(ALContext);
		alcDestroyContext(ALContext);
	}
	if ( ALDevice )
		alcCloseDevice(ALDevice);

	ALDevice = NULL;
	ALContext = NULL;

	_fPrevEaxRatioDestination = 0.0f;
	_usingEFX                 = false;
	_fEffectsLevel            = 0.0f;

	_DeleteMP3Entries();

	CStream::Terminate();

	for(int32 i = 0; i < MAX_STREAMS; i++)
		delete aStream[i];

	if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 )
	{
		free((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]);
		nSampleBankMemoryStartAddress[SFX_BANK_0] = 0;
	}

	if ( nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] != 0 )
	{
		free((void *)nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS]);
		nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] = 0;
	}

	_bSampmanInitialised = FALSE;
}

bool8 cSampleManager::CheckForAnAudioFileOnCD(void)
{
	return TRUE;
}

char cSampleManager::GetCDAudioDriveLetter(void)
{
	return '\0';
}

void
cSampleManager::UpdateEffectsVolume(void)
{
	if ( _bSampmanInitialised )
	{
		for ( int32 i = 0; i < NUM_CHANNELS; i++ )
		{
			if ( GetChannelUsedFlag(i) )
			{
				if ( nChannelVolume[i] != 0 )
					aChannel[i].SetVolume(m_nEffectsFadeVolume*nChannelVolume[i]*m_nEffectsVolume >> 14);
			}
		}
	}
}

void
cSampleManager::SetEffectsMasterVolume(uint8 nVolume)
{
	m_nEffectsVolume = nVolume;
	UpdateEffectsVolume();
}

void
cSampleManager::SetMusicMasterVolume(uint8 nVolume)
{
	m_nMusicVolume = nVolume;
}

void
cSampleManager::SetEffectsFadeVolume(uint8 nVolume)
{
	m_nEffectsFadeVolume = nVolume;
	UpdateEffectsVolume();
}

void
cSampleManager::SetMusicFadeVolume(uint8 nVolume)
{
	m_nMusicFadeVolume = nVolume;
}

void
cSampleManager::SetMonoMode(uint8 nMode)
{
	m_nMonoMode = nMode;
}

bool8
cSampleManager::LoadSampleBank(uint8 nBank)
{
	ASSERT( nBank < MAX_SFX_BANKS);

	if ( CTimer::GetIsCodePaused() )
		return FALSE;

	if ( MusicManager.IsInitialised()
		&& MusicManager.GetMusicMode() == MUSICMODE_CUTSCENE
		&& nBank != SFX_BANK_0 )
	{
		return FALSE;
	}

#ifdef OPUS_SFX
	int samplesRead = 0;
	int samplesSize = nSampleBankSize[nBank] / 2;
	op_pcm_seek(fpSampleDataHandle, 0);
	while (samplesSize > 0) {
		int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[nBank] + samplesRead), samplesSize, NULL);
		if (size <= 0) {
			// huh?
			//assert(0);
			break;
		}
		samplesRead += size*2;
		samplesSize -= size;
	}
#else
	if ( fseek(fpSampleDataHandle, nSampleBankDiscStartOffset[nBank], SEEK_SET) != 0 )
		return FALSE;

	if ( fread((void *)nSampleBankMemoryStartAddress[nBank], 1, nSampleBankSize[nBank], fpSampleDataHandle) != nSampleBankSize[nBank] )
		return FALSE;
#endif
	gBankLoaded[nBank] = LOADING_STATUS_LOADED;

	return TRUE;
}

void
cSampleManager::UnloadSampleBank(uint8 nBank)
{
	ASSERT( nBank < MAX_SFX_BANKS);

	gBankLoaded[nBank] = LOADING_STATUS_NOT_LOADED;
}

int8
cSampleManager::IsSampleBankLoaded(uint8 nBank)
{
	ASSERT( nBank < MAX_SFX_BANKS);

	return gBankLoaded[nBank];
}

uint8
cSampleManager::IsPedCommentLoaded(uint32 nComment)
{
	ASSERT( nComment < TOTAL_AUDIO_SAMPLES );

	for ( int32 i = 0; i < _TODOCONST(3); i++ )
	{
#ifdef FIX_BUGS
		int8 slot = (int8)nCurrentPedSlot - i - 1;
		if (slot < 0)
			slot += ARRAY_SIZE(nPedSlotSfx);
#else
		uint8 slot = nCurrentPedSlot - i - 1;
#endif
		if ( nComment == nPedSlotSfx[slot] )
			return LOADING_STATUS_LOADED;
	}

	return LOADING_STATUS_NOT_LOADED;
}

int32
cSampleManager::_GetPedCommentSlot(uint32 nComment)
{
	for (int32 i = 0; i < _TODOCONST(3); i++)
	{
#ifdef FIX_BUGS
		int8 slot = (int8)nCurrentPedSlot - i - 1;
		if (slot < 0)
			slot += ARRAY_SIZE(nPedSlotSfx);
#else
		uint8 slot = nCurrentPedSlot - i - 1;
#endif
		if (nComment == nPedSlotSfx[slot])
			return slot;
	}

	return -1;
}

bool8
cSampleManager::LoadPedComment(uint32 nComment)
{
	ASSERT( nComment < TOTAL_AUDIO_SAMPLES );

	if ( CTimer::GetIsCodePaused() )
		return FALSE;

	// no talking peds during cutsenes or the game end
	if ( MusicManager.IsInitialised() )
	{
		switch ( MusicManager.GetMusicMode() )
		{
			case MUSICMODE_CUTSCENE:
			{
				return FALSE;

				break;
			}

			case MUSICMODE_FRONTEND:
			{
				if ( MusicManager.GetNextTrack() == STREAMED_SOUND_GAME_COMPLETED )
					return FALSE;

				break;
			}
		}
	}

#ifdef OPUS_SFX
	int samplesRead = 0;
	int samplesSize = m_aSamples[nComment].nSize / 2;
	op_pcm_seek(fpSampleDataHandle, m_aSamples[nComment].nOffset / 2);
	while (samplesSize > 0) {
		int size = op_read(fpSampleDataHandle, (opus_int16 *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE * nCurrentPedSlot + samplesRead),
		                   samplesSize, NULL);
		if (size <= 0) {
			return FALSE;
		}
		samplesRead += size * 2;
		samplesSize -= size;
	}
#else
	if ( fseek(fpSampleDataHandle, m_aSamples[nComment].nOffset, SEEK_SET) != 0 )
		return FALSE;

	if ( fread((void *)(nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE*nCurrentPedSlot), 1, m_aSamples[nComment].nSize, fpSampleDataHandle) != m_aSamples[nComment].nSize )
		return FALSE;

#endif
	nPedSlotSfx[nCurrentPedSlot] = nComment;

	if ( ++nCurrentPedSlot >= MAX_PEDSFX )
		nCurrentPedSlot = 0;

	return TRUE;
}

int32
cSampleManager::GetBankContainingSound(uint32 offset)
{
	if ( offset >= BankStartOffset[SFX_BANK_PED_COMMENTS] )
		return SFX_BANK_PED_COMMENTS;

	if ( offset >= BankStartOffset[SFX_BANK_0] )
		return SFX_BANK_0;

	return INVALID_SFX_BANK;
}

uint32
cSampleManager::GetSampleBaseFrequency(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return m_aSamples[nSample].nFrequency;
}

uint32
cSampleManager::GetSampleLoopStartOffset(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return m_aSamples[nSample].nLoopStart;
}

int32
cSampleManager::GetSampleLoopEndOffset(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return m_aSamples[nSample].nLoopEnd;
}

uint32
cSampleManager::GetSampleLength(uint32 nSample)
{
	ASSERT( nSample < TOTAL_AUDIO_SAMPLES );
	return m_aSamples[nSample].nSize / sizeof(uint16);
}

bool8 cSampleManager::UpdateReverb(void)
{
	if ( !usingEAX && !_usingEFX )
		return FALSE;

	if ( AudioManager.m_FrameCounter & 15 )
		return FALSE;

#ifdef AUDIO_REFLECTIONS
	float y = AudioManager.m_afReflectionsDistances[REFLECTION_TOP]  + AudioManager.m_afReflectionsDistances[REFLECTION_BOTTOM];
	float x = AudioManager.m_afReflectionsDistances[REFLECTION_LEFT] + AudioManager.m_afReflectionsDistances[REFLECTION_RIGHT];
	float z = AudioManager.m_afReflectionsDistances[REFLECTION_UP];
#else
	float x = 0.0f;
	float y = 0.0f;
	float z = 0.0f;
#endif

	float normy = norm(y, 5.0f, 40.0f);
	float normx = norm(x, 5.0f, 40.0f);
	float normz = norm(z, 5.0f, 40.0f);

	#define ZR(v, a, b) (((v)==0)?(a):(b))
	#define CALCRATIO(x,y,z,min,max,val) (ZR(y, ZR(x, ZR(z, min, max), min), ZR(x, ZR(z, min, max), ZR(z, min, val))))

	float fRatio = CALCRATIO(normx, normy, normz, 0.3f, 0.5f, (normy+normx+normz)/3.0f);

	#undef CALCRATIO
	#undef ZR

	fRatio = Clamp(fRatio, usingEAX3==1 ? 0.0f : 0.30f, 1.0f);

	if ( fRatio == _fPrevEaxRatioDestination )
		return FALSE;

#ifdef JUICY_OAL
	if ( usingEAX3 || _usingEFX )
#else
	if ( usingEAX3 )
#endif
	{
		if ( EAX3ListenerInterpolate(&StartEAX3, &FinishEAX3, fRatio, &EAX3Params, false) )
		{
			EAX_SetAll(&EAX3Params);

			/*
			if ( IsFXSupported() )
			{
				alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);

				for ( int32 i = 0; i < MAXCHANNELS; i++ )
					aChannel[i].UpdateReverb(ALEffectSlot);
			}
			*/

			_fEffectsLevel = 1.0f - fRatio * 0.5f;
		}
	}
	else
	{
		if ( _usingEFX )
			_fEffectsLevel = (1.0f - fRatio) * 0.4f;
		else
			_fEffectsLevel = (1.0f - fRatio) * 0.7f;
	}

	_fPrevEaxRatioDestination = fRatio;

	return TRUE;
}

void
cSampleManager::SetChannelReverbFlag(uint32 nChannel, bool8 nReverbFlag)
{
	ASSERT( nChannel < NUM_CHANNELS );

	if ( usingEAX || _usingEFX )
	{
		if ( IsFXSupported() )
		{
			alAuxiliaryEffectSloti(ALEffectSlot, AL_EFFECTSLOT_EFFECT, ALEffect);

			if ( nReverbFlag != FALSE )
				aChannel[nChannel].SetReverbMix(ALEffectSlot, _fEffectsLevel);
			else
				aChannel[nChannel].SetReverbMix(ALEffectSlot, 0.0f);
		}
	}
}

bool8
cSampleManager::InitialiseChannel(uint32 nChannel, uint32 nSfx, uint8 nBank)
{
	ASSERT( nChannel < NUM_CHANNELS );

	uintptr addr;

	if ( nSfx < SAMPLEBANK_MAX )
	{
		if ( !IsSampleBankLoaded(nBank) )
			return FALSE;

		addr = nSampleBankMemoryStartAddress[nBank] + m_aSamples[nSfx].nOffset - m_aSamples[BankStartOffset[nBank]].nOffset;
	}
	else
	{
		int32 i;
		for ( i = 0; i < _TODOCONST(3); i++ )
		{
			int32 slot = nCurrentPedSlot - i - 1;
#ifdef FIX_BUGS
			if (slot < 0)
				slot += ARRAY_SIZE(nPedSlotSfx);
#endif
			if ( nSfx == nPedSlotSfx[slot] )
			{
				addr = (nSampleBankMemoryStartAddress[SFX_BANK_PED_COMMENTS] + PED_BLOCKSIZE * slot);
				break;
			}
		}

		if (i == _TODOCONST(3))
			return FALSE;
	}

	if ( GetChannelUsedFlag(nChannel) )
	{
		TRACE("Stopping channel %d - really!!!", nChannel);
		StopChannel(nChannel);
	}

	aChannel[nChannel].Reset();
	if ( aChannel[nChannel].HasSource() )
	{
		aChannel[nChannel].SetSampleData   ((void*)addr, m_aSamples[nSfx].nSize, m_aSamples[nSfx].nFrequency);
		aChannel[nChannel].SetLoopPoints   (0, -1);
		aChannel[nChannel].SetPitch        (1.0f);
		return TRUE;
	}

	return FALSE;
}

void
cSampleManager::SetChannelEmittingVolume(uint32 nChannel, uint32 nVolume)
{
	ASSERT( nChannel < MAXCHANNELS );

	uint32 vol = nVolume;
	if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

	nChannelVolume[nChannel] = vol;

	// reduce channel volume when JB.MP3 or S4_BDBD.MP3 playing
	if (   MusicManager.GetMusicMode()    == MUSICMODE_CUTSCENE
		&& MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO
		&& MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD )
	{
		nChannelVolume[nChannel] = vol / 4;
	}

	// no idea, does this one looks like a bug or it's SetChannelVolume ?
	aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*nChannelVolume[nChannel]*m_nEffectsVolume >> 14);
}

void
cSampleManager::SetChannel3DPosition(uint32 nChannel, float fX, float fY, float fZ)
{
	ASSERT( nChannel < MAXCHANNELS );

	aChannel[nChannel].SetPosition(-fX, fY, fZ);
}

void
cSampleManager::SetChannel3DDistances(uint32 nChannel, float fMax, float fMin)
{
	ASSERT( nChannel < MAXCHANNELS );
	aChannel[nChannel].SetDistances(fMax, fMin);
}

void
cSampleManager::SetChannelVolume(uint32 nChannel, uint32 nVolume)
{
	ASSERT( nChannel >= MAXCHANNELS );
	ASSERT( nChannel < NUM_CHANNELS );

	if ( nChannel == CHANNEL_POLICE_RADIO )
	{
		uint32 vol = nVolume;
		if ( vol > MAX_VOLUME ) vol = MAX_VOLUME;

		nChannelVolume[nChannel] = vol;

		// reduce the volume for JB.MP3 and S4_BDBD.MP3
		if (   MusicManager.GetMusicMode()    == MUSICMODE_CUTSCENE
			&& MusicManager.GetNextTrack() != STREAMED_SOUND_NEWS_INTRO
			&& MusicManager.GetNextTrack() != STREAMED_SOUND_CUTSCENE_SAL4_BDBD )
		{
			nChannelVolume[nChannel] = vol / 4;
		}

		aChannel[nChannel].SetVolume(m_nEffectsFadeVolume*vol*m_nEffectsVolume >> 14);
	}
}

void
cSampleManager::SetChannelPan(uint32 nChannel, uint32 nPan)
{
	ASSERT( nChannel >= MAXCHANNELS );
	ASSERT( nChannel < NUM_CHANNELS );

	if ( nChannel == CHANNEL_POLICE_RADIO )
	{
		aChannel[nChannel].SetPan(nPan);
	}
}

void
cSampleManager::SetChannelFrequency(uint32 nChannel, uint32 nFreq)
{
	ASSERT( nChannel < NUM_CHANNELS );

	aChannel[nChannel].SetCurrentFreq(nFreq);
}

void
cSampleManager::SetChannelLoopPoints(uint32 nChannel, uint32 nLoopStart, int32 nLoopEnd)
{
	ASSERT( nChannel < NUM_CHANNELS );

	aChannel[nChannel].SetLoopPoints(nLoopStart / (DIGITALBITS / 8), nLoopEnd / (DIGITALBITS / 8));
}

void
cSampleManager::SetChannelLoopCount(uint32 nChannel, uint32 nLoopCount)
{
	ASSERT( nChannel < NUM_CHANNELS );

	aChannel[nChannel].SetLoopCount(nLoopCount);
}

bool8
cSampleManager::GetChannelUsedFlag(uint32 nChannel)
{
	ASSERT( nChannel < NUM_CHANNELS );

	return aChannel[nChannel].IsUsed();
}

void
cSampleManager::StartChannel(uint32 nChannel)
{
	ASSERT( nChannel < NUM_CHANNELS );

	aChannel[nChannel].Start();
}

void
cSampleManager::StopChannel(uint32 nChannel)
{
	ASSERT( nChannel < NUM_CHANNELS );

	aChannel[nChannel].Stop();
}

void
cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	if ( nFile < TOTAL_STREAMED_SOUNDS )
	{
		CStream *stream = aStream[nStream];

		stream->Close();

#ifdef PS2_AUDIO_PATHS
		if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
#endif
			stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
		if ( !stream->Setup() )
		{
			stream->Close();
		}
	}
}

void
cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	CStream *stream = aStream[nStream];

	if ( stream->IsOpened() )
	{
		stream->SetPause(nPauseFlag != FALSE);
	}
}

void
cSampleManager::StartPreloadedStreamedFile(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	CStream *stream = aStream[nStream];

	if ( stream->IsOpened() )
	{
		stream->Start();
	}
}

bool8
cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
	uint32 i = 0;
	uint32 position = nPos;
	char filename[MAX_PATH];

	if ( nFile >= TOTAL_STREAMED_SOUNDS )
		return FALSE;

	aStream[nStream]->Close();

	if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER )
	{
		do
		{
			// Switched to MP3 player just now
			if ( !_bIsMp3Active && i == 0 )
			{
				if ( nPos > nStreamLength[STREAMED_SOUND_RADIO_MP3_PLAYER] )
					position = 0;
				tMP3Entry *e = _pMP3List;

				// Try to continue from previous song, if already started
				if(!_GetMP3PosFromStreamPos(&position, &e) && !e) {
					nFile = 0;
					CStream *stream = aStream[nStream];
#ifdef PS2_AUDIO_PATHS
					if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
#endif
						stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
					if ( stream->Setup() ) {
						if (position != 0)
							stream->SetPosMS(position);

						stream->Start();

						return TRUE;
					} else {
						stream->Close();
					}
					return FALSE;

				} else {
					if (e->pLinkPath != NULL)
						aStream[nStream]->Open(e->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
					else {
						strcpy(filename, _mp3DirectoryPath);
						strcat(filename, e->aFilename);

						aStream[nStream]->Open(filename);
					}

					if (aStream[nStream]->Setup()) {
						if (position != 0)
							aStream[nStream]->SetPosMS(position);

						aStream[nStream]->Start();

						_bIsMp3Active = TRUE;
						return TRUE;
					} else {
						aStream[nStream]->Close();
					}
					// fall through, start playing from another song
				}
			} else {
				if(++_CurMP3Index >= nNumMP3s) _CurMP3Index = 0;

				_CurMP3Pos = 0;

				tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index);
				if ( !mp3 )
				{
					mp3 = _pMP3List;
					if ( !_pMP3List )
					{
						nFile = 0;
						_bIsMp3Active = FALSE;
						CStream *stream = aStream[nStream];
#ifdef PS2_AUDIO_PATHS
						if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
#endif
							stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);

						if (stream->Setup()) {
							if (position != 0)
								stream->SetPosMS(position);

							stream->Start();

							return TRUE;
						} else {
							stream->Close();
						}
						return FALSE;
					}
				}
				if (mp3->pLinkPath != NULL)
					aStream[nStream]->Open(mp3->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
				else {
					strcpy(filename, _mp3DirectoryPath);
					strcat(filename, mp3->aFilename);
					aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
				}

				if (aStream[nStream]->Setup()) {
					aStream[nStream]->Start();
#ifdef FIX_BUGS
					_bIsMp3Active = TRUE;
#endif
					return TRUE;
				} else {
					aStream[nStream]->Close();
				}

			}
			_bIsMp3Active = FALSE;
		}
		while ( ++i < nNumMP3s );
		position = 0;
		nFile = 0;
	}
	CStream *stream = aStream[nStream];
#ifdef PS2_AUDIO_PATHS
	if(!stream->Open(PS2StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000))
#endif
		stream->Open(StreamedNameTable[nFile], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);

	if ( stream->Setup() ) {
		if (position != 0)
			stream->SetPosMS(position);

		stream->Start();

		return TRUE;
	} else {
		stream->Close();
	}
	return FALSE;
}

void
cSampleManager::StopStreamedFile(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	CStream *stream = aStream[nStream];

	stream->Close();

	if ( nStream == 0 )
		_bIsMp3Active = FALSE;
}

int32
cSampleManager::GetStreamedFilePosition(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	CStream *stream = aStream[nStream];

	if ( stream->IsOpened() )
	{
		if ( _bIsMp3Active )
		{
			tMP3Entry *mp3 = _GetMP3EntryByIndex(_CurMP3Index);

			if ( mp3 != NULL )
			{
				return stream->GetPosMS() + mp3->nTrackStreamPos;
			}
			else
				return 0;
		}
		else
		{
			return stream->GetPosMS();
		}
	}

	return 0;
}

void
cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffectFlag, uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	if ( nVolume > MAX_VOLUME )
		nVolume = MAX_VOLUME;

	if ( nPan > MAX_VOLUME )
		nPan = MAX_VOLUME;

	nStreamVolume[nStream] = nVolume;
	nStreamPan   [nStream] = nPan;

	CStream *stream = aStream[nStream];

	if ( stream->IsOpened() )
	{
		if ( nEffectFlag )
			stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14);
		else
			stream->SetVolume(m_nMusicFadeVolume*nVolume*m_nMusicVolume >> 14);

		stream->SetPan(nPan);
	}
}

int32
cSampleManager::GetStreamedFileLength(uint8 nStream)
{
	ASSERT( nStream < TOTAL_STREAMED_SOUNDS );

	return nStreamLength[nStream];
}

bool8
cSampleManager::IsStreamPlaying(uint8 nStream)
{
	ASSERT( nStream < MAX_STREAMS );

	CStream *stream = aStream[nStream];

	if ( stream->IsOpened() )
	{
		if ( stream->IsPlaying() )
			return TRUE;
	}

	return FALSE;
}

void
cSampleManager::Service(void)
{
	for ( int32 i = 0; i < MAX_STREAMS; i++ )
	{
		CStream *stream = aStream[i];

		if ( stream->IsOpened() )
			stream->Update();
	}
	int refCount = CChannel::channelsThatNeedService;
	for ( int32 i = 0; refCount && i < NUM_CHANNELS; i++ )
	{
		if ( aChannel[i].Update() )
			refCount--;
	}
}

bool8
cSampleManager::InitialiseSampleBanks(void)
{
	int32 nBank = SFX_BANK_0;

	fpSampleDescHandle = fcaseopen(SampleBankDescFilename, "rb");
	if ( fpSampleDescHandle == NULL )
		return FALSE;
#ifndef OPUS_SFX
	fpSampleDataHandle = fcaseopen(SampleBankDataFilename, "rb");
	if ( fpSampleDataHandle == NULL )
	{
		fclose(fpSampleDescHandle);
		fpSampleDescHandle = NULL;

		return FALSE;
	}

	fseek(fpSampleDataHandle, 0, SEEK_END);
	int32 _nSampleDataEndOffset = ftell(fpSampleDataHandle);
	rewind(fpSampleDataHandle);
#else
	int e;
	fpSampleDataHandle = op_open_file(SampleBankDataFilename, &e);
#endif
	fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle);
#ifdef OPUS_SFX
	int32 _nSampleDataEndOffset = m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nOffset + m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nSize;
#endif
	fclose(fpSampleDescHandle);
	fpSampleDescHandle = NULL;

	for ( int32 i = 0; i < TOTAL_AUDIO_SAMPLES; i++ )
	{
#ifdef FIX_BUGS
		if (nBank >= MAX_SFX_BANKS) break;
#endif
		if ( BankStartOffset[nBank] == BankStartOffset[SFX_BANK_0] + i )
		{
			nSampleBankDiscStartOffset[nBank] = m_aSamples[i].nOffset;
			nBank++;
		}
	}

	nSampleBankSize[SFX_BANK_0] = nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS] - nSampleBankDiscStartOffset[SFX_BANK_0];
	nSampleBankSize[SFX_BANK_PED_COMMENTS]  = _nSampleDataEndOffset                      - nSampleBankDiscStartOffset[SFX_BANK_PED_COMMENTS];

	return TRUE;
}
#endif