/*********************************************************************************
* SGP Digital Sound Module
*
*		This module handles the playing of digital samples, preloaded or streamed.
* 
* Derek Beland, May 28, 1997
*********************************************************************************/
#ifdef JA2_PRECOMPILED_HEADERS
	#include "JA2 SGP ALL.H"
#elif defined( WIZ8_PRECOMPILED_HEADERS )
	#include "WIZ8 SGP ALL.H"
#else
	#include <stdio.h>
	#include <string.h>
	#include "soundman.h"
	#include "FileMan.h"
	#include "debug.h"
	#include "MemMan.h"
	#include "mss.h"
	#include "random.h"
#endif

// Uncomment this to disable the startup of sound hardware
//#define SOUND_DISABLE

#pragma pack(push,1)

// WAV file chunk definitions
typedef struct {
				// General chunk header
				CHAR8				cTag[4];
				UINT32			uiChunkSize;
				} WAVCHUNK;

typedef struct {
				// WAV header
				CHAR8				cRiff[4];				// "RIFF"
				UINT32			uiChunkSize;		// Chunk length
				CHAR8				cFileType[4];		// "WAVE"
				} WAVRIFF;

typedef struct {
				// FMT chunk				
				CHAR8				cFormat[4];			// "FMT "
				UINT32			uiChunkSize;		// Chunk length
				UINT16			uiStereo;				// 1 if stereo, 0 if mono (Not reliable, use channels instead)
				UINT16			uiChannels;			// number of channels used 1=mono, 2=stereo, etc.
				UINT32			uiSpeed;				// Sampling Rate (speed)
				UINT32			uiBytesSec;			// Number of bytes per sec
				UINT16			uiBytesSample;	// Number of bytes per sample (1 = 8 bit mono,
																		// 2 = 8 bit stereo or 16 bit mono, 4 = 16 bit stereo
				UINT16			uiBitsSample;		// bits per sample
				} WAVFMT;

typedef struct {
				// Data chunk
				CHAR8				cName[4];				// "DATA"
				UINT32			uiChunkSize;		// Chunk length
				} WAVDATA;
	
#pragma pack(pop)

#define	WAV_CHUNK_RIFF					0
#define	WAV_CHUNK_FMT						1
#define WAV_CHUNK_DATA					2

#define NUM_WAV_CHUNKS	3

CHAR8	*cWAVChunks[3]={"RIFF", "FMT ", "DATA"};


// global settings
#define		SOUND_MAX_CACHED			128						// number of cache slots

#ifdef JA2
#define		SOUND_MAX_CHANNELS		16						// number of mixer channels
#else
#define		SOUND_MAX_CHANNELS		32						// number of mixer channels
#endif

#pragma message("TEMP!")

#define		SOUND_DEFAULT_MEMORY	(8048*1024)		// default memory limit
#define		SOUND_DEFAULT_THRESH	(256*8024)		// size for sample to be double-buffered
#define		SOUND_DEFAULT_STREAM	(64*1024)			// double-buffered buffer size

/*#define		SOUND_DEFAULT_MEMORY	(2048*1024)		// default memory limit
#define		SOUND_DEFAULT_THRESH	(256*1024)		// size for sample to be double-buffered
#define		SOUND_DEFAULT_STREAM	(64*1024)			// double-buffered buffer size
*/
// playing/random value to indicate default
#define		SOUND_PARMS_DEFAULT		0xffffffff

// Sound status flags
#define		SOUND_CALLBACK				0x00000008


// Local Function Prototypes
BOOLEAN		SoundInitCache(void);
BOOLEAN		SoundShutdownCache(void);
UINT32		SoundLoadSample(STR pFilename);
UINT32		SoundFreeSample(STR pFilename);
UINT32		SoundGetCached(STR pFilename);
UINT32		SoundLoadDisk(STR pFilename);

// Low level
UINT32		SoundGetEmptySample(void);
BOOLEAN		SoundProcessWAVHeader(UINT32 uiSample);
UINT32		SoundFreeSampleIndex(UINT32 uiSample);
UINT32		SoundGetIndexByID(UINT32 uiSoundID);
static HDIGDRIVER SoundInitDriver(UINT32 uiRate, UINT16 uiBits, UINT16 uiChans);
BOOLEAN		SoundInitHardware(void);
BOOLEAN		SoundGetDriverName(HDIGDRIVER DIG, CHAR8 *cBuf);
BOOLEAN		SoundShutdownHardware(void);
UINT32		SoundGetFreeChannel(void);
UINT32		SoundStartSample(UINT32 uiSample, UINT32 uiChannel, SOUNDPARMS *pParms);
UINT32		SoundStartStream(STR pFilename, UINT32 uiChannel, SOUNDPARMS *pParms);
UINT32		SoundGetUniqueID(void);
BOOLEAN		SoundPlayStreamed(STR pFilename);
BOOLEAN		SoundCleanCache(void);
BOOLEAN		SoundSampleIsPlaying(UINT32 uiSample);
BOOLEAN		SoundIndexIsPlaying(UINT32 uiSound);
BOOLEAN		SoundStopIndex(UINT32 uiSound);
UINT32		SoundGetVolumeIndex(UINT32 uiChannel);
BOOLEAN		SoundSetVolumeIndex(UINT32 uiChannel, UINT32 uiVolume);

// Global variables
UINT32		guiSoundDefaultVolume = 127;
UINT32		guiSoundMemoryLimit=SOUND_DEFAULT_MEMORY;			// Maximum memory used for sounds
UINT32		guiSoundMemoryUsed=0;													// Memory currently in use
UINT32		guiSoundCacheThreshold=SOUND_DEFAULT_THRESH;	// Double-buffered threshold

HDIGDRIVER hSoundDriver;																// Sound driver handle
BOOLEAN		fDirectSound=TRUE;														// Using Direct Sound

// Local module variables
BOOLEAN		fSoundSystemInit=FALSE;												// Startup called T/F
BOOLEAN		gfEnableStartup=TRUE;													// Allow hardware to starup

// Sample cache list for files loaded
SAMPLETAG	pSampleList[SOUND_MAX_CACHED];								
// Sound channel list for output channels
SOUNDTAG	pSoundList[SOUND_MAX_CHANNELS];

// 3D sound globals
CHAR8				*gpProviderName=NULL;
HPROVIDER		gh3DProvider=0;
H3DPOBJECT	gh3DListener=0;
BOOLEAN			gfUsingEAX=TRUE;
UINT32			guiRoomTypeIndex=0;

CHAR8* pEAXRoomTypes[EAXROOMTYPE_NUM_TYPES] =
{
	// None
	"PLAIN",

	// S,M,L Cave
	"STONECORRIDOR",
	"CAVE",
	"CONCERTHALL",

	// S,M,L Room
	"LIVINGROOM",
	"ROOM",
	"AUDITORIUM",

	// Flat open, valley
	"CITY",
	"MOUNTAINS",

	// Swimming
	"UNDERWATER"
};

//*******************************************************************************
// High Level Interface
//*******************************************************************************

//*******************************************************************************
// SoundEnableSound
//
//	Allows or disallows the startup of the sound hardware.
//
//	Returns:	Nothing.
//
//*******************************************************************************
void SoundEnableSound(BOOLEAN fEnable)
{
	gfEnableStartup=fEnable;
}


//*******************************************************************************
// InitializeSoundManager
//
//	Zeros out the structs for the system info, and initializes the cache.
//
//	Returns:	TRUE always
//
//*******************************************************************************
BOOLEAN InitializeSoundManager(void)
{
UINT32 uiCount;

	if(fSoundSystemInit)
		ShutdownSoundManager();

	for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
		memset(&pSoundList[uiCount], 0, sizeof(SOUNDTAG));
  
#ifndef SOUND_DISABLE
	if(gfEnableStartup && SoundInitHardware())
		fSoundSystemInit=TRUE;
#endif

	SoundInitCache();
	
	guiSoundMemoryLimit=SOUND_DEFAULT_MEMORY;
	guiSoundMemoryUsed=0;
	guiSoundCacheThreshold=SOUND_DEFAULT_THRESH;

	if(gpProviderName && !gh3DProvider)
		Sound3DInitProvider(gpProviderName);

	return(TRUE);
}

//*******************************************************************************
// ShutdownSoundManager
//
//		Silences all currently playing sound, deallocates any memory allocated,
//	and releases the sound hardware.
//
//*******************************************************************************
void ShutdownSoundManager(void)
{
	if(gh3DProvider)
		Sound3DShutdownProvider();

	SoundStopAll();
	SoundStopMusic();
	SoundShutdownCache();
	Sleep(1000);
	SoundShutdownHardware();
	//Sleep(1000);
	fSoundSystemInit=FALSE;

}

//*******************************************************************************
// SoundPlay
//
//		Starts a sample playing. If the sample is not loaded in the cache, it will	
//	be found and loaded. The pParms structure is used to
//	override the attributes of the sample such as playback speed, and to specify
//	a volume. Any entry containing SOUND_PARMS_DEFAULT will be set by the system.
//
//	Returns:	If the sound was started, it returns a sound ID unique to that
//						instance of the sound
//						If an error occured, SOUND_ERROR will be returned
//
//
//	!!Note:  Can no longer play streamed files
//
//*******************************************************************************

UINT32 SoundPlay(STR pFilename, SOUNDPARMS *pParms)
{
	UINT32 uiSample, uiChannel;

	if( fSoundSystemInit )
	{
		if( !SoundPlayStreamed(pFilename) )
		{
			if((uiSample=SoundLoadSample(pFilename))!=NO_SAMPLE)
			{
				if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
				{
					return(SoundStartSample(uiSample, uiChannel, pParms));
				}
			}
		}
		else
		{
			//Trying to play a sound which is bigger then the 'guiSoundCacheThreshold'
			
			// This line was causing a page fault in the Wiz 8 project, so
			// I changed it to the second line, which works OK. -- DB

			//DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("\n*******\nSoundPlay():  ERROR:  trying to play %s which is bigger then the 'guiSoundCacheThreshold', use SoundPlayStreamedFile() instead\n", pFilename ) );
			
			FastDebugMsg(String("SoundPlay: ERROR: Trying to play %s sound is too lardge to load into cache, use SoundPlayStreamedFile() instead\n", pFilename ) );
		}
	}

	return(SOUND_ERROR);
}


