mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
1871 lines
54 KiB
C++
1871 lines
54 KiB
C++
/*********************************************************************************
|
|
* SGP Digital Sound Module
|
|
*
|
|
* This module handles the playing of digital samples, preloaded or streamed.
|
|
*
|
|
* Derek Beland, May 28, 1997
|
|
*
|
|
*********************************************************************************/
|
|
#include "builddefines.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "soundman.h"
|
|
#include "FileMan.h"
|
|
#include "debug.h"
|
|
#include "MemMan.h"
|
|
#include "random.h"
|
|
#include "fmod.h"
|
|
#include "fmod_errors.h"
|
|
#include "sgp_logger.h"
|
|
// sevenfm
|
|
#include "message.h"
|
|
#include "Sound Control.h"
|
|
//#include "english.h"
|
|
//#include "input.h"
|
|
#include <ctime>
|
|
#include <chrono>
|
|
|
|
// Uncomment this to disable the startup of sound hardware
|
|
//#define SOUND_DISABLE
|
|
|
|
// global settings
|
|
#define SOUND_MAX_CACHED 128 // number of cache slots
|
|
|
|
// sevenfm: increased number of channels
|
|
#define SOUND_MAX_CHANNELS 128
|
|
|
|
// default memory limit
|
|
#define SOUND_DEFAULT_MEMORY (8048*1024)
|
|
|
|
// size for sample to be double-buffered
|
|
#define SOUND_DEFAULT_THRESH (256*8024)
|
|
|
|
// playing/random value to indicate default
|
|
#define SOUND_PARMS_DEFAULT 0xffffffff
|
|
|
|
// Sound status flags
|
|
#define SOUND_CALLBACK 0x00000008
|
|
|
|
// Max volume
|
|
#define MAX_VOLUME (127)
|
|
|
|
// Initialization parameters
|
|
#define AUDIO_BUFFER_LEN 100
|
|
#define STREAM_BUFFER_LEN 100
|
|
|
|
// Lesh modifications
|
|
// Sound debug
|
|
|
|
CHAR8 SndDebugFileName[]="sound.log";
|
|
FILE *SndDebug;
|
|
|
|
// Debug logging
|
|
void SoundLog(CHAR8 *strMessage);
|
|
|
|
// Cache system
|
|
BOOLEAN SoundInitCache(void);
|
|
BOOLEAN SoundShutdownCache(void);
|
|
UINT32 SoundLoadSample(STR pFilename);
|
|
UINT32 SoundFreeSample(STR pFilename);
|
|
UINT32 SoundGetCached(STR pFilename);
|
|
UINT32 SoundLoadDisk(STR pFilename);
|
|
BOOLEAN SoundCleanCache(void);
|
|
UINT32 SoundFreeSampleIndex(UINT32 uiSample);
|
|
|
|
// Low level
|
|
// Init, de-init
|
|
BOOLEAN SoundInitHardware(void);
|
|
BOOLEAN SoundShutdownHardware(void);
|
|
|
|
// Playing
|
|
UINT32 SoundStartSample(UINT32 uiSample, UINT32 uiChannel, SOUNDPARMS *pParms);
|
|
UINT32 SoundStartStream(STR pFilename, UINT32 uiChannel, SOUNDPARMS *pParms);
|
|
BOOLEAN SoundPlayStreamed(STR pFilename);
|
|
UINT32 SoundStartRandom(UINT32 uiSample);
|
|
BOOLEAN SoundRandomShouldPlay(UINT32 uiSample);
|
|
|
|
// Stopping
|
|
BOOLEAN SoundStopIndex(UINT32 uiSound);
|
|
|
|
// Checks
|
|
BOOLEAN SoundSampleIsPlaying(UINT32 uiSample);
|
|
BOOLEAN SoundIndexIsPlaying(UINT32 uiSound);
|
|
BOOLEAN SoundSampleIsInUse(UINT32 uiSample);
|
|
|
|
// Volume operations
|
|
UINT32 SoundGetVolumeIndex(UINT32 uiChannel);
|
|
BOOLEAN SoundSetVolumeIndex(UINT32 uiChannel, UINT32 uiVolume);
|
|
|
|
// Cleaning
|
|
UINT32 SoundGetEmptySample(void);
|
|
|
|
// Index, ID, other...
|
|
UINT32 SoundGetIndexByID(UINT32 uiSoundID);
|
|
UINT32 SoundGetFreeChannel(void);
|
|
UINT32 SoundGetUniqueID(void);
|
|
|
|
// Callbacks
|
|
void * F_CALLBACKAPI SoundFileOpen (const STR8 pName);
|
|
void F_CALLBACKAPI SoundFileClose(void *uiHandle);
|
|
INT F_CALLBACKAPI SoundFileRead (void *pBuffer, INT iSize, void *uiHandle);
|
|
INT F_CALLBACKAPI SoundFileSeek (void *uiHandle, INT iPos, signed char cMode);
|
|
INT F_CALLBACKAPI SoundFileTell (void *uiHandle);
|
|
|
|
// 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
|
|
|
|
BOOLEAN hSoundDriver; // Sound driver ready ?
|
|
BOOLEAN fDirectSound=TRUE; // Using Direct Sound
|
|
|
|
// Local module variables
|
|
BOOLEAN fSoundSystemInit=FALSE; // Startup called T/F
|
|
BOOLEAN gfEnableStartup=TRUE; // Allow hardware to starup
|
|
|
|
// Struct definition for sample slots in the cache
|
|
// Holds the regular sample data, as well as the
|
|
// data for the random samples
|
|
|
|
typedef struct {
|
|
CHAR8 pName[128]; // Sample path
|
|
UINT32 uiSize; // Sample size
|
|
UINT32 uiFlags; // Status flags
|
|
PTR pData; // Pointer to loaded sample
|
|
UINT32 uiCacheHits; // Cache hits for this sample
|
|
|
|
UINT32 uiTimeNext; // Random sound data
|
|
UINT32 uiTimeMin, uiTimeMax;
|
|
UINT32 uiVolMin, uiVolMax;
|
|
UINT32 uiPanMin, uiPanMax;
|
|
UINT32 uiPriority; // Priority
|
|
UINT32 uiInstances; // Instances of sample sounding at this moment
|
|
UINT32 uiMaxInstances; // Max allowable instances of sample
|
|
} SAMPLETAG;
|
|
|
|
// Structure definition for slots in the sound output
|
|
// These are used for both the cached and double-buffered
|
|
// streams
|
|
typedef struct {
|
|
UINT32 uiSample; // Sample slot in cache
|
|
FSOUND_STREAM* hStream; // Stream fmod handler
|
|
UINT32 uiFMODChannel; // Fmod channel
|
|
UINT32 uiFlags; // Sample flags
|
|
UINT32 uiSoundID; // Sound unique ID
|
|
UINT32 uiPriority; // Priority
|
|
void (*EOSCallback)(void *);
|
|
void *pCallbackData;
|
|
UINT32 uiTimeStamp; // Time stamp
|
|
BOOLEAN fLooping; // Loop flag
|
|
BOOLEAN fStopAtZero; // Stop at zero volume
|
|
UINT32 uiFadeVolume;
|
|
UINT32 uiFadeRate;
|
|
UINT32 uiFadeTime;
|
|
} SOUNDTAG;
|
|
|
|
// Sample cache list for files loaded
|
|
SAMPLETAG pSampleList[SOUND_MAX_CACHED];
|
|
// Sound channel list for output channels
|
|
SOUNDTAG pSoundList[SOUND_MAX_CHANNELS];
|
|
|
|
//*******************************************************************************
|
|
// High Level Interface
|
|
//*******************************************************************************
|
|
|
|
//*******************************************************************************
|
|
// SoundEnableSound
|
|
//
|
|
// Allows or disallows the startup of the sound hardware.
|
|
//
|
|
// Returns: Nothing.
|
|
//
|
|
//*******************************************************************************
|
|
void SoundEnableSound(BOOLEAN fEnable)
|
|
{
|
|
gfEnableStartup=fEnable;
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// SoundGetDriverHandle
|
|
//
|
|
// Returns: Pointer to the system level output device module.
|
|
//
|
|
//*******************************************************************************
|
|
void *SoundGetDriverHandle( void )
|
|
{
|
|
if(fSoundSystemInit)
|
|
return(FSOUND_GetOutputHandle());
|
|
else
|
|
return(NULL);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// InitializeSoundManager
|
|
//
|
|
// Zeros out the structs for the system info, and initializes the cache.
|
|
//
|
|
// Returns: TRUE always
|
|
//
|
|
//*******************************************************************************
|
|
BOOLEAN InitializeSoundManager(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
|
|
if(fSoundSystemInit)
|
|
{
|
|
SoundLog("Reopening JA2 sound manager");
|
|
ShutdownSoundManager();
|
|
}
|
|
else
|
|
SoundLog("Initialising JA2 sound manager");
|
|
|
|
SoundLog((CHAR8 *)String(" Using %d channels", SOUND_MAX_CHANNELS));
|
|
|
|
for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
|
|
{
|
|
memset(&pSoundList[uiCount], 0, sizeof(SOUNDTAG));
|
|
}
|
|
|
|
// Setup callbacks
|
|
FSOUND_File_SetCallbacks(SoundFileOpen, SoundFileClose, SoundFileRead, SoundFileSeek, SoundFileTell);
|
|
|
|
#ifndef SOUND_DISABLE
|
|
if(gfEnableStartup && SoundInitHardware())
|
|
fSoundSystemInit=TRUE;
|
|
#endif
|
|
|
|
SoundInitCache();
|
|
|
|
SoundLog((CHAR8 *)String(" Sound memory limit = %i", SOUND_DEFAULT_MEMORY));
|
|
SoundLog((CHAR8 *)String(" Cache threshold = %i", SOUND_DEFAULT_THRESH));
|
|
|
|
guiSoundMemoryLimit=SOUND_DEFAULT_MEMORY;
|
|
guiSoundMemoryUsed=0;
|
|
guiSoundCacheThreshold=SOUND_DEFAULT_THRESH;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// ShutdownSoundManager
|
|
//
|
|
// Silences all currently playing sound, deallocates any memory allocated,
|
|
// and releases the sound hardware.
|
|
//
|
|
//*******************************************************************************
|
|
void ShutdownSoundManager(void)
|
|
{
|
|
SoundLog("Closing sound system...");
|
|
|
|
SoundStopAll();
|
|
SoundShutdownCache();
|
|
SoundShutdownHardware();
|
|
fSoundSystemInit=FALSE;
|
|
SoundLog("JA2 sound manager shutdown");
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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
|
|
//
|
|
//*******************************************************************************
|
|
|
|
std::map<std::string, uint64_t, std::less<>> gSoundMap;
|
|
|
|
uint64_t TimeMS()
|
|
{
|
|
using namespace std::chrono;
|
|
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
|
}
|
|
|
|
UINT32 SoundPlay(STR pFilename, SOUNDPARMS *pParms)
|
|
{
|
|
UINT32 uiSample, uiChannel;
|
|
|
|
if (fSoundSystemInit)
|
|
{
|
|
if (!SoundPlayStreamed(pFilename))
|
|
{
|
|
// sevenfm: limit simultaneous sound playing
|
|
if (gGameExternalOptions.fLimitSimultaneousSound)
|
|
//!_KeyDown(SHIFT))
|
|
{
|
|
uint64_t curtime = TimeMS();
|
|
std::string filename(pFilename);
|
|
|
|
if (gSoundMap[filename] > curtime)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// set delay for this sound type
|
|
gSoundMap[filename] = curtime + 50;
|
|
}
|
|
|
|
if ((uiSample = SoundLoadSample(pFilename)) != NO_SAMPLE)
|
|
{
|
|
if ((uiChannel = SoundGetFreeChannel()) != SOUND_ERROR)
|
|
{
|
|
return(SoundStartSample(uiSample, uiChannel, pParms));
|
|
}
|
|
else
|
|
{
|
|
SoundLog((CHAR8 *)String("Could not get free channel, uiChannel = %d", uiChannel));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SoundLog((CHAR8 *)String("Could not load sample, uiSample = %d", uiSample));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Trying to play a sound which is bigger then the 'guiSoundCacheThreshold'
|
|
FastDebugMsg(String("SoundPlay: ERROR: Trying to play %s sound is too large to load into cache, use SoundPlayStreamedFile() instead\n", pFilename));
|
|
|
|
SoundLog((CHAR8 *)String("SoundPlay: ERROR: Trying to play %s sound is too large to load into cache, use SoundPlayStreamedFile() instead\n", pFilename));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SoundLog((CHAR8 *)String("SoundSystemInit FALSE"));
|
|
}
|
|
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
void ResetSoundMap(void)
|
|
{
|
|
gSoundMap.clear();
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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;
|
|
HWFILE hFile;
|
|
|
|
if( fSoundSystemInit )
|
|
{
|
|
if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
|
|
{
|
|
hFile = FileOpen( pFilename, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
|
|
if( !hFile )
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundPlayStreamedFile(): Couldnt open '%s'", pFilename ) );
|
|
return( SOUND_ERROR );
|
|
}
|
|
|
|
UINT32 uiSize = FileGetSize(hFile);
|
|
FileClose(hFile);
|
|
|
|
if (uiSize == 0)
|
|
return ( NO_SAMPLE);
|
|
|
|
return(SoundStartStream( pFilename, uiChannel, pParms));
|
|
}
|
|
}
|
|
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;
|
|
|
|
if(fSoundSystemInit)
|
|
{
|
|
if((uiSample=SoundLoadSample(pFilename))!=NO_SAMPLE)
|
|
{
|
|
// Sample loaded - marking slot
|
|
pSampleList[uiSample].uiFlags|=(SAMPLE_RANDOM|SAMPLE_LOCKED);
|
|
|
|
// Setup time intervals
|
|
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;
|
|
|
|
// Volume
|
|
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;
|
|
|
|
// Panning
|
|
if(pParms->uiPanMin==SOUND_PARMS_DEFAULT)
|
|
{
|
|
pSampleList[uiSample].uiPanMin=128;
|
|
pSampleList[uiSample].uiPanMax=128;
|
|
}
|
|
else
|
|
{
|
|
pSampleList[uiSample].uiPanMin=pParms->uiPanMin;
|
|
pSampleList[uiSample].uiPanMax=pParms->uiPanMax;
|
|
}
|
|
|
|
// Max instances
|
|
if(pParms->uiMaxInstances==SOUND_PARMS_DEFAULT)
|
|
pSampleList[uiSample].uiMaxInstances=1;
|
|
else
|
|
pSampleList[uiSample].uiMaxInstances=pParms->uiMaxInstances;
|
|
|
|
// Priority
|
|
if(pParms->uiPriority==SOUND_PARMS_DEFAULT)
|
|
pSampleList[uiSample].uiPriority=PRIORITY_RANDOM;
|
|
else
|
|
pSampleList[uiSample].uiPriority=pParms->uiPriority;
|
|
|
|
pSampleList[uiSample].uiInstances=0;
|
|
|
|
// Time stamp
|
|
pSampleList[uiSample].uiTimeNext=GetTickCount()+pSampleList[uiSample].uiTimeMin+Random(pSampleList[uiSample].uiTimeMax-pSampleList[uiSample].uiTimeMin);
|
|
|
|
return(uiSample);
|
|
}
|
|
else
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundPlayRandom(): Couldnt open '%s'", pFilename ) );
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
if(fSoundSystemInit)
|
|
{
|
|
if( pSoundList[uiSound].hStream!=NULL )
|
|
return(FSOUND_IsPlaying(pSoundList[uiSound].uiFMODChannel));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// 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, MAX_VOLUME);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// 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;
|
|
|
|
SoundLog(" Stopping all sounds");
|
|
|
|
if(fSoundSystemInit)
|
|
{
|
|
for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
|
|
SoundStopIndex(uiCount);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
//*******************************************************************************
|
|
// 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, MAX_VOLUME);
|
|
|
|
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, MAX_VOLUME);
|
|
|
|
if( pSoundList[uiChannel].hStream!=NULL )
|
|
FSOUND_SetVolume(pSoundList[uiChannel].uiFMODChannel, uiVolCap * 2);
|
|
|
|
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, 255);
|
|
|
|
if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
|
|
{
|
|
if( pSoundList[uiSound].hStream!=NULL )
|
|
FSOUND_SetPan(pSoundList[uiSound].uiFMODChannel, uiPanCap);
|
|
|
|
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].hStream!=NULL )
|
|
return((UINT32)FSOUND_GetVolume(pSoundList[uiChannel].uiFMODChannel) / 2);
|
|
}
|
|
|
|
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;
|
|
BOOLEAN fRandomSoundWasCreated=FALSE;
|
|
|
|
for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
|
|
{
|
|
if(!(pSampleList[uiCount].uiFlags&SAMPLE_RANDOM_MANUAL) && SoundRandomShouldPlay(uiCount))
|
|
fRandomSoundWasCreated |= SoundStartRandom(uiCount);
|
|
}
|
|
|
|
return(fRandomSoundWasCreated);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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)
|
|
{
|
|
if(pSampleList[uiSample].uiFlags&SAMPLE_RANDOM)
|
|
if(pSampleList[uiSample].uiTimeNext <= GetTickCount())
|
|
if(pSampleList[uiSample].uiInstances < pSampleList[uiSample].uiMaxInstances)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// SoundStartRandom
|
|
//
|
|
// Starts an instance of a random sample.
|
|
//
|
|
// Returns: TRUE if a new random sound was created, FALSE if nothing was done.
|
|
//
|
|
//*******************************************************************************
|
|
UINT32 SoundStartRandom(UINT32 uiSample)
|
|
{
|
|
UINT32 uiChannel, uiSoundID;
|
|
SOUNDPARMS spParms;
|
|
|
|
if((uiChannel=SoundGetFreeChannel())!=SOUND_ERROR)
|
|
{
|
|
memset(&spParms, 0xff, sizeof(SOUNDPARMS));
|
|
|
|
spParms.uiVolume=pSampleList[uiSample].uiVolMin+Random(pSampleList[uiSample].uiVolMax-pSampleList[uiSample].uiVolMin);
|
|
spParms.uiPan=pSampleList[uiSample].uiPanMin+Random(pSampleList[uiSample].uiPanMax-pSampleList[uiSample].uiPanMin);
|
|
spParms.uiLoop=1;
|
|
spParms.uiPriority=pSampleList[uiSample].uiPriority;
|
|
|
|
if((uiSoundID=SoundStartSample(uiSample, uiChannel, &spParms))!=SOUND_ERROR)
|
|
{
|
|
pSampleList[uiSample].uiTimeNext=GetTickCount()+pSampleList[uiSample].uiTimeMin+Random(pSampleList[uiSample].uiTimeMax-pSampleList[uiSample].uiTimeMin);
|
|
pSampleList[uiSample].uiInstances++;
|
|
//SoundLog((CHAR8 *)String(" SoundPlayRandom(): Sample #%d = '%s'", uiSample, pFilename ) );
|
|
return(TRUE);
|
|
}
|
|
else
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundStartRandom(): Sample #%d start error - %s", uiSample, FMOD_ErrorString(FSOUND_GetError())));
|
|
}
|
|
else
|
|
SoundLog(" ERROR in SoundStartRandom(): Failed to get free channel");
|
|
return(FALSE);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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].hStream!=NULL )
|
|
{
|
|
uiSample=pSoundList[uiChannel].uiSample;
|
|
|
|
// if this was a random sample, decrease the iteration count
|
|
if (uiSample != -1 && (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(TRUE);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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;
|
|
|
|
if(fSoundSystemInit)
|
|
{
|
|
for(uiCount=0; uiCount < SOUND_MAX_CHANNELS; uiCount++)
|
|
{
|
|
if( (pSoundList[uiCount].hStream!=NULL) && (pSoundList[uiCount].uiSample==-1) )
|
|
{
|
|
// 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);
|
|
SoundLog((CHAR8 *)String(" SoundServiceStreams(): Stop at zero volume on channel %d", 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, uiTime, uiPosition;
|
|
|
|
if(fSoundSystemInit)
|
|
{
|
|
if((uiSound=SoundGetIndexByID(uiSoundID))!=NO_SAMPLE)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
//*******************************************************************************
|
|
// Cacheing Subsystem
|
|
//*******************************************************************************
|
|
|
|
//*******************************************************************************
|
|
// SoundInitCache
|
|
//
|
|
// Zeros out the structures of the sample list.
|
|
//
|
|
//*******************************************************************************
|
|
BOOLEAN SoundInitCache(void)
|
|
{
|
|
UINT32 uiCount;
|
|
|
|
SoundLog("Init cache...");
|
|
SoundLog((CHAR8 *)String(" Using %d cache slots", SOUND_MAX_CACHED));
|
|
|
|
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();
|
|
SoundLog("Cache shutdown");
|
|
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;
|
|
|
|
SoundLog("Cleaning cache");
|
|
SoundStopAll();
|
|
|
|
for(uiCount=0; uiCount < SOUND_MAX_CACHED; uiCount++)
|
|
SoundFreeSampleIndex(uiCount);
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
//*******************************************************************************
|
|
// SoundLoadSample
|
|
//
|
|
// Loads a sample into cache.
|
|
//
|
|
// Returns: Sample index, if OK. NO_SAMPLE - if error.
|
|
//
|
|
//*******************************************************************************
|
|
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);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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;
|
|
|
|
if((hFile=FileOpen(pFilename, FILE_ACCESS_READ, FALSE))!=0)
|
|
{
|
|
uiSize=FileGetSize(hFile);
|
|
if(uiSize == 0)
|
|
{
|
|
return NO_SAMPLE;
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundLoadDisk(): trying to play '%s', not enough memory", 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)
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundLoadDisk(): Trying to play '%s', cache slots are full", pFilename ) );
|
|
FileClose(hFile);
|
|
return(NO_SAMPLE);
|
|
}
|
|
|
|
memset(&pSampleList[uiSample], 0, sizeof(SAMPLETAG));
|
|
|
|
if((pSampleList[uiSample].pData=MemAlloc(uiSize))==NULL)
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundLoadDisk(): Trying to play '%s', memory allocation failed", 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;
|
|
return(uiSample);
|
|
}
|
|
|
|
else
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundLoadDisk(): Failed to open '%s'", pFilename));
|
|
}
|
|
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);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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;
|
|
MemFree(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 uiCaps;
|
|
|
|
SoundLog("Init hardware...");
|
|
|
|
// Try to start up the FMOD Sound System
|
|
FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND);
|
|
FSOUND_SetBufferSize(AUDIO_BUFFER_LEN);
|
|
SoundLog((CHAR8 *)String(" Using DirectSound driver: %s", FSOUND_GetDriverName(FSOUND_GetDriver())));
|
|
SoundLog(" Driver capabilities:");
|
|
FSOUND_GetDriverCaps( FSOUND_GetDriver(), &uiCaps );
|
|
|
|
if ( uiCaps & FSOUND_CAPS_HARDWARE )
|
|
SoundLog(" - supports hardware accelerated 3d sound");
|
|
|
|
if ( uiCaps & FSOUND_CAPS_EAX2 )
|
|
SoundLog(" - supports EAX 2 reverb");
|
|
|
|
if ( uiCaps & FSOUND_CAPS_EAX3 )
|
|
SoundLog(" - supports EAX 3 reverb");
|
|
|
|
if( !FSOUND_Init(44100, SOUND_MAX_CHANNELS, FSOUND_INIT_GLOBALFOCUS|FSOUND_INIT_DONTLATENCYADJUST) )
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundInitHardware(): %s", FMOD_ErrorString(FSOUND_GetError())));
|
|
return(FALSE);
|
|
}
|
|
|
|
SoundLog(" FMOD started");
|
|
SoundLog((CHAR8 *)String(" Mixing rate: %d", FSOUND_GetOutputRate()));
|
|
|
|
// Driver is ready
|
|
hSoundDriver = TRUE;
|
|
fDirectSound = TRUE;
|
|
|
|
return(hSoundDriver);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// SoundShutdownHardware
|
|
//
|
|
// Shuts down the system hardware.
|
|
//
|
|
// Returns: TRUE always.
|
|
//
|
|
//*******************************************************************************
|
|
BOOLEAN SoundShutdownHardware(void)
|
|
{
|
|
if(fSoundSystemInit)
|
|
FSOUND_Close();
|
|
|
|
SoundLog(" FMOD closed");
|
|
return(TRUE);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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].hStream==NULL )
|
|
return(uiCount);
|
|
}
|
|
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// SoundStartSample
|
|
//
|
|
// Starts up a sample on the specified channel. Override parameters are passed
|
|
// in through the structure pointer pParms. Any entry with a value of 0xffffffff
|
|
// will be filled in by the system.
|
|
//
|
|
// Returns: Unique sound ID if successful, SOUND_ERROR if not.
|
|
//
|
|
//*******************************************************************************
|
|
UINT32 SoundStartSample(UINT32 uiSample, UINT32 uiChannel, SOUNDPARMS *pParms)
|
|
{
|
|
UINT32 uiSoundID;
|
|
|
|
if(!fSoundSystemInit)
|
|
return(SOUND_ERROR);
|
|
|
|
// Setting up stream buffer
|
|
FSOUND_Stream_SetBufferSize(STREAM_BUFFER_LEN);
|
|
|
|
// Creating stream
|
|
pSoundList[uiChannel].hStream=FSOUND_Stream_Open((STR8) pSampleList[uiSample].pData, FSOUND_LOADMEMORY|FSOUND_LOOP_NORMAL|FSOUND_2D, 0, pSampleList[uiSample].uiSize);
|
|
if(pSoundList[uiChannel].hStream==NULL)
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundStartSample(): %s", FMOD_ErrorString(FSOUND_GetError()) ));
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
// Setup sample params
|
|
// Loop
|
|
if((pParms!=NULL) && (pParms->uiLoop!=SOUND_PARMS_DEFAULT))
|
|
{
|
|
// If looping infinitely, 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;
|
|
}
|
|
else
|
|
FSOUND_Stream_SetLoopCount(pSoundList[uiChannel].hStream, pParms->uiLoop-1);
|
|
}
|
|
|
|
// Starting stream in pause
|
|
pSoundList[uiChannel].uiFMODChannel = FSOUND_Stream_PlayEx(uiChannel, pSoundList[uiChannel].hStream, NULL, TRUE);
|
|
if(pSoundList[uiChannel].uiFMODChannel==-1)
|
|
{
|
|
FSOUND_Stream_Close(pSoundList[uiChannel].hStream);
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundStartSample(): %s", FMOD_ErrorString(FSOUND_GetError()) ));
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
// Speed and pitchbend don't use
|
|
|
|
// Volume
|
|
if((pParms!=NULL) && (pParms->uiVolume!=SOUND_PARMS_DEFAULT))
|
|
FSOUND_SetVolume(pSoundList[uiChannel].uiFMODChannel, pParms->uiVolume * 2);
|
|
else
|
|
FSOUND_SetVolume(pSoundList[uiChannel].uiFMODChannel, guiSoundDefaultVolume);
|
|
|
|
// Panning
|
|
if((pParms!=NULL) && (pParms->uiPan!=SOUND_PARMS_DEFAULT))
|
|
FSOUND_SetPan(pSoundList[uiChannel].uiFMODChannel, pParms->uiPan);
|
|
else
|
|
FSOUND_SetPan(pSoundList[uiChannel].uiFMODChannel, 128);
|
|
|
|
// Start sound!
|
|
FSOUND_SetPaused(pSoundList[uiChannel].uiFMODChannel, FALSE);
|
|
|
|
// Priority
|
|
if((pParms!=NULL) && (pParms->uiPriority!=SOUND_PARMS_DEFAULT))
|
|
pSoundList[uiChannel].uiPriority=pParms->uiPriority;
|
|
else
|
|
pSoundList[uiChannel].uiPriority=PRIORITY_MAX;
|
|
|
|
// Callback at end of playback
|
|
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;
|
|
}
|
|
|
|
// Other stuff...
|
|
uiSoundID=SoundGetUniqueID();
|
|
pSoundList[uiChannel].uiSoundID=uiSoundID;
|
|
pSoundList[uiChannel].uiSample=uiSample;
|
|
pSoundList[uiChannel].uiTimeStamp=GetTickCount();
|
|
pSoundList[uiChannel].uiFadeVolume = SoundGetVolumeIndex(uiChannel);
|
|
|
|
pSampleList[uiSample].uiCacheHits++;
|
|
|
|
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;
|
|
|
|
if(!fSoundSystemInit)
|
|
return(SOUND_ERROR);
|
|
|
|
// Setting up stream buffer
|
|
FSOUND_Stream_SetBufferSize(STREAM_BUFFER_LEN);
|
|
|
|
// Creating stream
|
|
pSoundList[uiChannel].hStream=FSOUND_Stream_Open(pFilename, FSOUND_LOOP_NORMAL|FSOUND_2D, 0, 0);
|
|
if(pSoundList[uiChannel].hStream==NULL)
|
|
{
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundStartStream(): %s ('%s')", FMOD_ErrorString(FSOUND_GetError()), pFilename));
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
// Setup params
|
|
// Loop
|
|
if( (pParms!=NULL) && (pParms->uiLoop!=SOUND_PARMS_DEFAULT ) )
|
|
{
|
|
if(pParms->uiLoop>0)
|
|
FSOUND_Stream_SetLoopCount(pSoundList[uiChannel].hStream, pParms->uiLoop-1);
|
|
}
|
|
|
|
// Starting stream in pause
|
|
pSoundList[uiChannel].uiFMODChannel = FSOUND_Stream_PlayEx(uiChannel, pSoundList[uiChannel].hStream, NULL, TRUE);
|
|
if(pSoundList[uiChannel].uiFMODChannel==-1)
|
|
{
|
|
FSOUND_Stream_Close(pSoundList[uiChannel].hStream);
|
|
SoundLog((CHAR8 *)String(" ERROR in SoundStartStream(): %s ('%s')", FMOD_ErrorString(FSOUND_GetError()), pFilename));
|
|
return(SOUND_ERROR);
|
|
}
|
|
|
|
// Speed and pitchbend don't use
|
|
|
|
// Volume
|
|
if((pParms!=NULL) && (pParms->uiVolume!=SOUND_PARMS_DEFAULT))
|
|
FSOUND_SetVolume(pSoundList[uiChannel].uiFMODChannel, pParms->uiVolume * 2);
|
|
else
|
|
FSOUND_SetVolume(pSoundList[uiChannel].uiFMODChannel, guiSoundDefaultVolume);
|
|
|
|
// Panning
|
|
if((pParms!=NULL) && (pParms->uiPan!=SOUND_PARMS_DEFAULT))
|
|
FSOUND_SetPan(pSoundList[uiChannel].uiFMODChannel, pParms->uiPan);
|
|
|
|
// Start sound!
|
|
FSOUND_SetPaused(pSoundList[uiChannel].uiFMODChannel, FALSE);
|
|
|
|
// Get ID
|
|
uiSoundID=SoundGetUniqueID();
|
|
pSoundList[uiChannel].uiSoundID=uiSoundID;
|
|
|
|
// Priority
|
|
if(pParms)
|
|
pSoundList[uiChannel].uiPriority=pParms->uiPriority;
|
|
else
|
|
pSoundList[uiChannel].uiPriority=SOUND_PARMS_DEFAULT;
|
|
|
|
// Callback at end of playback
|
|
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;
|
|
}
|
|
|
|
// Other stuff
|
|
pSoundList[uiChannel].uiTimeStamp=GetTickCount();
|
|
pSoundList[uiChannel].uiFadeVolume = SoundGetVolumeIndex(uiChannel);
|
|
pSoundList[uiChannel].uiSample = -1; // it's streaming directly from file !!!!
|
|
|
|
return(uiSoundID);
|
|
}
|
|
|
|
// ------------------------
|
|
// Callbacks implementation
|
|
// ========================
|
|
static void * F_CALLBACKAPI SoundFileOpen(const STR8 pName)
|
|
{
|
|
return((void*)FileOpen(pName, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE));
|
|
}
|
|
|
|
static void F_CALLBACKAPI SoundFileClose(void *uiHandle)
|
|
{
|
|
FileClose((UINT32)uiHandle);
|
|
}
|
|
|
|
static INT F_CALLBACKAPI SoundFileRead(void *pBuffer, INT iSize, void *uiHandle)
|
|
{
|
|
UINT32 uiActuallyRead;
|
|
|
|
FileRead((UINT32)uiHandle, pBuffer, iSize, &uiActuallyRead);
|
|
return(uiActuallyRead);
|
|
}
|
|
|
|
static INT F_CALLBACKAPI SoundFileSeek(void *uiHandle, INT iPos, signed char cMode)
|
|
{
|
|
UINT8 uiHow;
|
|
|
|
switch(cMode)
|
|
{
|
|
case SEEK_END:
|
|
uiHow = FILE_SEEK_FROM_END;
|
|
break;
|
|
case SEEK_CUR:
|
|
uiHow = FILE_SEEK_FROM_CURRENT;
|
|
break;
|
|
default:
|
|
uiHow = FILE_SEEK_FROM_START;
|
|
}
|
|
|
|
return(!FileSeek((UINT32)uiHandle, iPos, uiHow));
|
|
}
|
|
|
|
static INT F_CALLBACKAPI SoundFileTell(void *uiHandle)
|
|
{
|
|
return(FileGetPos((UINT32)uiHandle));
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// 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==NO_SAMPLE)
|
|
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].hStream!=NULL)
|
|
{
|
|
FSOUND_Stream_Stop(pSoundList[uiChannel].hStream);
|
|
FSOUND_Stream_Close(pSoundList[uiChannel].hStream);
|
|
|
|
uiSample=pSoundList[uiChannel].uiSample;
|
|
|
|
// if this was a random sample, decrease the iteration count
|
|
if ( (uiSample != -1) && (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);
|
|
|
|
memset(&pSoundList[uiChannel], 0, sizeof(SOUNDTAG));
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// SoundLog
|
|
// Writes string into log file
|
|
//
|
|
// Returns nothing
|
|
//
|
|
// Created: 10.12.2005 Lesh
|
|
//*****************************************************************************************
|
|
void SoundLog(CHAR8 *strMessage)
|
|
{
|
|
static struct SoundLog {
|
|
sgp::Logger_ID id;
|
|
SoundLog() {
|
|
id = sgp::Logger::instance().createLogger();
|
|
sgp::Logger::instance().connectFile(id, SndDebugFileName, true, sgp::Logger::FLUSH_ON_DELETE);
|
|
}
|
|
} s_SoundLog;
|
|
SGP_LOG(s_SoundLog.id, vfs::String::widen(strMessage,strlen(strMessage)));
|
|
}
|