mirror of
https://github.com/1dot13/source.git
synced 2026-08-12 14:10:23 +02:00
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@21 3b4a5df2-a311-0410-b5c6-a8a6f20db521
3248 lines
96 KiB
C++
3248 lines
96 KiB
C++
/*********************************************************************************
|
|
* 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"
|
|
#include "Debug Control.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"
|
|
#include "Debug Control.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((long)(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;
|
|
// DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundServiceRandom ");
|
|
for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
|
|
{
|
|
if(!(pSampleList[uiCount].uiFlags&SAMPLE_RANDOM_MANUAL) && SoundRandomShouldPlay(uiCount))
|
|
SoundStartRandom(uiCount);
|
|
// DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("SoundStartRandom uicount = %d",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)
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartRandom ");
|
|
UINT32 uiChannel, uiSoundID;
|
|
SOUNDPARMS spParms;
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartRandom: SoundGetFreeChannel ");
|
|
if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartRandom: memset ");
|
|
memset(&spParms, 0xff, sizeof(SOUNDPARMS));
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartRandom: set sample info");
|
|
// 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;
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartRandom: SoundStartSample");
|
|
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++;
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("SoundStartRandom: returing sound %d ", uiSoundID));
|
|
return(uiSoundID);
|
|
}
|
|
}
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartRandom: sound error ");
|
|
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, (long *)puiTotalMilliseconds, (long *)puiCurrentMilliseconds);
|
|
return TRUE;
|
|
}
|
|
|
|
if(pSoundList[uiSound].hMSSStream!=NULL)
|
|
{
|
|
AIL_stream_ms_position(pSoundList[uiSound].hMSSStream, (long *)puiTotalMilliseconds, (long *)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)
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample");
|
|
|
|
UINT32 uiSoundID;
|
|
CHAR8 AILString[200];
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check sound init");
|
|
if(!fSoundSystemInit)
|
|
return(SOUND_ERROR);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check sound driver");
|
|
if((pSoundList[uiChannel].hMSS = AIL_allocate_sample_handle(hSoundDriver))==NULL)
|
|
{
|
|
sprintf(AILString, "Sample Error: %s", AIL_last_error());
|
|
FastDebugMsg(AILString);
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: AL_init_sample ");
|
|
AIL_init_sample(pSoundList[uiChannel].hMSS);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check sample");
|
|
if(!AIL_set_named_sample_file(pSoundList[uiChannel].hMSS, pSampleList[uiSample].pName, pSampleList[uiSample].pData, pSampleList[uiSample].uiSize, 0))
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: sample sound error");
|
|
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);
|
|
}
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: store playback rate");
|
|
// Store the natural playback rate before we modify it below
|
|
pSampleList[uiSample].uiSpeed=AIL_sample_playback_rate(pSoundList[uiChannel].hMSS);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check if random sample");
|
|
if(pSampleList[uiSample].uiFlags & SAMPLE_RANDOM)
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: random sample");
|
|
if((pSampleList[uiSample].uiSpeedMin != SOUND_PARMS_DEFAULT) && (pSampleList[uiSample].uiSpeedMin != SOUND_PARMS_DEFAULT))
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: get random sample");
|
|
|
|
UINT32 uiSpeed = pSampleList[uiSample].uiSpeedMin+Random(pSampleList[uiSample].uiSpeedMax-pSampleList[uiSample].uiSpeedMin);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: set playback rate");
|
|
AIL_set_sample_playback_rate(pSoundList[uiChannel].hMSS, uiSpeed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: not random sample");
|
|
if((pParms!=NULL) && (pParms->uiSpeed!=SOUND_PARMS_DEFAULT))
|
|
{
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check speed ");
|
|
Assert((pParms->uiSpeed > 0) && (pParms->uiSpeed <= 60000));
|
|
AIL_set_sample_playback_rate(pSoundList[uiChannel].hMSS, pParms->uiSpeed);
|
|
}
|
|
}
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check pitch bend");
|
|
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));
|
|
}
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check volume");
|
|
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);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check looping");
|
|
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;
|
|
}
|
|
}
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check pan");
|
|
if((pParms!=NULL) && (pParms->uiPan!=SOUND_PARMS_DEFAULT))
|
|
AIL_set_sample_pan(pSoundList[uiChannel].hMSS, pParms->uiPan);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check priority");
|
|
if((pParms!=NULL) && (pParms->uiPriority!=SOUND_PARMS_DEFAULT))
|
|
pSoundList[uiChannel].uiPriority=pParms->uiPriority;
|
|
else
|
|
pSoundList[uiChannel].uiPriority=PRIORITY_MAX;
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: check EOS callback");
|
|
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;
|
|
}
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: set up channel in sound list");
|
|
uiSoundID=SoundGetUniqueID();
|
|
pSoundList[uiChannel].uiSoundID=uiSoundID;
|
|
pSoundList[uiChannel].uiSample=uiSample;
|
|
pSoundList[uiChannel].uiTimeStamp=GetTickCount();
|
|
pSoundList[uiChannel].uiFadeVolume = SoundGetVolumeIndex(uiChannel);
|
|
|
|
pSampleList[uiSample].uiCacheHits++;
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: start sample");
|
|
AIL_start_sample(pSoundList[uiChannel].hMSS);
|
|
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"SoundStartSample: done");
|
|
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);
|
|
|
|
// Snap: First see if the file is in the custom Data catalogue:
|
|
std::string filePath;
|
|
if ( gCustomDataCat.FindFile(pFilename) ) {
|
|
filePath = gCustomDataCat.GetRootDir() + '\\';
|
|
}
|
|
filePath += pFilename;
|
|
// Bad cast! pFilename should have been const. Oh well...
|
|
pFilename = const_cast<STR>( filePath.c_str() );
|
|
// Now pFilename points either to the original file name,
|
|
// or to the full file path in the custom Data directory.
|
|
// Except for this substitution, the rest of the function is unchanged.
|
|
|
|
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);
|
|
}
|