//*******************************************************************************
// SoundPlayStreamedFile
//
//		The sample will
//	be played as a double-buffered sample. The pParms structure is used to
//	override the attributes of the sample such as playback speed, and to specify
//	a volume. Any entry containing SOUND_PARMS_DEFAULT will be set by the system.
//
//	Returns:	If the sound was started, it returns a sound ID unique to that
//						instance of the sound
//						If an error occured, SOUND_ERROR will be returned
//
//*******************************************************************************
UINT32	SoundPlayStreamedFile( STR pFilename, SOUNDPARMS *pParms )
{
	UINT32	uiChannel;
	HANDLE	hRealFileHandle;
	CHAR8		pFileHandlefileName[ 128 ];
	HWFILE	hFile;
	UINT32	uiRetVal=FALSE;

	if( fSoundSystemInit )
	{
		if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
		{
			//Open the file
			hFile = FileOpen( pFilename, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
			if( !hFile )
			{
				FastDebugMsg(String("\n*******\nSoundPlayStreamedFile():  ERROR:  Couldnt open '%s' in SoundPlayStreamedFile()\n", pFilename ) );
				return( SOUND_ERROR );
			}

			// MSS cannot determine which provider to play if you don't give it a real filename
			// so if the file isn't in a library, play it normally
			if(DB_EXTRACT_LIBRARY(hFile) == REAL_FILE_LIBRARY_ID)
			{
				FileClose(hFile);
				return(SoundStartStream( pFilename, uiChannel, pParms));
			}

			//Get the real file handle of the file
			hRealFileHandle = GetRealFileHandleFromFileManFileHandle( hFile );
			if( hRealFileHandle == 0 )
			{
				FastDebugMsg(String("\n*******\nSoundPlayStreamedFile():  ERROR:  Couldnt get a real file handle for '%s' in SoundPlayStreamedFile()\n", pFilename ) );
				return( SOUND_ERROR );
			}

			//Convert the file handle into a 'name'
			sprintf( pFileHandlefileName, "\\\\\\\\%d", hRealFileHandle );

			//Start the sound stream
			uiRetVal = SoundStartStream( pFileHandlefileName, uiChannel, pParms);

			//if it succeeded, record the file handle
			if( uiRetVal != SOUND_ERROR )
				pSoundList[uiChannel].hFile = hFile;
			else
				FileClose( hFile );

			return( uiRetVal );
		}
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundPlayRandom
//
//		Registers a sample to be played randomly within the specified parameters.
//	Parameters are passed in through pParms. Any parameter containing
//	SOUND_PARMS_DEFAULT will be set by the system. Only the uiTimeMin entry may
//	NOT be defaulted. 
//
//	* Samples designated "random" are ALWAYS loaded into the cache, and locked
//	in place. They are never double-buffered, and this call will fail if they
//	cannot be loaded. *
//
//	Returns:	If successful, it returns the sample index it is loaded to, else
//						SOUND_ERROR is returned.
//
//*******************************************************************************
UINT32 SoundPlayRandom(STR pFilename, RANDOMPARMS *pParms)
{
UINT32 uiSample, uiTicks;

	if(fSoundSystemInit)
	{
		if((uiSample=SoundLoadSample(pFilename))!=NO_SAMPLE)
		{
			pSampleList[uiSample].uiFlags|=(SAMPLE_RANDOM|SAMPLE_LOCKED);
			
			if(pParms->uiTimeMin==SOUND_PARMS_DEFAULT)
				return(SOUND_ERROR);
			else
				pSampleList[uiSample].uiTimeMin=pParms->uiTimeMin;

			if(pParms->uiTimeMax==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiTimeMax=pParms->uiTimeMin;
			else
				pSampleList[uiSample].uiTimeMax=pParms->uiTimeMax;

			pSampleList[uiSample].uiSpeedMin=pParms->uiSpeedMin;

			pSampleList[uiSample].uiSpeedMax=pParms->uiSpeedMax;

			if(pParms->uiVolMin==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiVolMin=guiSoundDefaultVolume;
			else
				pSampleList[uiSample].uiVolMin=pParms->uiVolMin;

			if(pParms->uiVolMax==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiVolMax=guiSoundDefaultVolume;
			else
				pSampleList[uiSample].uiVolMax=pParms->uiVolMax;

			if(pParms->uiPanMin==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiPanMin=64;
			else
				pSampleList[uiSample].uiPanMin=pParms->uiPanMin;

			if(pParms->uiPanMax==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiPanMax=64;
			else
				pSampleList[uiSample].uiPanMax=pParms->uiPanMax;

			if(pParms->uiMaxInstances==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiMaxInstances=1;
			else
				pSampleList[uiSample].uiMaxInstances=pParms->uiMaxInstances;

			if(pParms->uiPriority==SOUND_PARMS_DEFAULT)
				pSampleList[uiSample].uiPriority=PRIORITY_RANDOM;
			else
				pSampleList[uiSample].uiPriority=pParms->uiPriority;

			pSampleList[uiSample].uiInstances=0;

			uiTicks=GetTickCount();
			pSampleList[uiSample].uiTimeNext=GetTickCount()+pSampleList[uiSample].uiTimeMin+Random(pSampleList[uiSample].uiTimeMax-pSampleList[uiSample].uiTimeMin);
			return(uiSample);
		}
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundStreamCallback
//
//	Plays a sound through streaming, and executes a callback for each buffer
//	loaded.
//
//	Returns:	If successful, it returns the sample index it is loaded to, else
//						SOUND_ERROR is returned.
//
//*******************************************************************************
UINT32 SoundStreamCallback(STR pFilename, SOUNDPARMS *pParms, void (*pCallback)(UINT8 *, UINT32, UINT32, UINT32, void *), void *pData)
{
UINT32 uiChannel, uiSoundID;
	
	if(fSoundSystemInit)
	{
		if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
		{
			uiSoundID=SoundStartStream(pFilename, uiChannel, pParms);
			if(uiSoundID!=SOUND_ERROR)
			{
				AIL_auto_service_stream(pSoundList[uiChannel].hMSSStream, FALSE);
				pSoundList[uiChannel].pCallback=pCallback;
				pSoundList[uiChannel].pData=pData;
				pSoundList[uiChannel].uiFlags|=SOUND_CALLBACK;

				return(uiSoundID);
			}
		}
	}
	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundIsPlaying
//
//		Returns TRUE/FALSE that an instance of a sound is still playing.
//
//*******************************************************************************
BOOLEAN SoundIsPlaying(UINT32 uiSoundID)
{
UINT32 uiSound;
	
	if(fSoundSystemInit)
	{
		uiSound=SoundGetIndexByID(uiSoundID);
		if(uiSound!=NO_SAMPLE)
			return(SoundIndexIsPlaying(uiSound));
	}

	return(FALSE);
}


//*****************************************************************************************
// SoundIndexIsPlaying
// 
// Returns TRUE/FALSE whether a sound channel's sample is currently playing.
// 
// Returns BOOLEAN            - TRUE = playing, FALSE = stopped or nothing allocated
// 
// UINT32 uiSound             - Channel number of sound
//
// Created:  2/24/00 Derek Beland
//*****************************************************************************************
BOOLEAN SoundIndexIsPlaying(UINT32 uiSound)
{
INT32 iStatus=SMP_DONE;

	if(fSoundSystemInit)
	{
		if(pSoundList[uiSound].hMSS!=NULL)
			iStatus = AIL_sample_status(pSoundList[uiSound].hMSS);
		
		if(pSoundList[uiSound].hMSSStream!=NULL)
			iStatus = AIL_stream_status(pSoundList[uiSound].hMSSStream);

		if(pSoundList[uiSound].hM3D!=NULL)
			iStatus = AIL_3D_sample_status(pSoundList[uiSound].hM3D);

		return((iStatus!=SMP_DONE) && (iStatus!=SMP_STOPPED));
	}

	return(FALSE);
}

//*******************************************************************************
// SoundStop
//
//		Stops the playing of a sound instance, if still playing.
//
//	Returns:	TRUE if the sample was actually stopped, FALSE if it could not be
//						found, or was not playing.
//
//*******************************************************************************
BOOLEAN SoundStop(UINT32 uiSoundID)
{
UINT32 uiSound;
	
	if(fSoundSystemInit)
	{
		if(SoundIsPlaying(uiSoundID))
		{
			uiSound=SoundGetIndexByID(uiSoundID);
			if(uiSound!=NO_SAMPLE)
			{
				SoundStopIndex(uiSound);
				return(TRUE);
			}
		}
	}
	return(FALSE);
}

//*******************************************************************************
// SoundStopGroup
//
//		Stops multiple instances of sounds that have the indicated priority. This
//	is useful for silencing all ambient sounds when switching to menus, etc.
//
//	Returns:	TRUE if samples were actually stopped, FALSE if none were found
//
//*******************************************************************************
BOOLEAN SoundStopGroup(UINT32 uiPriority)
{
UINT32 uiCount;
BOOLEAN fStopped=FALSE;

	if(fSoundSystemInit)
	{
		for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
		{
			if((pSoundList[uiCount].hMSS!=NULL) || (pSoundList[uiCount].hMSSStream!=NULL) || (pSoundList[uiCount].hM3D!=NULL))
			{
				if(pSoundList[uiCount].uiPriority==uiPriority)
				{
					SoundStop(pSoundList[uiCount].uiSoundID);
					fStopped=TRUE;
				}
			}
		}
	}

	return(fStopped);
}

//*******************************************************************************
// SoundSetMemoryLimit
//
//		Specifies how much memory the sound system is allowed to dynamically 
// allocate. Once this limit is reached, the cache code will start dropping the
// least-used samples. You should always set the limit higher by a good margin
// than your actual memory requirements, to give the cache some elbow room.
//
//	Returns:	TRUE if the limit was set, or FALSE if the memory already used is
//						greater than the limit requested.
//
//*******************************************************************************
BOOLEAN SoundSetMemoryLimit(UINT32 uiLimit)
{
	if(guiSoundMemoryLimit < guiSoundMemoryUsed)
		return(FALSE);

	guiSoundMemoryLimit=uiLimit;
	return(TRUE);
}

//*******************************************************************************
// SoundGetSystemInfo
//
//		Returns information about the capabilities of the hardware. Currently does
//	nothing.
//
//	Returns:	FALSE, always
//
//*******************************************************************************
BOOLEAN SoundGetSystemInfo(void)
{
	return(FALSE);
}

//*******************************************************************************
// SoundSetDigitalVolume
//
//		Sets the master volume for the digital section. All sample volumes will be
//	affected by this setting.
//
//	Returns:	TRUE, always
//
//*******************************************************************************
BOOLEAN SoundSetDigitalVolume(UINT32 uiVolume)
{
UINT32 uiVolClip;

	if(fSoundSystemInit)
	{
		uiVolClip=__min(uiVolume, 127);
		AIL_set_digital_master_volume(hSoundDriver, uiVolClip);
	}
	return(TRUE);
}
	
//*******************************************************************************
// SoundGetDigitalVolume
//
//		Returns the current value of the digital master volume.
//
//	Returns:	0-127
//
//*******************************************************************************
UINT32 SoundGetDigitalVolume(UINT32 uiVolume)
{
	if(fSoundSystemInit)
		return((UINT32)AIL_digital_master_volume(hSoundDriver));
	else
		return(0);
}



//*****************************************************************************************
// SoundSetDefaultVolume
// 
// Sets the volume to use when a default is not chosen.
// 
// Returns BOOLEAN            - 
// 
// UINT32 uiVolume            - 
//
// Created:  3/28/00 Derek Beland
//*****************************************************************************************
void SoundSetDefaultVolume(UINT32 uiVolume)
{
	guiSoundDefaultVolume=__min(uiVolume, 127);
}
	

//*****************************************************************************************
// SoundGetDefaultVolume
// 
// 
// 
// Returns UINT32             - 
// 
// UINT32 uiVolume            - 
//
// Created:  3/28/00 Derek Beland
//*****************************************************************************************
UINT32 SoundGetDefaultVolume(void)
{
	return(guiSoundDefaultVolume);
}



//*******************************************************************************
// SoundStopAll
//
//		Stops all currently playing sounds.
//
//	Returns:	TRUE, always
//
//*******************************************************************************
BOOLEAN SoundStopAll(void)
{
UINT32 uiCount;

	if(fSoundSystemInit)
	{
		for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
			if(!pSoundList[uiCount].fMusic)
				SoundStopIndex(uiCount);
	}
	
	return(TRUE);
}


//*****************************************************************************************
// SoundSetFadeVolume
// 
// Sets a target volume to fade towards. The fade volume is updated in SoundServiceStreams.
// 
// Returns BOOLEAN            - TRUE if the fading volume was set, FALSE otherwise
// 
// UINT32 uiSoundID           - ID of sound 
// UINT32 uiVolume            - Volume to fade towards (0-127)
// UINT32 uiRate              - Total time taken to change volume
// BOOLEAN fStopAtZero        - If TRUE, sample is stopped when volume reaches zero
//
// Created:  3/17/00 Derek Beland
//*****************************************************************************************
BOOLEAN SoundSetFadeVolume(UINT32 uiSoundID, UINT32 uiVolume, UINT32 uiRate, BOOLEAN fStopAtZero)
{
UINT32 uiSound, uiVolCap, uiVolumeDiff;

	if(fSoundSystemInit)
	{
		uiVolCap=__min(uiVolume, 127);			
		
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			uiVolumeDiff = abs(uiVolCap - SoundGetVolumeIndex(uiSound));

			if(!uiVolumeDiff)
				return(FALSE);

			pSoundList[uiSound].uiFadeVolume = uiVolCap;
			pSoundList[uiSound].fStopAtZero = fStopAtZero;
			pSoundList[uiSound].uiFadeRate = uiRate / uiVolumeDiff;
			pSoundList[uiSound].uiFadeTime = GetTickCount(); 

			return(TRUE);
		}
	}

	return(FALSE);
}

//*******************************************************************************
// SoundSetVolume
//
//		Sets the volume on a currently playing sound. 
//
//	Returns:	TRUE if the volume was actually set on the sample, FALSE if the
//						sample had already expired or couldn't be found
//
//*******************************************************************************
BOOLEAN SoundSetVolume(UINT32 uiSoundID, UINT32 uiVolume)
{
UINT32 uiSound, uiVolCap;

	if(fSoundSystemInit)
	{
		uiVolCap=__min(uiVolume, 127);			
		
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			pSoundList[uiSound].uiFadeVolume = uiVolume;
			return(SoundSetVolumeIndex(uiSound, uiVolume));
		}
	}

	return(FALSE);
}


//*****************************************************************************************
// SoundSetVolumeIndex
// 
// Sounds the volume on a sound channel.
// 
// Returns BOOLEAN            - TRUE if the volume was set
// 
// UINT32 uiChannel           - Sound channel	
// UINT32 uiVolume            - New volume 0-127
//
// Created:  3/17/00 Derek Beland
//*****************************************************************************************
BOOLEAN SoundSetVolumeIndex(UINT32 uiChannel, UINT32 uiVolume)
{
UINT32 uiVolCap;

	if(fSoundSystemInit)
	{
		uiVolCap=__min(uiVolume, 127);			
		
		if(pSoundList[uiChannel].hMSS!=NULL)
			AIL_set_sample_volume(pSoundList[uiChannel].hMSS, uiVolCap);

		if(pSoundList[uiChannel].hMSSStream!=NULL)
			AIL_set_stream_volume(pSoundList[uiChannel].hMSSStream, uiVolCap);

		if(pSoundList[uiChannel].hM3D!=NULL)
			AIL_set_3D_sample_volume(pSoundList[uiChannel].hM3D, uiVolCap);

		return(TRUE);
	}

	return(FALSE);
}

//*******************************************************************************
// SoundSetPan
//
//		Sets the pan on a currently playing sound. 
//
//	Returns:	TRUE if the pan was actually set on the sample, FALSE if the
//						sample had already expired or couldn't be found
//
//*******************************************************************************
BOOLEAN SoundSetPan(UINT32 uiSoundID, UINT32 uiPan)
{
UINT32 uiSound, uiPanCap;

	if(fSoundSystemInit)
	{
		uiPanCap=__min(uiPan, 127);			
		
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
				AIL_set_sample_pan(pSoundList[uiSound].hMSS, uiPanCap);

			if(pSoundList[uiSound].hMSSStream!=NULL)
				AIL_set_stream_pan(pSoundList[uiSound].hMSSStream, uiPanCap);

			return(TRUE);
		}
	}

	return(FALSE);
}

//*******************************************************************************
// SoundSetFrequency
//
//		Sets the frequency on a currently playing sound. 
//
//	Returns:	TRUE if the frequency was actually set on the sample, FALSE if the
//						sample had already expired or couldn't be found
//
//*******************************************************************************
BOOLEAN SoundSetFrequency(UINT32 uiSoundID, UINT32 uiFreq)
{
UINT32 uiSound, uiFreqCap;

	if(fSoundSystemInit)
	{
		uiFreqCap=__min(uiFreq, 44100);			
		
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
				AIL_set_sample_playback_rate(pSoundList[uiSound].hMSS, uiFreqCap);

			if(pSoundList[uiSound].hMSSStream!=NULL)
				AIL_set_stream_playback_rate(pSoundList[uiSound].hMSSStream, uiFreqCap);

			if(pSoundList[uiSound].hM3D!=NULL)
				AIL_set_3D_sample_playback_rate(pSoundList[uiSound].hM3D, uiFreqCap);

			return(TRUE);
		}
	}

	return(FALSE);
}

//*******************************************************************************
// SoundSetLoop
//
//		Sets the loop on a currently playing sound. 
//
//	Returns:	TRUE if the loop was actually set on the sample, FALSE if the
//						sample had already expired or couldn't be found
//
//*******************************************************************************
BOOLEAN SoundSetLoop(UINT32 uiSoundID, UINT32 uiLoop)
{
UINT32 uiSound, uiLoopCap;

	if(fSoundSystemInit)
	{
		uiLoopCap=__min(uiLoop, 10000);			
		
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
				AIL_set_sample_loop_count(pSoundList[uiSound].hMSS, uiLoopCap);

			if(pSoundList[uiSound].hMSSStream!=NULL)
				AIL_set_stream_loop_count(pSoundList[uiSound].hMSSStream, uiLoopCap);

			if(pSoundList[uiSound].hM3D!=NULL)
				AIL_set_3D_sample_loop_count(pSoundList[uiSound].hM3D, uiLoopCap);

			return(TRUE);
		}
	}

	return(FALSE);
}

//*******************************************************************************
// SoundGetVolume
//
//		Returns the current volume setting of a sound that is playing. If the sound
//	has expired, or could not be found, SOUND_ERROR is returned.
//
//*******************************************************************************
UINT32 SoundGetVolume(UINT32 uiSoundID)
{
UINT32 uiSound;

	if(fSoundSystemInit)
	{
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
			return(SoundGetVolumeIndex(uiSound));
	}

	return(SOUND_ERROR);
}


//*****************************************************************************************
// SoundGetVolumeIndex
// 
// Returns the current volume of a sound channel.
// 
// Returns UINT32             - Volume 0-127
// 
// UINT32 uiChannel           - Channel
//
// Created:  3/17/00 Derek Beland
//*****************************************************************************************
UINT32 SoundGetVolumeIndex(UINT32 uiChannel)
{
	if(fSoundSystemInit)
	{
		if(pSoundList[uiChannel].hMSS!=NULL)
			return((UINT32)AIL_sample_volume(pSoundList[uiChannel].hMSS));

		if(pSoundList[uiChannel].hMSSStream!=NULL)
			return((UINT32)AIL_stream_volume(pSoundList[uiChannel].hMSSStream));

		if(pSoundList[uiChannel].hM3D!=NULL)
			return((UINT32)AIL_3D_sample_volume(pSoundList[uiChannel].hM3D));
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundGetPan
//
//		Returns the current pan setting of a sound that is playing. If the sound
//	has expired, or could not be found, SOUND_ERROR is returned.
//
//*******************************************************************************
UINT32 SoundGetPan(UINT32 uiSoundID)
{
UINT32 uiSound;

	if(fSoundSystemInit)
	{
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
				return((UINT32)AIL_sample_pan(pSoundList[uiSound].hMSS));

			if(pSoundList[uiSound].hMSSStream!=NULL)
				return((UINT32)AIL_stream_pan(pSoundList[uiSound].hMSSStream));
		}
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundGetFrequency
//
//		Returns the current frequency setting of a sound that is playing. If the sound
//	has expired, or could not be found, SOUND_ERROR is returned.
//
//*******************************************************************************
UINT32 SoundGetFrequency(UINT32 uiSoundID)
{
UINT32 uiSound;

	if(fSoundSystemInit)
	{
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
				return((UINT32)AIL_sample_playback_rate(pSoundList[uiSound].hMSS));

			if(pSoundList[uiSound].hMSSStream!=NULL)
				return((UINT32)AIL_stream_playback_rate(pSoundList[uiSound].hMSSStream));

			if(pSoundList[uiSound].hM3D!=NULL)
				return((UINT32)AIL_3D_sample_playback_rate(pSoundList[uiSound].hM3D));
		}
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundGetLoop
//
//		Returns the current loop count of a sound that is playing. If the sound
//	has expired, or could not be found, SOUND_ERROR is returned.
//
//*******************************************************************************
UINT32 SoundGetLoop(UINT32 uiSoundID)
{
UINT32 uiSound;

	if(fSoundSystemInit)
	{
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
				return((UINT32)AIL_sample_loop_count(pSoundList[uiSound].hMSS));

			if(pSoundList[uiSound].hMSSStream!=NULL)
				return((UINT32)AIL_stream_loop_count(pSoundList[uiSound].hMSSStream));

			if(pSoundList[uiSound].hM3D!=NULL)
				return((UINT32)AIL_3D_sample_loop_count(pSoundList[uiSound].hM3D));
		}
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// SoundServiceRandom
//
//		This function should be polled by the application if random samples are
//	used. The time marks on each are checked and if it is time to spawn a new
//	instance of the sound, the number already in existance are checked, and if
//	there is room, a new one is made and the count updated. 
//		If random samples are not being used, there is no purpose in polling this
//	function.
// 
//	Returns:	TRUE if a new random sound was created, FALSE if nothing was done.
//
//*******************************************************************************
BOOLEAN SoundServiceRandom(void)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
	{
		if(!(pSampleList[uiCount].uiFlags&SAMPLE_RANDOM_MANUAL) && SoundRandomShouldPlay(uiCount))
			SoundStartRandom(uiCount);
	}

	return(FALSE);
}

//*******************************************************************************
// SoundRandomShouldPlay
//
//	Determines whether a random sound is ready for playing or not.
// 
//	Returns:	TRUE if a the sample should be played.
//
//*******************************************************************************
BOOLEAN SoundRandomShouldPlay(UINT32 uiSample)
{
UINT32 uiTicks;

	uiTicks=GetTickCount();
	if(pSampleList[uiSample].uiFlags&SAMPLE_RANDOM)
		if(pSampleList[uiSample].uiTimeNext <= GetTickCount())
			if(pSampleList[uiSample].uiInstances < pSampleList[uiSample].uiMaxInstances)
				return(TRUE);

	return(FALSE);
}

//*******************************************************************************
// SoundStartRandom
//
//	Starts an instance of a random sample.
// 
//	Returns:	TRUE if a new random sound was created, FALSE if nothing was done.
//
//*******************************************************************************
UINT32 SoundStartRandom(UINT32 uiSample)
{
UINT32 uiChannel, uiSoundID;
SOUNDPARMS spParms;

	if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
	{
		memset(&spParms, 0xff, sizeof(SOUNDPARMS));

//		spParms.uiSpeed=pSampleList[uiSample].uiSpeedMin+Random(pSampleList[uiSample].uiSpeedMax-pSampleList[uiSample].uiSpeedMin);
		spParms.uiVolume=pSampleList[uiSample].uiVolMin+Random(pSampleList[uiSample].uiVolMax-pSampleList[uiSample].uiVolMin);
		spParms.uiPan=pSampleList[uiSample].uiPanMin+Random(pSampleList[uiSample].uiPanMax-pSampleList[uiSample].uiPanMin);
		spParms.uiLoop=1;
		spParms.uiPriority=pSampleList[uiSample].uiPriority;

		if((uiSoundID=SoundStartSample(uiSample, uiChannel, &spParms))!=SOUND_ERROR)
		{
			pSampleList[uiSample].uiTimeNext=GetTickCount()+pSampleList[uiSample].uiTimeMin+Random(pSampleList[uiSample].uiTimeMax-pSampleList[uiSample].uiTimeMin);
			pSampleList[uiSample].uiInstances++;
			return(uiSoundID);
		}
	}
	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundStopAllRandom
//
//		This function should be polled by the application if random samples are
//	used. The time marks on each are checked and if it is time to spawn a new
//	instance of the sound, the number already in existance are checked, and if
//	there is room, a new one is made and the count updated. 
//		If random samples are not being used, there is no purpose in polling this
//	function.
// 
//	Returns:	TRUE if a new random sound was created, FALSE if nothing was done.
//
//*******************************************************************************
BOOLEAN SoundStopAllRandom(void)
{
UINT32 uiChannel, uiSample;

	// Stop all currently playing random sounds
	for(uiChannel=0; uiChannel < SOUND_MAX_CHANNELS; uiChannel++)
	{
		if((pSoundList[uiChannel].hMSS!=NULL) || (pSoundList[uiChannel].hM3D!=NULL))
		{
			uiSample=pSoundList[uiChannel].uiSample;
			
			// if this was a random sample, decrease the iteration count
			if(pSampleList[uiSample].uiFlags&SAMPLE_RANDOM)
				SoundStopIndex(uiChannel);
		}
	}

	// Unlock all random sounds so they can be dumped from the cache, and
	// take the random flag off so they won't be serviced/played
	for(uiSample=0; uiSample < SOUND_MAX_CACHED; uiSample++)
	{
		if(pSampleList[uiSample].uiFlags & SAMPLE_RANDOM)
			pSampleList[uiSample].uiFlags &= (~(SAMPLE_RANDOM | SAMPLE_LOCKED));
	}
			
	return(FALSE);
}

//*******************************************************************************
// SoundServiceStreams
//
//		Can be polled in tight loops where sound buffers might starve due to heavy
//	hardware use, etc. Streams DO NOT normally need to be serviced manually, but
//	in some cases (heavy file loading) it might be desirable.
//
//		If you are using the end of sample callbacks, you must call this function
//	periodically to check the sample's status.
// 
//	Returns:	TRUE always.
//
//*******************************************************************************
BOOLEAN SoundServiceStreams(void)
{
UINT32 uiCount, uiSpeed, uiBuffLen, uiBytesPerSample;
UINT8		*pBuffer;
void		*pData;


	if(fSoundSystemInit)
	{
		for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
		{
			if(pSoundList[uiCount].hMSSStream!=NULL)
			{
				if(AIL_service_stream(pSoundList[uiCount].hMSSStream, 0))
				{
					if(pSoundList[uiCount].uiFlags&SOUND_CALLBACK)
					{
						uiSpeed=pSoundList[uiCount].hMSSStream->datarate;
						uiBuffLen=pSoundList[uiCount].hMSSStream->bufsize;
						pBuffer=pSoundList[uiCount].hMSSStream->bufs[pSoundList[uiCount].hMSSStream->buf1];
						uiBytesPerSample=pSoundList[uiCount].hMSSStream->samp->format;
						pData=pSoundList[uiCount].pData;
						pSoundList[uiCount].pCallback(pBuffer, uiBuffLen, uiSpeed, uiBytesPerSample, pData);
					}
				}
			}

			if(pSoundList[uiCount].hMSS || pSoundList[uiCount].hMSSStream || pSoundList[uiCount].hM3D)
			{
				// If a sound has a handle, but isn't playing, stop it and free up the handle
				if(!SoundIsPlaying(pSoundList[uiCount].uiSoundID))
					SoundStopIndex(uiCount);
				else
				{ // Check the volume fades on currently playing sounds
					UINT32 uiVolume = SoundGetVolumeIndex(uiCount);
					UINT32 uiTime = GetTickCount();

					if((uiVolume != pSoundList[uiCount].uiFadeVolume) && (uiTime >= (pSoundList[uiCount].uiFadeTime + pSoundList[uiCount].uiFadeRate)) )
					{
						if(uiVolume < pSoundList[uiCount].uiFadeVolume)
							SoundSetVolumeIndex(uiCount, ++uiVolume);
						else if(uiVolume > pSoundList[uiCount].uiFadeVolume)
						{
							uiVolume--;
							if(!uiVolume && pSoundList[uiCount].fStopAtZero)
								SoundStopIndex(uiCount);
							else
								SoundSetVolumeIndex(uiCount, uiVolume);
						}

						pSoundList[uiCount].uiFadeTime = uiTime;
					}
				}
			}
		}
	}

	return(TRUE);
}


//*******************************************************************************
// SoundGetPosition
//
//	Reports the current time position of the sample. 
//
//	Note: You should be checking SoundIsPlaying very carefully while
//	calling this function.
//
//	Returns:	The current time of the sample in milliseconds.
//
//*******************************************************************************
UINT32 SoundGetPosition(UINT32 uiSoundID)
{
//UINT32 uiSound, uiFreq=0, uiPosition=0, uiBytesPerSample=0, uiFormat=0;
UINT32 uiSound, uiTime, uiPosition;

	if(fSoundSystemInit)
	{
		if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
		{
/*			if(pSoundList[uiSound].hMSSStream!=NULL)
			{
				uiPosition=(UINT32)AIL_stream_position(pSoundList[uiSound].hMSSStream);
				uiFreq=(UINT32)pSoundList[uiSound].hMSSStream->samp->playback_rate;
				uiFormat=(UINT32)pSoundList[uiSound].hMSSStream->samp->format;

			}
			else if(pSoundList[uiSound].hMSS!=NULL)
			{
				uiPosition=(UINT32)AIL_sample_position(pSoundList[uiSound].hMSS);
				uiFreq=(UINT32)pSoundList[uiSound].hMSS->playback_rate;
				uiFormat=(UINT32)pSoundList[uiSound].hMSS->format;
			}
		}

		switch(uiFormat)
		{
			case DIG_F_MONO_8:		uiBytesPerSample=1;
														break;
			case DIG_F_MONO_16:		uiBytesPerSample=2;
														break;
			case DIG_F_STEREO_8:	uiBytesPerSample=2;
														break;
			case DIG_F_STEREO_16:	uiBytesPerSample=4;
														break;
		}

		if(uiFreq)
		{
			return((uiPosition/uiBytesPerSample)/(uiFreq/1000));
		}
	}
*/
			uiTime=GetTickCount();
			// check for rollover
			if(uiTime < pSoundList[uiSound].uiTimeStamp)
				uiPosition=(0-pSoundList[uiSound].uiTimeStamp)+uiTime;
			else
				uiPosition=(uiTime-pSoundList[uiSound].uiTimeStamp);

			return(uiPosition);
		}
	}

	return(0);
}

//*****************************************************************************************
//
//  SoundGetMilliSecondPosition
//
//  Get the sounds total length and our current position within that 
//  sound in milliseconds
//
//
//  Returns BOOLEAN:	TRUE if the sound exists.
//
//  Created by:     Gilles Beauparlant
//  Created on:     7/23/99
//
//*****************************************************************************************
BOOLEAN SoundGetMilliSecondPosition(UINT32 uiSoundID, UINT32 *puiTotalMilliseconds, UINT32 *puiCurrentMilliseconds)
{
UINT32 uiSound;
	
	if(fSoundSystemInit)
	{
		uiSound=SoundGetIndexByID(uiSoundID);
		if(uiSound!=NO_SAMPLE)
		{
			if(pSoundList[uiSound].hMSS!=NULL)
			{
				AIL_sample_ms_position(pSoundList[uiSound].hMSS, puiTotalMilliseconds, puiCurrentMilliseconds);
				return TRUE;
			}
			
			if(pSoundList[uiSound].hMSSStream!=NULL)
			{
				AIL_stream_ms_position(pSoundList[uiSound].hMSSStream, puiTotalMilliseconds, puiCurrentMilliseconds);
				return TRUE;
			}
		}
	}
	
	*puiTotalMilliseconds = 0;
	*puiCurrentMilliseconds = 0;
	return FALSE;
}

//*******************************************************************************
// Cacheing Subsystem
//*******************************************************************************

//*******************************************************************************
// SoundInitCache
//
//		Zeros out the structures of the sample list.
//
//*******************************************************************************
BOOLEAN SoundInitCache(void)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
		memset(&pSampleList[uiCount], 0, sizeof(SAMPLETAG));

	return(TRUE);
}

//*******************************************************************************
// SoundShutdownCache
//
//		Empties out the cache.
//
//	Returns: TRUE, always
//
//*******************************************************************************
BOOLEAN SoundShutdownCache(void)
{
	SoundEmptyCache();
	return(TRUE);
}

//*******************************************************************************
// SoundSetCacheThreshold
//
//		Sets the sound size above which samples will be played double-buffered, 
// below which they will be loaded into the cache.
//
//	Returns: TRUE, always
//
//*******************************************************************************
BOOLEAN SoundSetCacheThreshhold(UINT32 uiThreshold)
{
	if(uiThreshold==0)
		guiSoundCacheThreshold=SOUND_DEFAULT_THRESH;
	else
		guiSoundCacheThreshold=uiThreshold;

	return(TRUE);
}

//*******************************************************************************
// SoundEmptyCache
//
//		Frees up all samples in the cache.
// 
//	Returns: TRUE, always
//
//*******************************************************************************
BOOLEAN SoundEmptyCache(void)
{
UINT32 uiCount;

	SoundStopAll();

	for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
		SoundFreeSampleIndex(uiCount);

	return(TRUE);
}


//*******************************************************************************
// SoundLoadSample
//
//		Frees up all samples in the cache.
// 
//	Returns: TRUE, always
//
//*******************************************************************************
UINT32 SoundLoadSample(STR pFilename)
{
UINT32 uiSample=NO_SAMPLE;

	if((uiSample=SoundGetCached(pFilename))!=NO_SAMPLE)
		return(uiSample);

	return(SoundLoadDisk(pFilename));
}

//*******************************************************************************
// SoundLockSample
//
//		Locks a sample into cache memory, so the cacheing system won't release it
//	when it needs room.
// 
//	Returns: The sample index if successful, NO_SAMPLE if the file wasn't found
//						in the cache.
//
//*******************************************************************************
UINT32 SoundLockSample(STR pFilename)
{
UINT32 uiSample;

	if((uiSample=SoundGetCached(pFilename))!=NO_SAMPLE)
	{
		pSampleList[uiSample].uiFlags|=SAMPLE_LOCKED;
		return(uiSample);
	}

	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundUnlockSample
//
//		Removes the lock on a sample so the cache is free to dump it when necessary.
// 
//	Returns: The sample index if successful, NO_SAMPLE if the file wasn't found
//						in the cache.
//
//*******************************************************************************
UINT32 SoundUnlockSample(STR pFilename)
{
UINT32 uiSample;

	if((uiSample=SoundGetCached(pFilename))!=NO_SAMPLE)
	{
		pSampleList[uiSample].uiFlags&=(~SAMPLE_LOCKED);
		return(uiSample);
	}

	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundFreeSample
//
//		Releases the resources associated with a sample from the cache.
// 
//	Returns: The sample index if successful, NO_SAMPLE if the file wasn't found
//						in the cache.
//
//*******************************************************************************
UINT32 SoundFreeSample(STR pFilename)
{
UINT32 uiSample;

	if((uiSample=SoundGetCached(pFilename))!=NO_SAMPLE)
	{
		if(!SoundSampleIsPlaying(uiSample))
		{
			SoundFreeSampleIndex(uiSample);
			return(uiSample);
		}
	}
	
	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundFreeGroup
//
//		Releases a group of samples with a given priority. Does not take into
// account locked/unlocked status.
// 
//	Returns:	TRUE if samples were freed, FALSE if none
//
// NOTE:
//		This function is going to be removed! If you are attempting to stop all
// random sounds, call SoundStopAllRandom instead. 
//
//*******************************************************************************
BOOLEAN SoundFreeGroup(UINT32 uiPriority)
{
BOOLEAN fFreed=FALSE;

	SoundStopGroup(uiPriority);

	return(fFreed);
}
//*******************************************************************************
// SoundGetCached
//
//		Tries to locate a sound by looking at what is currently loaded in the
//	cache.
// 
//	Returns: The sample index if successful, NO_SAMPLE if the file wasn't found
//						in the cache.
//
//*******************************************************************************
UINT32 SoundGetCached(STR pFilename)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
	{
		if(_stricmp(pSampleList[uiCount].pName, pFilename)==0)
			return(uiCount);
	}
	
	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundLoadDisk
//
//		Loads a sound file from disk into the cache, allocating memory and a slot
//	for storage.	
//	
// 
//	Returns: The sample index if successful, NO_SAMPLE if the file wasn't found
//						in the cache.
//
//*******************************************************************************
UINT32 SoundLoadDisk(STR pFilename)
{
HWFILE	hFile;
UINT32	uiSize, uiSample;
BOOLEAN fRemoved=TRUE;

	Assert(pFilename!=NULL);

	if((hFile=FileOpen(pFilename, FILE_ACCESS_READ, FALSE))!=0)
	{
		uiSize=FileGetSize(hFile);
		
		// if insufficient memory, start unloading old samples until either
		// there's nothing left to unload, or we fit
		fRemoved=TRUE;
		while(((uiSize + guiSoundMemoryUsed) > guiSoundMemoryLimit) && (fRemoved))
			fRemoved=SoundCleanCache();

		// if we still don't fit
		if((uiSize + guiSoundMemoryUsed) > guiSoundMemoryLimit)
		{
			FastDebugMsg(String("SoundLoadDisk:  ERROR:  trying to play %s, not enough memory\n", pFilename ) );
			FileClose(hFile);
			return(NO_SAMPLE);
		}

		// if all the sample slots are full, unloading one
		if((uiSample=SoundGetEmptySample())==NO_SAMPLE)
		{
			SoundCleanCache();
			uiSample=SoundGetEmptySample();
		}

		// if we still don't have a sample slot
		if(uiSample==NO_SAMPLE)
		{
			FastDebugMsg(String("SoundLoadDisk:  ERROR: Trying to play %s, sound channels are full\n", pFilename ) );
			FileClose(hFile);
			return(NO_SAMPLE);
		}

		memset(&pSampleList[uiSample], 0, sizeof(SAMPLETAG));

		if((pSampleList[uiSample].pData=AIL_mem_alloc_lock(uiSize))==NULL)
		{
			FastDebugMsg(String("SoundLoadDisk:  ERROR: Trying to play %s, AIL channels are full\n", pFilename ) );
			FileClose(hFile);
			return(NO_SAMPLE);
		}

		guiSoundMemoryUsed+=uiSize;

		FileRead(hFile, pSampleList[uiSample].pData, uiSize, NULL);
		FileClose(hFile);


		strcpy(pSampleList[uiSample].pName, pFilename);
		strupr(pSampleList[uiSample].pName);
		pSampleList[uiSample].uiSize=uiSize;
		pSampleList[uiSample].uiFlags|=SAMPLE_ALLOCATED;

/*		if(!strstr(pFilename, ".MP3"))
			SoundProcessWAVHeader(uiSample);
*/		return(uiSample);
	}

	return(NO_SAMPLE);
}


//*******************************************************************************
// SoundCleanCache
//
//		Removes the least-used sound from the cache to make room.
// 
//	Returns:	TRUE if a sample was freed, FALSE if none 
//
//*******************************************************************************
BOOLEAN SoundCleanCache(void)
{
UINT32 uiCount, uiLowestHits=NO_SAMPLE, uiLowestHitsCount=0;

	for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
	{
		if((pSampleList[uiCount].uiFlags&SAMPLE_ALLOCATED) &&
			!(pSampleList[uiCount].uiFlags&SAMPLE_LOCKED))
		{
			if((uiLowestHits==NO_SAMPLE) || (uiLowestHitsCount < pSampleList[uiCount].uiCacheHits))
			{
				if(!SoundSampleIsPlaying(uiCount))
				{
					uiLowestHits=uiCount;
					uiLowestHitsCount=pSampleList[uiCount].uiCacheHits;
				}
			}
		}
	}

	if(uiLowestHits!=NO_SAMPLE)
	{
		SoundFreeSampleIndex(uiLowestHits);
		return(TRUE);
	}

	return(FALSE);
}

//*******************************************************************************
// Low Level Interface (Local use only)
//*******************************************************************************

//*******************************************************************************
// SoundSampleIsPlaying
//
//		Returns TRUE/FALSE that a sample is currently in use for playing a sound.
//
//*******************************************************************************
BOOLEAN SoundSampleIsPlaying(UINT32 uiSample)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
	{
		if(pSoundList[uiCount].uiSample==uiSample)
			return(TRUE);
	}

	return(FALSE);
}

//*******************************************************************************
// SoundGetEmptySample
//
//		Returns the slot number of an available sample index.
// 
//	Returns:	A free sample index, or NO_SAMPLE if none are left.
//
//*******************************************************************************
UINT32 SoundGetEmptySample(void)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
	{
		if(!(pSampleList[uiCount].uiFlags&SAMPLE_ALLOCATED))
			return(uiCount);
	}

	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundProcessWAVHeader
//
//		Reads the information contained in the header of a loaded WAV file, and
//	transfers it to the system structures for that slot.
// 
//	Returns:	TRUE if a good header was processed, FALSE if an error occurred.
//
//*******************************************************************************
BOOLEAN SoundProcessWAVHeader(UINT32 uiSample)
{
CHAR8 *pChunk;
AILSOUNDINFO ailInfo;

	pChunk=(CHAR8 *)pSampleList[uiSample].pData;
	if(!AIL_WAV_info((void *)pChunk, &ailInfo))
		return(FALSE);

	pSampleList[uiSample].uiSpeed=ailInfo.rate;
	pSampleList[uiSample].fStereo=(BOOLEAN)(ailInfo.channels==2);
	pSampleList[uiSample].ubBits=(UINT8)ailInfo.bits;
	
	pSampleList[uiSample].pSoundStart=(PTR)ailInfo.data_ptr;
	pSampleList[uiSample].uiSoundSize=ailInfo.data_len;

	pSampleList[uiSample].uiAilWaveFormat=ailInfo.format;
	pSampleList[uiSample].uiADPCMBlockSize=ailInfo.block_size;

	return(TRUE);
}

//*******************************************************************************
// SoundFreeSampleIndex
//
//		Frees up a sample referred to by it's index slot number.	
// 
//	Returns:	Slot number if something was free, NO_SAMPLE otherwise.
//
//*******************************************************************************
UINT32 SoundFreeSampleIndex(UINT32 uiSample)
{
	if(pSampleList[uiSample].uiFlags&SAMPLE_ALLOCATED)
	{
		if(pSampleList[uiSample].pData!=NULL)
		{
			guiSoundMemoryUsed-=pSampleList[uiSample].uiSize;
			AIL_mem_free_lock(pSampleList[uiSample].pData);
		}

		memset(&pSampleList[uiSample], 0, sizeof(SAMPLETAG));
		return(uiSample);
	}
	
	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundGetIndexByID
//
//		Searches out a sound instance referred to by it's ID number.
// 
//	Returns:	If the instance was found, the slot number. NO_SAMPLE otherwise.
//
//*******************************************************************************
UINT32 SoundGetIndexByID(UINT32 uiSoundID)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
	{
		if(pSoundList[uiCount].uiSoundID==uiSoundID)
			return(uiCount);
	}

	return(NO_SAMPLE);
}

//*******************************************************************************
// SoundInitHardware
//
//		Initializes the sound hardware through Windows/DirectX. THe highest possible
//	mixing rate and capabilities set are searched out and used.
// 
//	Returns:	TRUE if the hardware was initialized, FALSE otherwise.
//
//*******************************************************************************
BOOLEAN SoundInitHardware(void)
{
UINT32 uiCount;
CHAR8	cDriverName[128];

	// Try to start up the Miles Sound System
	if(!AIL_startup()) 
		return(FALSE);

	// Initialize the driver handle
	hSoundDriver = NULL;

	// Set up preferences, to try to use DirectSound and to set the 
	// maximum number of handles that we are allowed to allocate. Note
	// that this is not the number we may have playing at one time--
	// that number is set by SOUND_MAX_CHANNELS
	AIL_set_preference(DIG_MIXER_CHANNELS, SOUND_MAX_CHANNELS);


	fDirectSound=TRUE;

	AIL_set_preference(DIG_USE_WAVEOUT,NO);
	// startup with DirectSound
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(44100, 16, 2);
	if (hSoundDriver == NULL)
		hSoundDriver = SoundInitDriver(44100, 8, 2);
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(22050, 8, 2); 
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(11025, 8, 1); 


	if(hSoundDriver)
	{
		// Detect if the driver is emulated or not
		SoundGetDriverName(hSoundDriver, cDriverName);
		_strlwr(cDriverName);
		// If it is, we don't want to use it, since the extra
		// code layer can slow us down by up to 40% under NT
		if(strstr(cDriverName, "emulated"))
		{
			AIL_waveOutClose(hSoundDriver);
			hSoundDriver=NULL;
		}
	}

	// nothing in DirectSound worked, so try waveOut
	if (hSoundDriver == NULL)
	{
		fDirectSound=FALSE;
		AIL_set_preference(DIG_USE_WAVEOUT,YES); 
	}
	
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(44100, 16, 2);
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(44100, 8, 2);
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(22050, 8, 2); 
	if (hSoundDriver == NULL) 
		hSoundDriver = SoundInitDriver(11025, 8, 1); 

	if (hSoundDriver!=NULL)
	{
		for(uiCount = 0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
			memset(&pSoundList[uiCount], 0, sizeof(SOUNDTAG));

		return(TRUE);
	}

	return(FALSE);

/*
	// midi startup
	if (hSoundDriver!=NULL)
	{
		soundMDI = MIDI_init_driver();
		if (soundMDI==NULL)
		{
			_RPT1(_CRT_WARN, "MIDI: %s", AIL_last_error());
		}
		else
		{
			soundSEQ = MIDI_load_sequence(soundMDI, "SOUNDS\\DEMO.XMI");
			if (soundSEQ==NULL)
  			_RPT1(_CRT_WARN, "MIDI: %s", AIL_last_error());
		}
	}
*/
}

//*******************************************************************************
// SoundShutdownHardware
//
//		Shuts down the system hardware.	
// 
//	Returns:	TRUE always.
//
//*******************************************************************************
BOOLEAN SoundShutdownHardware(void)
{
	if(fSoundSystemInit)
		AIL_shutdown();

	return(TRUE);
}

//*******************************************************************************
// SoundInitDriver
//
//		Tries to initialize the sound driver using the specified settings.
// 
//	Returns:	Pointer to the driver if successful, NULL otherwise.
//
//*******************************************************************************
static HDIGDRIVER SoundInitDriver(UINT32 uiRate, UINT16 uiBits, UINT16 uiChans)
{
static PCMWAVEFORMAT	sPCMWF;
HDIGDRIVER						DIG;
CHAR8									cBuf[128];

	memset(&sPCMWF, 0, sizeof(PCMWAVEFORMAT));
  sPCMWF.wf.wFormatTag      = WAVE_FORMAT_PCM;
  sPCMWF.wf.nChannels       = uiChans;
  sPCMWF.wf.nSamplesPerSec  = uiRate;
  sPCMWF.wf.nAvgBytesPerSec = uiRate * (uiBits / 8) * uiChans;
  sPCMWF.wf.nBlockAlign     =        (uiBits / 8) * uiChans;
  sPCMWF.wBitsPerSample     = uiBits;

  if(AIL_waveOutOpen(&DIG, NULL, 0, (LPWAVEFORMAT) &sPCMWF))
		return(NULL);

  memset(cBuf, 0, 128);
  AIL_digital_configuration(DIG,0,0,cBuf);
	FastDebugMsg(String("Sound Init: %dKHz, %d uiBits, %s %s\n", uiRate, uiBits, (uiChans==1)? "Mono": "Stereo", cBuf));

	return(DIG);
}

//*******************************************************************************
// SoundGetDriverName
//
//		Returns the name of the AIL device.
// 
//	Returns:	TRUE or FALSE if the string was filled.
//
//*******************************************************************************
BOOLEAN SoundGetDriverName(HDIGDRIVER DIG, CHAR8 *cBuf)
{
	if(DIG)
	{
		cBuf[0]='\0';
		AIL_digital_configuration(DIG, NULL, NULL, cBuf);
		return(TRUE);
	}
	else
		return(FALSE);
}

//*******************************************************************************
// SoundGetFreeChannel
//
//		Finds an unused sound channel in the channel list.
// 
//	Returns:	Index of a sound channel if one was found, SOUND_ERROR if not.
//
//*******************************************************************************
UINT32 SoundGetFreeChannel(void)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
	{
		if(!SoundIsPlaying(pSoundList[uiCount].uiSoundID))
		{
			SoundStopIndex(uiCount);
		}

		if((pSoundList[uiCount].hMSS==NULL) && (pSoundList[uiCount].hMSSStream==NULL) && (pSoundList[uiCount].hM3D==NULL))
			return(uiCount);
	}

	return(SOUND_ERROR);	
}

//*******************************************************************************
// SoundStartSample
//
//		Starts up a sample on the specified channel. Override parameters are passed
//	in through the structure pointer pParms. Any entry with a value of 0xffffffff
//	will be filled in by the system.
// 
//	Returns:	Unique sound ID if successful, SOUND_ERROR if not.
//
//*******************************************************************************
UINT32 SoundStartSample(UINT32 uiSample, UINT32 uiChannel, SOUNDPARMS *pParms)
{
UINT32 uiSoundID;
CHAR8 AILString[200];

	if(!fSoundSystemInit)
		return(SOUND_ERROR);

	if((pSoundList[uiChannel].hMSS = AIL_allocate_sample_handle(hSoundDriver))==NULL)
	{
		sprintf(AILString, "Sample Error: %s", AIL_last_error());
		FastDebugMsg(AILString);
		return(SOUND_ERROR);
	}

	AIL_init_sample(pSoundList[uiChannel].hMSS);

	if(!AIL_set_named_sample_file(pSoundList[uiChannel].hMSS, pSampleList[uiSample].pName, pSampleList[uiSample].pData, pSampleList[uiSample].uiSize, 0))
	{
		AIL_release_sample_handle(pSoundList[uiChannel].hMSS);
		pSoundList[uiChannel].hMSS=NULL;

		sprintf(AILString, "AIL Set Sample Error: %s", AIL_last_error());
		DbgMessage(TOPIC_GAME, DBG_LEVEL_0, AILString);
		return(SOUND_ERROR);
	}

	// Store the natural playback rate before we modify it below
	pSampleList[uiSample].uiSpeed=AIL_sample_playback_rate(pSoundList[uiChannel].hMSS);

	if(pSampleList[uiSample].uiFlags & SAMPLE_RANDOM)
	{
		if((pSampleList[uiSample].uiSpeedMin != SOUND_PARMS_DEFAULT) && (pSampleList[uiSample].uiSpeedMin != SOUND_PARMS_DEFAULT))
		{
			UINT32 uiSpeed = pSampleList[uiSample].uiSpeedMin+Random(pSampleList[uiSample].uiSpeedMax-pSampleList[uiSample].uiSpeedMin);

			AIL_set_sample_playback_rate(pSoundList[uiChannel].hMSS, uiSpeed);
		}
	}
	else
	{
		if((pParms!=NULL) && (pParms->uiSpeed!=SOUND_PARMS_DEFAULT))
		{
			Assert((pParms->uiSpeed > 0) && (pParms->uiSpeed <= 60000));
			AIL_set_sample_playback_rate(pSoundList[uiChannel].hMSS, pParms->uiSpeed);
		}
	}

	if((pParms!=NULL) && (pParms->uiPitchBend!=SOUND_PARMS_DEFAULT))
	{
		UINT32 uiRate = AIL_sample_playback_rate(pSoundList[uiChannel].hMSS);
		UINT32 uiBend = uiRate * pParms->uiPitchBend/100;
		AIL_set_sample_playback_rate(pSoundList[uiChannel].hMSS, uiRate + (Random(uiBend*2)-uiBend));
	}

	if((pParms!=NULL) && (pParms->uiVolume!=SOUND_PARMS_DEFAULT))
		AIL_set_sample_volume(pSoundList[uiChannel].hMSS, pParms->uiVolume);
	else
		AIL_set_sample_volume(pSoundList[uiChannel].hMSS, guiSoundDefaultVolume);

	if((pParms!=NULL) && (pParms->uiLoop!=SOUND_PARMS_DEFAULT))
	{
		AIL_set_sample_loop_count(pSoundList[uiChannel].hMSS, pParms->uiLoop);

		// If looping infinately, lock the sample so it can't be unloaded
		// and mark it as a looping sound
		if(pParms->uiLoop==0)
		{
			pSampleList[uiSample].uiFlags|=SAMPLE_LOCKED;
			pSoundList[uiChannel].fLooping=TRUE;
		}
	}

	if((pParms!=NULL) && (pParms->uiPan!=SOUND_PARMS_DEFAULT))
		AIL_set_sample_pan(pSoundList[uiChannel].hMSS, pParms->uiPan);

	if((pParms!=NULL) && (pParms->uiPriority!=SOUND_PARMS_DEFAULT))
		pSoundList[uiChannel].uiPriority=pParms->uiPriority;
	else
		pSoundList[uiChannel].uiPriority=PRIORITY_MAX;

	if((pParms!=NULL) && ((UINT32)pParms->EOSCallback!=SOUND_PARMS_DEFAULT))
	{
		pSoundList[uiChannel].EOSCallback=pParms->EOSCallback;
		pSoundList[uiChannel].pCallbackData=pParms->pCallbackData;
	}
	else
	{
		pSoundList[uiChannel].EOSCallback=NULL;
		pSoundList[uiChannel].pCallbackData=NULL;
	}
		
	uiSoundID=SoundGetUniqueID();
	pSoundList[uiChannel].uiSoundID=uiSoundID;
	pSoundList[uiChannel].uiSample=uiSample;
	pSoundList[uiChannel].uiTimeStamp=GetTickCount();
	pSoundList[uiChannel].uiFadeVolume = SoundGetVolumeIndex(uiChannel);
	
	pSampleList[uiSample].uiCacheHits++;
	
	AIL_start_sample(pSoundList[uiChannel].hMSS);

	return(uiSoundID);
}

//*******************************************************************************
// SoundStartStream
//
//		Starts up a stream on the specified channel. Override parameters are passed
//	in through the structure pointer pParms. Any entry with a value of 0xffffffff
//	will be filled in by the system.
// 
//	Returns:	Unique sound ID if successful, SOUND_ERROR if not.
//
//*******************************************************************************
UINT32 SoundStartStream(STR pFilename, UINT32 uiChannel, SOUNDPARMS *pParms)
{
UINT32 uiSoundID, uiSpeed;
CHAR8	AILString[200];

	if(!fSoundSystemInit)
		return(SOUND_ERROR);

	if((pSoundList[uiChannel].hMSSStream=AIL_open_stream(hSoundDriver, pFilename, SOUND_DEFAULT_STREAM))==NULL)
	{
		SoundCleanCache();
		pSoundList[uiChannel].hMSSStream=AIL_open_stream(hSoundDriver, pFilename, SOUND_DEFAULT_STREAM);
	}

	if(pSoundList[uiChannel].hMSSStream==NULL)
	{
		sprintf(AILString, "Stream Error: %s", AIL_last_error());
		DbgMessage(TOPIC_GAME, DBG_LEVEL_0, AILString);
		return(SOUND_ERROR);
	}
	
	if((pParms!=NULL) && (pParms->uiSpeed!=SOUND_PARMS_DEFAULT))
		uiSpeed=pParms->uiSpeed;
	else
		uiSpeed=AIL_stream_playback_rate(pSoundList[uiChannel].hMSSStream);

	if((pParms!=NULL) && (pParms->uiPitchBend!=SOUND_PARMS_DEFAULT))
	{
		UINT32 uiBend = uiSpeed * pParms->uiPitchBend/100;
		uiSpeed+=(Random(uiBend*2)-uiBend);
	}

	AIL_set_stream_playback_rate(pSoundList[uiChannel].hMSSStream, uiSpeed);

	if((pParms!=NULL) && (pParms->uiVolume!=SOUND_PARMS_DEFAULT))
		AIL_set_stream_volume(pSoundList[uiChannel].hMSSStream, pParms->uiVolume);
	else
		AIL_set_stream_volume(pSoundList[uiChannel].hMSSStream, guiSoundDefaultVolume);

	if( pParms!=NULL )
	{
		if (pParms->uiLoop!=SOUND_PARMS_DEFAULT )
			AIL_set_stream_loop_count(pSoundList[uiChannel].hMSSStream, pParms->uiLoop);
	}

	if((pParms!=NULL) && (pParms->uiPan!=SOUND_PARMS_DEFAULT))
		AIL_set_stream_pan(pSoundList[uiChannel].hMSSStream, pParms->uiPan);

	AIL_start_stream(pSoundList[uiChannel].hMSSStream);

	uiSoundID=SoundGetUniqueID();
	pSoundList[uiChannel].uiSoundID=uiSoundID;
	if(pParms)
		pSoundList[uiChannel].uiPriority=pParms->uiPriority;
	else
		pSoundList[uiChannel].uiPriority=SOUND_PARMS_DEFAULT;

	if((pParms!=NULL) && ((UINT32)pParms->EOSCallback!=SOUND_PARMS_DEFAULT))
	{
		pSoundList[uiChannel].EOSCallback=pParms->EOSCallback;
		pSoundList[uiChannel].pCallbackData=pParms->pCallbackData;
	}
	else
	{
		pSoundList[uiChannel].EOSCallback=NULL;
		pSoundList[uiChannel].pCallbackData=NULL;
	}

	pSoundList[uiChannel].uiTimeStamp=GetTickCount();
	pSoundList[uiChannel].uiFadeVolume = SoundGetVolumeIndex(uiChannel);

	return(uiSoundID);
}

//*******************************************************************************
// SoundGetUniqueID
//
//		Returns a unique ID number with every call. Basically it's just a 32-bit
// static value that is incremented each time.
//
//*******************************************************************************
UINT32 SoundGetUniqueID(void)
{
static UINT32 uiNextID=0;

	if(uiNextID==SOUND_ERROR)
		uiNextID++;

	return(uiNextID++);
}

//*******************************************************************************
// SoundPlayStreamed
//
//		Returns TRUE/FALSE whether a sound file should be played as a streamed
//	sample, or loaded into the cache. The decision is based on the size of the
//	file compared to the guiSoundCacheThreshold.
// 
//	Returns:	TRUE if it should be streamed, FALSE if loaded.
//
//*******************************************************************************
BOOLEAN SoundPlayStreamed(STR pFilename)
{
HWFILE hDisk;
UINT32 uiFilesize;

	if((hDisk=FileOpen(pFilename, FILE_ACCESS_READ, FALSE))!=0)
	{
		uiFilesize=FileGetSize(hDisk);
		FileClose(hDisk);
		return(uiFilesize >= guiSoundCacheThreshold);
	}

	return(FALSE);
}

//*******************************************************************************
// SoundStopIndex
//
//		Stops a sound referred to by it's slot number. This function is the only
//	one that should be deallocating sample handles. The random sounds have to have
//	their counters maintained, and using this as the central function ensures
//	that they stay in sync.
// 
//	Returns:	TRUE if the sample was stopped, FALSE if it could not be found.
//
//*******************************************************************************
BOOLEAN SoundStopIndex(UINT32 uiChannel)
{
UINT32 uiSample;	

	if(fSoundSystemInit)
	{
		if(uiChannel!=NO_SAMPLE)
		{
				if(pSoundList[uiChannel].hMSS!=NULL)
				{
					AIL_stop_sample(pSoundList[uiChannel].hMSS);
					AIL_release_sample_handle(pSoundList[uiChannel].hMSS);
					pSoundList[uiChannel].hMSS=NULL;
					uiSample=pSoundList[uiChannel].uiSample;
					
					// if this was a random sample, decrease the iteration count
					if(pSampleList[uiSample].uiFlags&SAMPLE_RANDOM)
						pSampleList[uiSample].uiInstances--;

					if(pSoundList[uiChannel].EOSCallback!=NULL)
						pSoundList[uiChannel].EOSCallback(pSoundList[uiChannel].pCallbackData);

					if(pSoundList[uiChannel].fLooping && !SoundSampleIsInUse(uiChannel))
						SoundRemoveSampleFlags(uiSample, SAMPLE_LOCKED);

					pSoundList[uiChannel].uiSample=NO_SAMPLE;
				}

				if(pSoundList[uiChannel].hMSSStream!=NULL)
				{
					AIL_close_stream(pSoundList[uiChannel].hMSSStream);
					pSoundList[uiChannel].hMSSStream=NULL;
					if(pSoundList[uiChannel].EOSCallback!=NULL)
						pSoundList[uiChannel].EOSCallback(pSoundList[uiChannel].pCallbackData);

					pSoundList[uiChannel].uiSample=NO_SAMPLE;
				}

				if(pSoundList[uiChannel].hM3D!=NULL)
				{
					AIL_stop_3D_sample(pSoundList[uiChannel].hM3D);
					AIL_release_3D_sample_handle(pSoundList[uiChannel].hM3D);
					pSoundList[uiChannel].hM3D=NULL;
					uiSample=pSoundList[uiChannel].uiSample;
					
					// if this was a random sample, decrease the iteration count
					if(pSampleList[uiSample].uiFlags&SAMPLE_RANDOM)
						pSampleList[uiSample].uiInstances--;

					if(pSoundList[uiChannel].EOSCallback!=NULL)
						pSoundList[uiChannel].EOSCallback(pSoundList[uiChannel].pCallbackData);

					if(pSoundList[uiChannel].fLooping && !SoundSampleIsInUse(uiChannel))
						SoundRemoveSampleFlags(uiSample, SAMPLE_LOCKED);

					pSoundList[uiChannel].uiSample=NO_SAMPLE;
				}

				if( pSoundList[uiChannel].hFile != 0 )
				{
					FileClose( pSoundList[uiChannel].hFile );
					pSoundList[uiChannel].hFile = 0;

					pSoundList[uiChannel].uiSample=NO_SAMPLE;
				}
				
				return(TRUE);
		}
	}

	return(FALSE);
}

//*******************************************************************************
// SoundGetDriverHandle
//
//	Returns:	Pointer to the current sound driver
//
//*******************************************************************************
HDIGDRIVER SoundGetDriverHandle(void)
{
	return(hSoundDriver);
}

// FUNCTIONS TO SET / RESET SAMPLE FLAGS
void SoundSetSampleFlags( UINT32 uiSample, UINT32 uiFlags )
{
	// CHECK FOR VALUE SAMPLE
	if((pSampleList[ uiSample ].uiFlags&SAMPLE_ALLOCATED) )
	{
		// SET
		pSampleList[uiSample].uiFlags |= uiFlags;
	}
}

void SoundRemoveSampleFlags( UINT32 uiSample, UINT32 uiFlags )
{
	// CHECK FOR VALID SAMPLE
	if((pSampleList[ uiSample ].uiFlags&SAMPLE_ALLOCATED) )
	{
		//REMOVE
		pSampleList[uiSample].uiFlags &= (~uiFlags);
	}
}

//*******************************************************************************
// SoundSampleIsInUse
//
//	Returns:	TRUE if the sample index is currently being played by the system.
//
//*******************************************************************************
BOOLEAN SoundSampleIsInUse(UINT32 uiSample)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
	{
		if((pSoundList[uiCount].uiSample==uiSample) && SoundIsPlaying(uiCount))
			return(TRUE);
	}

	return(FALSE);
}


//*****************************************************************************************
// SoundFileIsPlaying
// 
// Returns true or false on whether a certain file is currently being played. This function
// will only work on sounds loaded into the cache, it will NOT work on streamed sounds.
// 
// Returns BOOLEAN            - 
// 
// CHAR8 *pFilename           - 
//
// Created:  2/24/00 Derek Beland
//*****************************************************************************************
BOOLEAN SoundFileIsPlaying(CHAR8 *pFilename)
{
UINT32 uiCount;

	for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
	{
		if(SoundIndexIsPlaying(uiCount))
		{
			if(stricmp(pSampleList[pSoundList[uiCount].uiSample].pName, pFilename)==0)
				return(TRUE);
		}
	}

	return(FALSE);
}
//*****************************************************************************************
// SoundSampleSetVolumeRange
// 
// Sets the minimum and maximum volume for a sample.
// 
// Returns nothing. 
// 
// UINT32 uiSample            - Sample handle
// UINT32 uiVolMin            - Minimum volume
// UINT32 uiVolMax            - Maximum volume
//
// Created:  10/29/97 Andrew Emmons
//*****************************************************************************************
void SoundSampleSetVolumeRange(UINT32 uiSample, UINT32 uiVolMin, UINT32 uiVolMax)
{
	Assert((uiSample >= 0) && (uiSample < SOUND_MAX_CACHED));

	pSampleList[uiSample].uiVolMin=uiVolMin;
	pSampleList[uiSample].uiVolMax=uiVolMax;
}


//*****************************************************************************************
// SoundSampleSetPanRange
// 
// Sets the left/right pan values for a sample.
// 
// Returns nothing. 
// 
// UINT32 uiSample            - Sample handle
// UINT32 uiPanMin            - Left setting
// UINT32 uiPanMax            - Right setting
//
// Created:  10/29/97 Andrew Emmons
//*****************************************************************************************
void SoundSampleSetPanRange(UINT32 uiSample, UINT32 uiPanMin, UINT32 uiPanMax)
{
	Assert((uiSample >= 0) && (uiSample < SOUND_MAX_CACHED));

	pSampleList[uiSample].uiPanMin=uiPanMin;
	pSampleList[uiSample].uiPanMax=uiPanMax;
}



//*****************************************************************************************
// SoundSetMusic
// 
// Marks a sample as being music. Cannot be stopped by anything other than SoundStopMusic.
// 
// Returns nothing. 
// 
// UINT32 uiSound             - Sound instance to stop 
//
// Created:  3/16/00 Derek Beland
//*****************************************************************************************
void SoundSetMusic(UINT32 uiSoundID)
{
UINT32 uiSound=SoundGetIndexByID(uiSoundID);

	if(uiSound!=NO_SAMPLE)
		pSoundList[uiSound].fMusic=TRUE;
}


//*****************************************************************************************
// SoundStopMusic
// 
// Stops any sound instance with the music flag.
// 
// Returns nothing. 
// 
// Created:  3/16/00 Derek Beland
//*****************************************************************************************
BOOLEAN SoundStopMusic(void)
{
UINT32 uiCount;
BOOLEAN fStopped=FALSE;

	if(fSoundSystemInit)
	{
		for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
		{
			if((pSoundList[uiCount].hMSS!=NULL) || (pSoundList[uiCount].hMSSStream!=NULL) || (pSoundList[uiCount].hM3D!=NULL))
			{
				if(pSoundList[uiCount].fMusic)
				{
					SoundStop(pSoundList[uiCount].uiSoundID);
					fStopped=TRUE;
				}
			}
		}
	}

	return(fStopped);
}

//*****************************************************************************************
//
//
//
//
// New 3D Sound Code
//
//
//
//
//*****************************************************************************************






//*****************************************************************************************
// Sound3DSetProvider
// 
// Sets the name of the 3D provider to initialize with.
// 
// Returns nothing. 
// 
// CHAR8 *pProviderName       - Pointer to provider name
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetProvider(CHAR8 *pProviderName)
{
	Assert(pProviderName);

	if(pProviderName)
	{
		gpProviderName = (CHAR8 *)MemAlloc(strlen(pProviderName)+1);
		strcpy(gpProviderName, pProviderName);
	}
}


//*****************************************************************************************
// Sound3DInitProvider
// 
// Attempt
// 
// Returns BOOLEAN            - 
// 
// CHAR8 *pProviderName       - 
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
BOOLEAN Sound3DInitProvider(CHAR8 *pProviderName)
{
HPROENUM hEnum=HPROENUM_FIRST;
HPROVIDER hProvider=0;
BOOLEAN fDone=FALSE;
CHAR8 *pName;
INT32 iResult;

	// 3D sound providers depend on the 2D sound system being initialized first
	if(!fSoundSystemInit || !pProviderName)
		return(FALSE);

	// We're already booted up
	if(gh3DProvider)
		return(TRUE);

	while(!fDone)
	{
		if(!AIL_enumerate_3D_providers(&hEnum, &hProvider, &pName))
			fDone=TRUE;
		else if(hProvider)
		{
			if(strcmp(pProviderName, pName)==0)
			{
				fDone=TRUE;
				if(AIL_open_3D_provider(hProvider)==M3D_NOERR)
				{
					gh3DProvider = hProvider;

					// Create a "listener" which represents our position in space
					gh3DListener = AIL_open_3D_listener(gh3DProvider);
					if(!gh3DListener)
					{
						AIL_close_3D_provider(gh3DProvider);
						return(FALSE);
					}
					Sound3DSetListener(0.0f, 0.0f, 0.0f);

			    AIL_3D_provider_attribute(gh3DProvider, "EAX environment selection", &iResult);
					if(iResult!=(-1))
						gfUsingEAX = TRUE;

					return(TRUE);
				}

			}
		}
	}

	// We didn't find a provider with a matching name that would boot up
	return(FALSE);
}


//*****************************************************************************************
// Sound3DShutdownProvider
// 
// Shuts down and deallocates the 3D sound system
// 
// Returns nothing. 
// 
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DShutdownProvider(void)
{
	Sound3DStopAll();

	if(gh3DListener)
	{
		AIL_close_3D_listener(gh3DListener);
		gh3DListener=0;
	}

	if(gh3DProvider)
	{
		AIL_close_3D_provider(gh3DProvider);
		gh3DProvider=0;
	}
}


//*****************************************************************************************
// Sound3DSetPosition
// 
// Sets the 3-space position of a sound sample.
// 
// Returns nothing. 
// 
// UINT32 uiSample            - ID of sample
// FLOAT flX                  - X coordinate
// FLOAT flY                  - Y coordinate
// FLOAT flZ                  - Z coordinate
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetPosition(UINT32 uiSample, FLOAT flX, FLOAT flY, FLOAT flZ)
{
UINT32 uiChannel;

	if(fSoundSystemInit && gh3DProvider)
	{
		if((uiChannel=SoundGetIndexByID(uiSample))!=NO_SAMPLE)
		{
			if(pSoundList[uiChannel].hM3D!=NULL)
			{
				AIL_set_3D_position(pSoundList[uiChannel].hM3D, flX, flY, flZ);
			}
		}
	}
}

//*****************************************************************************************
// Sound3DSetVelocity
// 
// Sets the velocity of a sample. This is important for calculation of doppler effect (pitch
// shifting, think of a train whistle as it passes by you).
// 
// Returns nothing. 
// 
// UINT32 uiSample            - ID of sample
// FLOAT flX                  - X coordinate
// FLOAT flY                  - Y coordinate
// FLOAT flZ                  - Z coordinate
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetVelocity(UINT32 uiSample, FLOAT flX, FLOAT flY, FLOAT flZ)
{
UINT32 uiChannel;

	if(fSoundSystemInit && gh3DProvider)
	{
		if((uiChannel=SoundGetIndexByID(uiSample))!=NO_SAMPLE)
		{
			if(pSoundList[uiChannel].hM3D!=NULL)
			{
			}
		}
	}
}


//*****************************************************************************************
// Sound3DSetListener
// 
// Sets the listener location. This should be set to the current camera location.
// 
// Returns nothing. 
// 
// FLOAT flX                  - X coordinate
// FLOAT flY                  - Y coordinate
// FLOAT flZ                  - Z coordinate
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetListener(FLOAT flX, FLOAT flY, FLOAT flZ)
{
	if(fSoundSystemInit && gh3DListener)
		AIL_set_3D_position(gh3DListener, flX, flY, flZ);
}


//*****************************************************************************************
// Sound3DSetFacing
// 
// Sets the orientation of the listener. The inputs are two vectors that are *always* at
// right angles to each other. The first is the facing vector, and the second is the up
// vector, which points out of the top of the listeners head.
// 
// Returns nothing. 
// 
// FLOAT flXFace              - X coordinate facing
// FLOAT flYFace              - Y coordinate facing
// FLOAT flZFace              - Z coordinate facing
// FLOAT flXUp                - X coordinate up
// FLOAT flYUp                - Y coordinate up
// FLOAT flZUp                - Z coordinate up
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetFacing(FLOAT flXFace, FLOAT flYFace, FLOAT flZFace, FLOAT flXUp, FLOAT flYUp, FLOAT flZUp)
{
	if(fSoundSystemInit && gh3DListener)
		AIL_set_3D_orientation(gh3DListener, flXFace, flYFace, flZFace, flXUp, flYUp, flZUp);
}

//*****************************************************************************************
// Sound3DSetDirection
// 
// Sets the orientation of the listener. The inputs are two vectors that are *always* at
// right angles to each other. The first is the facing vector, and the second is the up
// vector, which points out of the top of the listeners head.
// 
// Returns nothing. 
// 
// FLOAT flXFace              - X coordinate facing
// FLOAT flYFace              - Y coordinate facing
// FLOAT flZFace              - Z coordinate facing
// FLOAT flXUp                - X coordinate up
// FLOAT flYUp                - Y coordinate up
// FLOAT flZUp                - Z coordinate up
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetDirection(UINT32 uiSample, FLOAT flXFace, FLOAT flYFace, FLOAT flZFace, FLOAT flXUp, FLOAT flYUp, FLOAT flZUp)
{
UINT32 uiChannel;

	if(fSoundSystemInit && gh3DProvider)
	{
		if((uiChannel=SoundGetIndexByID(uiSample))!=NO_SAMPLE)
		{
			if(pSoundList[uiChannel].hM3D!=NULL)
			{
				AIL_set_3D_orientation(pSoundList[uiChannel].hM3D, flXFace, flYFace, flZFace, flXUp, flYUp, flZUp);
			}
		}
	}
}


//*****************************************************************************************
// Sound3DSetFalloff
// 
// Sets the falloff of the sound which determines the maximum radius at which the sample
// produces any sound, and the minimum distance before the sound volume begins to fall off
// towards zero.
// 
// Returns nothing. 
// 
// UINT32 uiSample            - Sample index
// FLOAT flMax                - Point at which the sound volume is zero
// FLOAT flMin                - Point at which the sound volume begins to fall off
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetFalloff(UINT32 uiSample, FLOAT flMax, FLOAT flMin)
{
UINT32 uiChannel;
// max = far
// min = near

	if(fSoundSystemInit && gh3DProvider)
	{
		if((uiChannel=SoundGetIndexByID(uiSample))!=NO_SAMPLE)
		{
			if(pSoundList[uiChannel].hM3D!=NULL)
			{
				AIL_set_3D_sample_distances(pSoundList[uiChannel].hM3D, flMax, flMin);
			}
		}
	}
}


//*****************************************************************************************
// Sound3DActiveSounds
// 
// Returns the number of active sounds.
// 
// Returns INT32              - Number of 3D sounds playing
// 
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
INT32 Sound3DActiveSounds(void)
{
	return((INT32)AIL_active_3D_sample_count(gh3DProvider));
}


//*****************************************************************************************
// Sound3DSetEnvironment
// 
// Sets the current environment type for the listener. This determines which atmospheric
// effects are applied to the 3D sounds. Eg. Caves, underwater, etc.
// 
// Returns nothing. 
// 
// INT32 iEnvironment         - Index of environment type
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DSetEnvironment(INT32 iEnvironment)
{
	if(fSoundSystemInit && gh3DProvider)
	{
	}
}


//*****************************************************************************************
// Sound3DPlay
// 
// Starts a 3D sample playing.
// 
// Returns UINT32             - Sound index
// 
// STR pFilename              - Pointer to filename of sound
// SOUNDPARMS *pParms         - Parameter struct (or NULL for defaults)
//
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
UINT32 Sound3DPlay(STR pFilename, SOUND3DPARMS *pParms)
{
	UINT32 uiSample, uiChannel;

	if(fSoundSystemInit && gh3DProvider)
	{
		if((uiSample=SoundLoadSample(pFilename))!=NO_SAMPLE)
		{
			if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
			{
				return(Sound3DStartSample(uiSample, uiChannel, pParms));
			}
		}
		else
		{
			FastDebugMsg(String("Sound3DPlay: ERROR: Failed loading sample %s\n", pFilename ) );
		}
	}

	return(SOUND_ERROR);
}

//*******************************************************************************
// Sound3DStartSample
//
//		Starts up a sample on the specified channel. Override parameters are passed
//	in through the structure pointer pParms. Any entry with a value of 0xffffffff
//	will be filled in by the system.
// 
//	Returns:	Unique sound ID if successful, SOUND_ERROR if not.
//
//*******************************************************************************
UINT32 Sound3DStartSample(UINT32 uiSample, UINT32 uiChannel, SOUND3DPARMS *pParms)
{
UINT32 uiSoundID;
CHAR8 AILString[200];

	if(!fSoundSystemInit || !gh3DProvider)
		return(SOUND_ERROR);

	// Stereo samples have no purpose in 3D, nor MP3 files
	if(pSampleList[uiSample].fStereo || strstr(pSampleList[uiSample].pName, ".MP3"))
		return(SOUND_ERROR);
		
	if((pSoundList[uiChannel].hM3D = AIL_allocate_3D_sample_handle(gh3DProvider))==NULL)
	{
		sprintf(AILString, "AIL3D Error: %s", AIL_last_error());
		DbgMessage(TOPIC_GAME, DBG_LEVEL_0, AILString);
		return(SOUND_ERROR);
	}

	if(!AIL_set_3D_sample_file(pSoundList[uiChannel].hM3D, pSampleList[uiSample].pData))
	{
		AIL_release_3D_sample_handle(pSoundList[uiChannel].hM3D);
		pSoundList[uiChannel].hM3D=NULL;

		sprintf(AILString, "AIL3D Set Sample Error: %s", AIL_last_error());
		DbgMessage(TOPIC_GAME, DBG_LEVEL_0, AILString);
		return(SOUND_ERROR);
	}

	// Store the natural playback rate before we modify it below
	pSampleList[uiSample].uiSpeed=AIL_3D_sample_playback_rate(pSoundList[uiChannel].hM3D);

	if(pSampleList[uiSample].uiFlags & SAMPLE_RANDOM)
	{
		if((pSampleList[uiSample].uiSpeedMin != SOUND_PARMS_DEFAULT) && (pSampleList[uiSample].uiSpeedMax != SOUND_PARMS_DEFAULT))
		{
			UINT32 uiSpeed = pSampleList[uiSample].uiSpeedMin+Random(pSampleList[uiSample].uiSpeedMax-pSampleList[uiSample].uiSpeedMin);

			AIL_set_3D_sample_playback_rate(pSoundList[uiChannel].hM3D, uiSpeed);
		}
	}
	else
	{
		if((pParms!=NULL) && (pParms->uiSpeed!=SOUND_PARMS_DEFAULT))
			AIL_set_3D_sample_playback_rate(pSoundList[uiChannel].hM3D, pParms->uiSpeed);
	}

	if((pParms!=NULL) && (pParms->uiPitchBend!=SOUND_PARMS_DEFAULT))
	{
		UINT32 uiRate = AIL_3D_sample_playback_rate(pSoundList[uiChannel].hM3D);
		UINT32 uiBend = uiRate * pParms->uiPitchBend/100;
		AIL_set_3D_sample_playback_rate(pSoundList[uiChannel].hM3D,	uiRate + (Random(uiBend*2)-uiBend));
	}

	if((pParms!=NULL) && (pParms->uiVolume!=SOUND_PARMS_DEFAULT))
		AIL_set_3D_sample_volume(pSoundList[uiChannel].hM3D, pParms->uiVolume);
	else
		AIL_set_3D_sample_volume(pSoundList[uiChannel].hM3D, guiSoundDefaultVolume);

	if((pParms!=NULL) && (pParms->uiLoop!=SOUND_PARMS_DEFAULT))
	{
		AIL_set_3D_sample_loop_count(pSoundList[uiChannel].hM3D, pParms->uiLoop);

		// If looping infinately, lock the sample so it can't be unloaded
		// and mark it as a looping sound
		if(pParms->uiLoop==0)
		{
			pSampleList[uiSample].uiFlags|=SAMPLE_LOCKED;
			pSoundList[uiChannel].fLooping=TRUE;
		}
	}

	if((pParms!=NULL) && (pParms->uiPriority!=SOUND_PARMS_DEFAULT))
		pSoundList[uiChannel].uiPriority=pParms->uiPriority;
	else
		pSoundList[uiChannel].uiPriority=PRIORITY_MAX;

	if((pParms!=NULL) && ((UINT32)pParms->EOSCallback!=SOUND_PARMS_DEFAULT))
	{
		pSoundList[uiChannel].EOSCallback=pParms->EOSCallback;
		pSoundList[uiChannel].pCallbackData=pParms->pCallbackData;
	}
	else
	{
		pSoundList[uiChannel].EOSCallback=NULL;
		pSoundList[uiChannel].pCallbackData=NULL;
	}
		

	AIL_set_3D_position(pSoundList[uiChannel].hM3D, pParms->Pos.flX, pParms->Pos.flY, pParms->Pos.flZ);
	AIL_set_3D_velocity_vector(pSoundList[uiChannel].hM3D, pParms->Pos.flVelX, pParms->Pos.flVelY, pParms->Pos.flVelZ);
	AIL_set_3D_orientation(pSoundList[uiChannel].hM3D, pParms->Pos.flFaceX, pParms->Pos.flFaceY, pParms->Pos.flFaceZ, pParms->Pos.flUpX, pParms->Pos.flUpY, pParms->Pos.flUpZ);
//	AIL_set_3D_sample_distances(pSoundList[uiChannel].hM3D, pParms->Pos.flFalloffMax, pParms->Pos.flFalloffMin);
	AIL_set_3D_sample_distances(pSoundList[uiChannel].hM3D, 99999999.9f, 99999999.9f);

	uiSoundID=SoundGetUniqueID();
	pSoundList[uiChannel].uiSoundID=uiSoundID;
	pSoundList[uiChannel].uiSample=uiSample;
	pSoundList[uiChannel].uiTimeStamp=GetTickCount();

	pSampleList[uiSample].uiCacheHits++;
	
	AIL_start_3D_sample(pSoundList[uiChannel].hM3D);

	return(uiSoundID);
}


//*****************************************************************************************
// Sound3DStopAll
// 
// Stops all currently playing 3D samples.
// 
// Returns nothing. 
// 
// Created:  8/17/99 Derek Beland
//*****************************************************************************************
void Sound3DStopAll(void)
{
UINT32 uiChannel;

	// Stop all currently playing random sounds
	for(uiChannel=0; uiChannel < SOUND_MAX_CHANNELS; uiChannel++)
	{
		if(pSoundList[uiChannel].hM3D!=NULL)
			SoundStopIndex(uiChannel);
	}
}

//*******************************************************************************
// Sound3DStartRandom
//
//	Starts an instance of a random sample.
// 
//	Returns:	TRUE if a new random sound was created, FALSE if nothing was done.
//
//*******************************************************************************
UINT32 Sound3DStartRandom(UINT32 uiSample, SOUND3DPOS *pPos)
{
UINT32 uiChannel, uiSoundID;
SOUND3DPARMS sp3DParms;

	if(pPos && ((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR))
	{
		memset(&sp3DParms, 0xff, sizeof(SOUND3DPARMS));

//		sp3DParms.uiSpeed=pSampleList[uiSample].uiSpeedMin+Random(pSampleList[uiSample].uiSpeedMax-pSampleList[uiSample].uiSpeedMin);
		sp3DParms.uiLoop=1;
		sp3DParms.uiPriority=pSampleList[uiSample].uiPriority;

//		memcpy(&sp3DParms.Pos, pPos, sizeof(SOUND3DPOS));

		sp3DParms.Pos.flX=pPos->flX;
		sp3DParms.Pos.flY=pPos->flY;
		sp3DParms.Pos.flZ=pPos->flZ;

		sp3DParms.Pos.flVelX=pPos->flVelX;
		sp3DParms.Pos.flVelY=pPos->flVelY;
		sp3DParms.Pos.flVelZ=pPos->flVelZ;

		sp3DParms.Pos.flFaceX=pPos->flFaceX;
		sp3DParms.Pos.flFaceY=pPos->flFaceY;
		sp3DParms.Pos.flFaceZ=pPos->flFaceZ;

		sp3DParms.Pos.flUpX=pPos->flUpX;
		sp3DParms.Pos.flUpY=pPos->flUpY;
		sp3DParms.Pos.flUpZ=pPos->flUpZ;

		sp3DParms.Pos.flFalloffMax=pPos->flFalloffMax;
		sp3DParms.Pos.flFalloffMin=pPos->flFalloffMin;

		sp3DParms.Pos.uiVolume=pPos->uiVolume;
		sp3DParms.uiVolume=pPos->uiVolume;

		if((uiSoundID=Sound3DStartSample(uiSample, uiChannel, &sp3DParms))!=SOUND_ERROR)
		{
			pSampleList[uiSample].uiTimeNext=GetTickCount()+pSampleList[uiSample].uiTimeMin+Random(pSampleList[uiSample].uiTimeMax-pSampleList[uiSample].uiTimeMin);
			pSampleList[uiSample].uiInstances++;
			return(uiSoundID);
		}
	}

	return(NO_SAMPLE);
}


//*****************************************************************************************
// Sound3DSetRoomType
// 
// Sets the environment presets (reverb, chorus, etc) for 3D sounds. Currently only 
// has effect for EAX cards.
// 
// Returns nothing. 
// 
// UINT32 uiRoomType          - Index of room type (see Soundman.h e_EAXRoomTypes)
//
// Created:  8/23/99 Derek Beland
//*****************************************************************************************
void Sound3DSetRoomType(UINT32 uiRoomType)
{
	if(gh3DProvider && gfUsingEAX && (guiRoomTypeIndex!=uiRoomType))
	{
		CHAR8 cName[128];
		
		sprintf(cName, "EAX_ENVIRONMENT_%s", pEAXRoomTypes[uiRoomType]);

		AIL_set_3D_provider_preference(gh3DProvider, cName, (void *)(&uiRoomType));
		guiRoomTypeIndex = uiRoomType;
	}
}


//*****************************************************************************************
// Sound3DChannelsUsed
// 
// 
// 
// Returns UINT32             - 
// 
// Created:  5/26/00 Derek Beland
//*****************************************************************************************
UINT32 Sound3DChannelsInUse(void)
{
UINT32 uiChannel, uiUsed=0;

	// Stop all currently playing random sounds
	for(uiChannel=0; uiChannel < SOUND_MAX_CHANNELS; uiChannel++)
	{
		if(pSoundList[uiChannel].hM3D!=NULL)
			uiUsed++;
	}

	return(uiUsed);
}


//*****************************************************************************************
// SoundStreamsInUse
// 
// 
// 
// Returns UINT32             - 
// 
// Created:  5/26/00 Derek Beland
//*****************************************************************************************
UINT32 SoundStreamsInUse(void)
{
UINT32 uiChannel, uiUsed=0;

	// Stop all currently playing random sounds
	for(uiChannel=0; uiChannel < SOUND_MAX_CHANNELS; uiChannel++)
	{
		if(pSoundList[uiChannel].hMSSStream!=NULL)
			uiUsed++;
	}

	return(uiUsed);
}


//*****************************************************************************************
// Sound2DChannelsInUse
// 
// 
// 
// Returns UINT32             - 
// 
// Created:  5/26/00 Derek Beland
//*****************************************************************************************
UINT32 Sound2DChannelsInUse(void)
{
UINT32 uiChannel, uiUsed=0;

	// Stop all currently playing random sounds
	for(uiChannel=0; uiChannel < SOUND_MAX_CHANNELS; uiChannel++)
	{
		if(pSoundList[uiChannel].hMSS!=NULL)
			uiUsed++;
	}

	return(uiUsed);
}

//*****************************************************************************************
// SoundChannelsInUse
// 
// 
// 
// Returns UINT32             - 
// 
// Created:  5/26/00 Derek Beland
//*****************************************************************************************
UINT32 SoundTotalChannelsInUse(void)
{
UINT32 uiChannel, uiUsed=0;

	// Stop all currently playing random sounds
	for(uiChannel=0; uiChannel < SOUND_MAX_CHANNELS; uiChannel++)
	{
		if(SoundIndexIsPlaying(uiChannel))
			uiUsed++;
	}

	return(uiUsed);
}
