mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-11 13:30:59 +00:00
2c58845d28
- s_scaleDownAndClamp CVar so the clamping and scaling down of all sounds (that is done to prevent clipping) can be disabled (enabled by default) - s_alOutputLimiter CVar to allow configuring ALC_SOFT_output_limiter which (if enabled) reduces the overall volume if it gets too loud, to avoid clipping (defaults to -1 = "let OpenAL decide") - s_alHRTF to allow enabling or disabling HRTF, requires ALC_SOFT_HRTF (defaults to -1 = "let OpenAL decide") Those CVars can be changed at runtime and are applied immediately (in case of s_alHRTF and s_alOutputLimiter by resetting the OpenAL device, just like we do when a disconnect is detected)
963 lines
30 KiB
C++
963 lines
30 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#ifndef __SND_LOCAL_H__
|
|
#define __SND_LOCAL_H__
|
|
|
|
#ifdef ID_DEDICATED
|
|
// stub-only mode: AL_API and ALC_API shouldn't refer to any dll-stuff
|
|
// because the implemenations are in openal_stub.cpp
|
|
// this is ensured by defining AL_LIBTYPE_STATIC before including the AL headers
|
|
#define AL_LIBTYPE_STATIC
|
|
#endif
|
|
|
|
#include <AL/al.h>
|
|
#include <AL/alc.h>
|
|
#include <AL/alext.h>
|
|
|
|
// DG: make this code build with older OpenAL headers that don't know about ALC_SOFT_HRTF
|
|
// which provides LPALCRESETDEVICESOFT for idSoundSystemLocal::alcResetDeviceSOFT()
|
|
#ifndef ALC_SOFT_HRTF
|
|
typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
|
|
#endif
|
|
|
|
// DG: ALC_SOFT_output_mode is pretty new, provide compatibility..
|
|
#ifndef ALC_SOFT_output_mode
|
|
#define ALC_SOFT_output_mode
|
|
#define ALC_OUTPUT_MODE_SOFT 0x19AC
|
|
#define ALC_ANY_SOFT 0x19AD
|
|
|
|
#define ALC_STEREO_BASIC_SOFT 0x19AE
|
|
#define ALC_STEREO_UHJ_SOFT 0x19AF
|
|
#define ALC_STEREO_HRTF_SOFT 0x19B2
|
|
|
|
#define ALC_SURROUND_5_1_SOFT 0x1504
|
|
#define ALC_SURROUND_6_1_SOFT 0x1505
|
|
#define ALC_SURROUND_7_1_SOFT 0x1506
|
|
#endif
|
|
// the following formats are defined in https://openal-soft.org/openal-extensions/SOFT_output_mode.txt
|
|
// but commented out in OpenAL Softs current AL/alext.h
|
|
#ifndef ALC_MONO_SOFT
|
|
#define ALC_MONO_SOFT 0x1500
|
|
#endif
|
|
#ifndef ALC_STEREO_SOFT
|
|
#define ALC_STEREO_SOFT 0x1501
|
|
#endif
|
|
#ifndef ALC_QUAD_SOFT
|
|
#define ALC_QUAD_SOFT 0x1503
|
|
#endif
|
|
|
|
// DG: in case ALC_SOFT_output_limiter is not available in some headers..
|
|
#ifndef ALC_SOFT_output_limiter
|
|
#define ALC_SOFT_output_limiter
|
|
#define ALC_OUTPUT_LIMITER_SOFT 0x199A
|
|
#endif
|
|
|
|
#include "framework/UsercmdGen.h"
|
|
#include "sound/efxlib.h"
|
|
#include "sound/sound.h"
|
|
|
|
// demo sound commands
|
|
typedef enum {
|
|
SCMD_STATE, // followed by a load game state
|
|
SCMD_PLACE_LISTENER,
|
|
SCMD_ALLOC_EMITTER,
|
|
|
|
SCMD_FREE,
|
|
SCMD_UPDATE,
|
|
SCMD_START,
|
|
SCMD_MODIFY,
|
|
SCMD_STOP,
|
|
SCMD_FADE
|
|
} soundDemoCommand_t;
|
|
|
|
const int SOUND_MAX_CHANNELS = 8;
|
|
const int SOUND_DECODER_FREE_DELAY = 1000 * MIXBUFFER_SAMPLES / USERCMD_MSEC; // four seconds
|
|
|
|
const int PRIMARYFREQ = 44100; // samples per second
|
|
const float SND_EPSILON = 1.0f / 32768.0f; // if volume is below this, it will always multiply to zero
|
|
|
|
const int ROOM_SLICES_IN_BUFFER = 10;
|
|
|
|
class idAudioBuffer;
|
|
class idWaveFile;
|
|
class idSoundCache;
|
|
class idSoundSample;
|
|
class idSampleDecoder;
|
|
class idSoundWorldLocal;
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
General extended waveform format structure.
|
|
Use this for all NON PCM formats.
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
#pragma pack(1)
|
|
#endif
|
|
struct waveformatex_s {
|
|
word wFormatTag; /* format type */
|
|
word nChannels; /* number of channels (i.e. mono, stereo...) */
|
|
dword nSamplesPerSec; /* sample rate */
|
|
dword nAvgBytesPerSec; /* for buffer estimation */
|
|
word nBlockAlign; /* block size of data */
|
|
word wBitsPerSample; /* Number of bits per sample of mono data */
|
|
word cbSize; /* The count in bytes of the size of
|
|
extra information (after cbSize) */
|
|
} PACKED;
|
|
|
|
typedef waveformatex_s waveformatex_t;
|
|
|
|
/* OLD general waveform format structure (information common to all formats) */
|
|
struct waveformat_s {
|
|
word wFormatTag; /* format type */
|
|
word nChannels; /* number of channels (i.e. mono, stereo, etc.) */
|
|
dword nSamplesPerSec; /* sample rate */
|
|
dword nAvgBytesPerSec; /* for buffer estimation */
|
|
word nBlockAlign; /* block size of data */
|
|
} PACKED;
|
|
|
|
typedef waveformat_s waveformat_t;
|
|
|
|
/* flags for wFormatTag field of WAVEFORMAT */
|
|
enum {
|
|
WAVE_FORMAT_TAG_PCM = 1,
|
|
WAVE_FORMAT_TAG_OGG = 2
|
|
};
|
|
|
|
/* specific waveform format structure for PCM data */
|
|
struct pcmwaveformat_s {
|
|
waveformat_t wf;
|
|
word wBitsPerSample;
|
|
} PACKED;
|
|
|
|
typedef pcmwaveformat_s pcmwaveformat_t;
|
|
|
|
#ifndef mmioFOURCC
|
|
#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \
|
|
( (dword)(byte)(ch0) | ( (dword)(byte)(ch1) << 8 ) | \
|
|
( (dword)(byte)(ch2) << 16 ) | ( (dword)(byte)(ch3) << 24 ) )
|
|
#endif
|
|
|
|
#define fourcc_riff mmioFOURCC('R', 'I', 'F', 'F')
|
|
|
|
struct waveformatextensible_s {
|
|
waveformatex_t Format;
|
|
union {
|
|
word wValidBitsPerSample; /* bits of precision */
|
|
word wSamplesPerBlock; /* valid if wBitsPerSample==0*/
|
|
word wReserved; /* If neither applies, set to zero*/
|
|
} Samples;
|
|
dword dwChannelMask; /* which channels are */
|
|
/* present in stream */
|
|
int SubFormat;
|
|
} PACKED;
|
|
|
|
typedef waveformatextensible_s waveformatextensible_t;
|
|
|
|
typedef dword fourcc;
|
|
|
|
/* RIFF chunk information data structure */
|
|
struct mminfo_s {
|
|
fourcc ckid; /* chunk ID */
|
|
dword cksize; /* chunk size */
|
|
fourcc fccType; /* form type or list type */
|
|
dword dwDataOffset; /* offset of data portion of chunk */
|
|
} PACKED;
|
|
|
|
typedef mminfo_s mminfo_t;
|
|
|
|
#ifdef WIN32
|
|
#pragma pack()
|
|
#endif
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
idWaveFile
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
class idWaveFile {
|
|
public:
|
|
idWaveFile( void );
|
|
~idWaveFile( void );
|
|
|
|
int Open( const char* strFileName, waveformatex_t* pwfx = NULL );
|
|
int OpenFromMemory( short* pbData, int ulDataSize, waveformatextensible_t* pwfx );
|
|
int Read( byte* pBuffer, int dwSizeToRead, int *pdwSizeRead );
|
|
int Seek( int offset );
|
|
int Close( void );
|
|
int ResetFile( void );
|
|
|
|
int GetOutputSize( void ) { return mdwSize; }
|
|
int GetMemorySize( void ) { return mMemSize; }
|
|
|
|
waveformatextensible_t mpwfx; // Pointer to waveformatex structure
|
|
|
|
private:
|
|
idFile * mhmmio; // I/O handle for the WAVE
|
|
mminfo_t mck; // Multimedia RIFF chunk
|
|
mminfo_t mckRiff; // used when opening a WAVE file
|
|
dword mdwSize; // size in samples
|
|
dword mMemSize; // size of the wave data in memory
|
|
dword mseekBase;
|
|
ID_TIME_T mfileTime;
|
|
|
|
bool mbIsReadingFromMemory;
|
|
short * mpbData;
|
|
short * mpbDataCur;
|
|
dword mulDataSize;
|
|
|
|
void * ogg; // only !NULL when !s_realTimeDecoding
|
|
byte* oggData; // the contents of the .ogg for stbi_vorbis (it doesn't support custom reading callbacks)
|
|
bool isOgg;
|
|
|
|
private:
|
|
int ReadMMIO( void );
|
|
|
|
int OpenOGG( const char* strFileName, waveformatex_t* pwfx = NULL );
|
|
int ReadOGG( byte* pBuffer, int dwSizeToRead, int *pdwSizeRead );
|
|
int CloseOGG( void );
|
|
};
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
Encapsulates functionality of a DirectSound buffer.
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
class idAudioBuffer {
|
|
public:
|
|
virtual int Play( dword dwPriority=0, dword dwFlags=0 ) = 0;
|
|
virtual int Stop( void ) = 0;
|
|
virtual int Reset( void ) = 0;
|
|
virtual bool IsSoundPlaying( void ) = 0;
|
|
virtual void SetVolume( float x ) = 0;
|
|
};
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
idSoundEmitterLocal
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
typedef enum {
|
|
REMOVE_STATUS_INVALID = -1,
|
|
REMOVE_STATUS_ALIVE = 0,
|
|
REMOVE_STATUS_WAITSAMPLEFINISHED = 1,
|
|
REMOVE_STATUS_SAMPLEFINISHED = 2
|
|
} removeStatus_t;
|
|
|
|
class idSoundFade {
|
|
public:
|
|
int fadeStart44kHz;
|
|
int fadeEnd44kHz;
|
|
float fadeStartVolume; // in dB
|
|
float fadeEndVolume; // in dB
|
|
|
|
void Clear();
|
|
float FadeDbAt44kHz( int current44kHz );
|
|
};
|
|
|
|
class SoundFX {
|
|
protected:
|
|
bool initialized;
|
|
|
|
int channel;
|
|
int maxlen;
|
|
|
|
float* buffer;
|
|
float continuitySamples[4];
|
|
|
|
float param;
|
|
|
|
public:
|
|
SoundFX() { channel = 0; buffer = NULL; initialized = false; maxlen = 0; memset( continuitySamples, 0, sizeof( float ) * 4 ); };
|
|
virtual ~SoundFX() { if ( buffer ) delete buffer; };
|
|
|
|
virtual void Initialize() { };
|
|
virtual void ProcessSample( float* in, float* out ) = 0;
|
|
|
|
void SetChannel( int chan ) { channel = chan; };
|
|
int GetChannel() { return channel; };
|
|
|
|
void SetContinuitySamples( float in1, float in2, float out1, float out2 ) { continuitySamples[0] = in1; continuitySamples[1] = in2; continuitySamples[2] = out1; continuitySamples[3] = out2; }; // FIXME?
|
|
void GetContinuitySamples( float& in1, float& in2, float& out1, float& out2 ) { in1 = continuitySamples[0]; in2 = continuitySamples[1]; out1 = continuitySamples[2]; out2 = continuitySamples[3]; };
|
|
|
|
void SetParameter( float val ) { param = val; };
|
|
};
|
|
|
|
class SoundFX_Lowpass : public SoundFX {
|
|
public:
|
|
virtual void ProcessSample( float* in, float* out );
|
|
};
|
|
|
|
class SoundFX_LowpassFast : public SoundFX {
|
|
float freq;
|
|
float res;
|
|
float a1, a2, a3;
|
|
float b1, b2;
|
|
|
|
public:
|
|
virtual void ProcessSample( float* in, float* out );
|
|
void SetParms( float p1 = 0, float p2 = 0, float p3 = 0 );
|
|
};
|
|
|
|
class SoundFX_Comb : public SoundFX {
|
|
int currentTime;
|
|
|
|
public:
|
|
virtual void Initialize();
|
|
virtual void ProcessSample( float* in, float* out );
|
|
};
|
|
|
|
class FracTime {
|
|
public:
|
|
int time;
|
|
float frac;
|
|
|
|
void Set( int val ) { time = val; frac = 0; };
|
|
void Increment( float val ) { frac += val; while ( frac >= 1.f ) { time++; frac--; } };
|
|
};
|
|
|
|
enum {
|
|
PLAYBACK_RESET,
|
|
PLAYBACK_ADVANCING
|
|
};
|
|
|
|
class idSoundChannel;
|
|
|
|
class idSlowChannel {
|
|
bool active;
|
|
const idSoundChannel* chan;
|
|
|
|
int playbackState;
|
|
int triggerOffset;
|
|
|
|
FracTime newPosition;
|
|
int newSampleOffset;
|
|
|
|
FracTime curPosition;
|
|
int curSampleOffset;
|
|
|
|
SoundFX_LowpassFast lowpass;
|
|
|
|
// functions
|
|
void GenerateSlowChannel( FracTime& playPos, int sampleCount44k, float* finalBuffer );
|
|
|
|
float GetSlowmoSpeed();
|
|
|
|
public:
|
|
|
|
void AttachSoundChannel( const idSoundChannel *chan );
|
|
void Reset();
|
|
|
|
void GatherChannelSamples( int sampleOffset44k, int sampleCount44k, float *dest );
|
|
|
|
bool IsActive() { return active; };
|
|
FracTime GetCurrentPosition() { return curPosition; };
|
|
};
|
|
|
|
class idSoundChannel {
|
|
public:
|
|
idSoundChannel( void );
|
|
~idSoundChannel( void );
|
|
|
|
void Clear( void );
|
|
void Start( void );
|
|
void Stop( void );
|
|
void GatherChannelSamples( int sampleOffset44k, int sampleCount44k, float *dest ) const;
|
|
void ALStop( void ); // free OpenAL resources if any
|
|
|
|
bool triggerState;
|
|
int trigger44kHzTime; // hardware time sample the channel started
|
|
int triggerGame44kHzTime; // game time sample time the channel started
|
|
soundShaderParms_t parms; // combines the shader parms and the per-channel overrides
|
|
idSoundSample * leadinSample; // if not looped, this is the only sample
|
|
s_channelType triggerChannel;
|
|
const idSoundShader *soundShader;
|
|
idSampleDecoder * decoder;
|
|
float diversity;
|
|
float lastVolume; // last calculated volume based on distance
|
|
float lastV[6]; // last calculated volume for each speaker, so we can smoothly fade
|
|
idSoundFade channelFade;
|
|
bool triggered;
|
|
ALuint openalSource;
|
|
ALuint openalStreamingOffset;
|
|
ALuint openalStreamingBuffer[3];
|
|
ALuint lastopenalStreamingBuffer[3];
|
|
bool stopped;
|
|
|
|
bool paused; // DG: currently paused, but generally still playing - for when menu is open etc
|
|
|
|
bool disallowSlow;
|
|
|
|
};
|
|
|
|
class idSoundEmitterLocal : public idSoundEmitter {
|
|
public:
|
|
|
|
idSoundEmitterLocal( void );
|
|
virtual ~idSoundEmitterLocal( void );
|
|
|
|
//----------------------------------------------
|
|
|
|
// the "time" parameters should be game time in msec, which is used to make queries
|
|
// return deterministic values regardless of async buffer scheduling
|
|
|
|
// a non-immediate free will let all currently playing sounds complete
|
|
virtual void Free( bool immediate );
|
|
|
|
// the parms specified will be the default overrides for all sounds started on this emitter.
|
|
// NULL is acceptable for parms
|
|
virtual void UpdateEmitter( const idVec3 &origin, int listenerId, const soundShaderParms_t *parms );
|
|
|
|
// returns the length of the started sound in msec
|
|
virtual int StartSound( const idSoundShader *shader, const s_channelType channel, float diversity = 0, int shaderFlags = 0, bool allowSlow = true /* D3XP */ );
|
|
|
|
// can pass SCHANNEL_ANY
|
|
virtual void ModifySound( const s_channelType channel, const soundShaderParms_t *parms );
|
|
virtual void StopSound( const s_channelType channel );
|
|
virtual void FadeSound( const s_channelType channel, float to, float over );
|
|
|
|
virtual bool CurrentlyPlaying( void ) const;
|
|
|
|
// can pass SCHANNEL_ANY
|
|
virtual float CurrentAmplitude( void );
|
|
|
|
// used for save games
|
|
virtual int Index( void ) const;
|
|
|
|
//----------------------------------------------
|
|
|
|
void Clear( void );
|
|
|
|
void PauseAll( void ); // DG: to pause active OpenAL sources when entering menu etc
|
|
void UnPauseAll( void ); // DG: to resume active OpenAL sources when leaving menu etc
|
|
|
|
void OverrideParms( const soundShaderParms_t *base, const soundShaderParms_t *over, soundShaderParms_t *out );
|
|
void CheckForCompletion( int current44kHzTime );
|
|
void Spatialize( idVec3 listenerPos, int listenerArea, idRenderWorld *rw );
|
|
|
|
idSoundWorldLocal * soundWorld; // the world that holds this emitter
|
|
|
|
int index; // in world emitter list
|
|
removeStatus_t removeStatus;
|
|
|
|
idVec3 origin;
|
|
int listenerId;
|
|
soundShaderParms_t parms; // default overrides for all channels
|
|
|
|
|
|
// the following are calculated in UpdateEmitter, and don't need to be archived
|
|
float maxDistance; // greatest of all playing channel distances
|
|
int lastValidPortalArea; // so an emitter that slides out of the world continues playing
|
|
bool playing; // if false, no channel is active
|
|
bool hasShakes;
|
|
idVec3 spatializedOrigin; // the virtual sound origin, either the real sound origin,
|
|
// or a point through a portal chain
|
|
float realDistance; // in meters
|
|
float distance; // in meters, this may be the straight-line distance, or
|
|
// it may go through a chain of portals. If there
|
|
// is not an open-portal path, distance will be > maxDistance
|
|
|
|
// a single soundEmitter can have many channels playing from the same point
|
|
idSoundChannel channels[SOUND_MAX_CHANNELS];
|
|
|
|
idSlowChannel slowChannels[SOUND_MAX_CHANNELS];
|
|
|
|
idSlowChannel GetSlowChannel( const idSoundChannel *chan );
|
|
void SetSlowChannel( const idSoundChannel *chan, idSlowChannel slow );
|
|
void ResetSlowChannel( const idSoundChannel *chan );
|
|
|
|
// this is just used for feedback to the game or rendering system:
|
|
// flashing lights and screen shakes. Because the material expression
|
|
// evaluation doesn't do common subexpression removal, we cache the
|
|
// last generated value
|
|
int ampTime;
|
|
float amplitude;
|
|
};
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
idSoundWorldLocal
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
class s_stats {
|
|
public:
|
|
s_stats( void ) {
|
|
rinuse = 0;
|
|
runs = 1;
|
|
timeinprocess = 0;
|
|
missedWindow = 0;
|
|
missedUpdateWindow = 0;
|
|
activeSounds = 0;
|
|
}
|
|
int rinuse;
|
|
int runs;
|
|
int timeinprocess;
|
|
int missedWindow;
|
|
int missedUpdateWindow;
|
|
int activeSounds;
|
|
};
|
|
|
|
typedef struct soundPortalTrace_s {
|
|
int portalArea;
|
|
const struct soundPortalTrace_s *prevStack;
|
|
} soundPortalTrace_t;
|
|
|
|
class idSoundWorldLocal : public idSoundWorld {
|
|
public:
|
|
virtual ~idSoundWorldLocal( void );
|
|
|
|
// call at each map start
|
|
virtual void ClearAllSoundEmitters( void );
|
|
virtual void StopAllSounds( void );
|
|
|
|
// get a new emitter that can play sounds in this world
|
|
virtual idSoundEmitter *AllocSoundEmitter( void );
|
|
|
|
// for load games
|
|
virtual idSoundEmitter *EmitterForIndex( int index );
|
|
|
|
// query data from all emitters in the world
|
|
virtual float CurrentShakeAmplitudeForPosition( const int time, const idVec3 &listererPosition );
|
|
|
|
// where is the camera/microphone
|
|
// listenerId allows listener-private sounds to be added
|
|
virtual void PlaceListener( const idVec3 &origin, const idMat3 &axis, const int listenerId, const int gameTime, const idStr& areaName );
|
|
|
|
// fade all sounds in the world with a given shader soundClass
|
|
// to is in Db (sigh), over is in seconds
|
|
virtual void FadeSoundClasses( const int soundClass, const float to, const float over );
|
|
|
|
// dumps the current state and begins archiving commands
|
|
virtual void StartWritingDemo( idDemoFile *demo );
|
|
virtual void StopWritingDemo( void );
|
|
|
|
// read a sound command from a demo file
|
|
virtual void ProcessDemoCommand( idDemoFile *readDemo );
|
|
|
|
// background music
|
|
virtual void PlayShaderDirectly( const char *name, int channel = -1 );
|
|
|
|
// pause and unpause the sound world
|
|
virtual void Pause( void );
|
|
virtual void UnPause( void );
|
|
virtual bool IsPaused( void );
|
|
|
|
// avidump
|
|
virtual void AVIOpen( const char *path, const char *name );
|
|
virtual void AVIClose( void );
|
|
|
|
// SaveGame Support
|
|
virtual void WriteToSaveGame( idFile *savefile );
|
|
virtual void ReadFromSaveGame( idFile *savefile );
|
|
|
|
virtual void ReadFromSaveGameSoundChannel( idFile *saveGame, idSoundChannel *ch );
|
|
virtual void ReadFromSaveGameSoundShaderParams( idFile *saveGame, soundShaderParms_t *params );
|
|
virtual void WriteToSaveGameSoundChannel( idFile *saveGame, idSoundChannel *ch );
|
|
virtual void WriteToSaveGameSoundShaderParams( idFile *saveGame, soundShaderParms_t *params );
|
|
|
|
virtual void SetSlowmo( bool active );
|
|
virtual void SetSlowmoSpeed( float speed );
|
|
virtual void SetEnviroSuit( bool active );
|
|
|
|
//=======================================
|
|
|
|
idSoundWorldLocal( void );
|
|
|
|
void Shutdown( void );
|
|
void Init( idRenderWorld *rw );
|
|
|
|
// update
|
|
void ForegroundUpdate( int currentTime );
|
|
void OffsetSoundTime( int offset44kHz );
|
|
|
|
idSoundEmitterLocal * AllocLocalSoundEmitter();
|
|
void CalcEars( int numSpeakers, idVec3 realOrigin, idVec3 listenerPos, idMat3 listenerAxis, float ears[6], float spatialize );
|
|
void AddChannelContribution( idSoundEmitterLocal *sound, idSoundChannel *chan,
|
|
int current44kHz, int numSpeakers, float *finalMixBuffer );
|
|
void MixLoop( int current44kHz, int numSpeakers, float *finalMixBuffer );
|
|
void AVIUpdate( void );
|
|
void ResolveOrigin( const int stackDepth, const soundPortalTrace_t *prevStack, const int soundArea, const float dist, const idVec3& soundOrigin, idSoundEmitterLocal *def );
|
|
float FindAmplitude( idSoundEmitterLocal *sound, const int localTime, const idVec3 *listenerPosition, const s_channelType channel, bool shakesOnly );
|
|
|
|
//============================================
|
|
|
|
idRenderWorld * rw; // for portals and debug drawing
|
|
idDemoFile * writeDemo; // if not NULL, archive commands here
|
|
|
|
idMat3 listenerAxis;
|
|
idVec3 listenerPos; // position in meters
|
|
int listenerPrivateId;
|
|
idVec3 listenerQU; // position in "quake units"
|
|
int listenerArea;
|
|
idStr listenerAreaName;
|
|
ALuint listenerEffect;
|
|
ALuint listenerSlot;
|
|
bool listenerAreFiltersInitialized;
|
|
ALuint listenerFilters[2]; // 0 - direct; 1 - send.
|
|
float listenerSlotReverbGain;
|
|
|
|
int gameMsec;
|
|
int game44kHz;
|
|
int pause44kHz;
|
|
int lastAVI44kHz; // determine when we need to mix and write another block
|
|
|
|
idList<idSoundEmitterLocal *>emitters;
|
|
|
|
idSoundFade soundClassFade[SOUND_MAX_CLASSES]; // for global sound fading
|
|
|
|
// avi stuff
|
|
idFile * fpa[6];
|
|
idStr aviDemoPath;
|
|
idStr aviDemoName;
|
|
|
|
idSoundEmitterLocal * localSound; // just for playShaderDirectly()
|
|
|
|
bool slowmoActive;
|
|
float slowmoSpeed;
|
|
bool enviroSuitActive;
|
|
};
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
idSoundSystemLocal
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
typedef struct {
|
|
ALuint handle;
|
|
int startTime;
|
|
idSoundChannel *chan;
|
|
bool inUse;
|
|
bool looping;
|
|
bool stereo;
|
|
} openalSource_t;
|
|
|
|
class idSoundSystemLocal : public idSoundSystem {
|
|
public:
|
|
idSoundSystemLocal( ) {
|
|
isInitialized = false;
|
|
}
|
|
|
|
// all non-hardware initialization
|
|
virtual void Init( void );
|
|
|
|
// shutdown routine
|
|
virtual void Shutdown( void );
|
|
|
|
// sound is attached to the window, and must be recreated when the window is changed
|
|
virtual bool ShutdownHW( void );
|
|
virtual bool InitHW( void );
|
|
|
|
// async loop, called at 60Hz
|
|
virtual int AsyncUpdate( int time );
|
|
// async loop, when the sound driver uses a write strategy
|
|
virtual int AsyncUpdateWrite( int time );
|
|
// direct mixing called from the sound driver thread for OSes that support it
|
|
virtual int AsyncMix( int soundTime, float *mixBuffer );
|
|
|
|
virtual void SetMute( bool mute );
|
|
|
|
virtual cinData_t ImageForTime( const int milliseconds, const bool waveform );
|
|
|
|
int GetSoundDecoderInfo( int index, soundDecoderInfo_t &decoderInfo );
|
|
|
|
// if rw == NULL, no portal occlusion or rendered debugging is available
|
|
virtual idSoundWorld *AllocSoundWorld( idRenderWorld *rw );
|
|
|
|
// specifying NULL will cause silence to be played
|
|
virtual void SetPlayingSoundWorld( idSoundWorld *soundWorld );
|
|
|
|
// some tools, like the sound dialog, may be used in both the game and the editor
|
|
// This can return NULL, so check!
|
|
virtual idSoundWorld *GetPlayingSoundWorld( void );
|
|
|
|
virtual void BeginLevelLoad( void );
|
|
virtual void EndLevelLoad( const char *mapString );
|
|
|
|
virtual void PrintMemInfo( MemInfo_t *mi );
|
|
|
|
virtual int IsEFXAvailable( void );
|
|
|
|
//-------------------------
|
|
|
|
int GetCurrent44kHzTime( void ) const;
|
|
float dB2Scale( const float val ) const;
|
|
int SamplesToMilliseconds( int samples ) const;
|
|
int MillisecondsToSamples( int ms ) const;
|
|
|
|
void DoEnviroSuit( float* samples, int numSamples, int numSpeakers );
|
|
|
|
ALuint AllocOpenALSource( idSoundChannel *chan, bool looping, bool stereo );
|
|
void FreeOpenALSource( ALuint handle );
|
|
|
|
// returns true if openalDevice is still available,
|
|
// otherwise it will try to recover the device and return false while it's gone
|
|
// (display audio sound devices sometimes disappear for a few seconds when switching resolution)
|
|
bool CheckDeviceAndRecoverIfNeeded();
|
|
// resets the OpenAL device, applying the settings of s_alHRTF and s_alOutputLimiter
|
|
// returns false if that failed, or the necessary OpenAL extension isn't available
|
|
bool ResetALDevice();
|
|
|
|
idSoundCache * soundCache;
|
|
|
|
idSoundWorldLocal * currentSoundWorld; // the one to mix each async tic
|
|
|
|
int olddwCurrentWritePos; // statistics
|
|
int buffers; // statistics
|
|
int CurrentSoundTime; // set by the async thread and only used by the main thread
|
|
|
|
unsigned int nextWriteBlock;
|
|
|
|
float realAccum[6*MIXBUFFER_SAMPLES+16];
|
|
float * finalMixBuffer; // points inside realAccum at a 16 byte aligned boundary
|
|
|
|
bool isInitialized;
|
|
bool muted;
|
|
bool shutdown;
|
|
|
|
s_stats soundStats; // NOTE: updated throughout the code, not displayed anywhere
|
|
|
|
int meterTops[256];
|
|
int meterTopsTime[256];
|
|
|
|
dword * graph;
|
|
|
|
float volumesDB[1200]; // dB to float volume conversion
|
|
|
|
idList<SoundFX*> fxList;
|
|
|
|
ALCdevice *openalDevice;
|
|
ALCcontext *openalContext;
|
|
ALsizei openalSourceCount;
|
|
openalSource_t openalSources[256];
|
|
|
|
LPALGENEFFECTS alGenEffects;
|
|
LPALDELETEEFFECTS alDeleteEffects;
|
|
LPALISEFFECT alIsEffect;
|
|
LPALEFFECTI alEffecti;
|
|
LPALEFFECTF alEffectf;
|
|
LPALEFFECTFV alEffectfv;
|
|
LPALGENFILTERS alGenFilters;
|
|
LPALDELETEFILTERS alDeleteFilters;
|
|
LPALISFILTER alIsFilter;
|
|
LPALFILTERI alFilteri;
|
|
LPALFILTERF alFilterf;
|
|
LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots;
|
|
LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots;
|
|
LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot;
|
|
LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti;
|
|
LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf;
|
|
|
|
idEFXFile EFXDatabase;
|
|
bool efxloaded;
|
|
// latches
|
|
static bool useEFXReverb;
|
|
// mark available during initialization, or through an explicit test
|
|
static int EFXAvailable;
|
|
|
|
static bool alHRTFavailable; // needs ALC_SOFT_HRTF extension
|
|
static bool alOutputLimiterAvailable; // needs ALC_SOFT_output_limiter extension (+ HRTF extension)
|
|
static bool alEnumerateAllAvailable; // needs ALC_ENUMERATE_ALL_EXT
|
|
static bool alIsDisconnectAvailable; // needs ALC_EXT_disconnect
|
|
static bool alOutputModeAvailable; // needs ALC_SOFT_output_mode
|
|
|
|
// DG: for CheckDeviceAndRecoverIfNeeded()
|
|
LPALCRESETDEVICESOFT alcResetDeviceSOFT; // needs ALC_SOFT_HRTF extension
|
|
int resetRetryCount;
|
|
unsigned int lastCheckTime;
|
|
|
|
static idCVar s_noSound;
|
|
static idCVar s_device;
|
|
static idCVar s_quadraticFalloff;
|
|
static idCVar s_drawSounds;
|
|
static idCVar s_minVolume6;
|
|
static idCVar s_dotbias6;
|
|
static idCVar s_minVolume2;
|
|
static idCVar s_dotbias2;
|
|
static idCVar s_spatializationDecay;
|
|
static idCVar s_showStartSound;
|
|
static idCVar s_maxSoundsPerShader;
|
|
static idCVar s_reverse;
|
|
static idCVar s_showLevelMeter;
|
|
static idCVar s_meterTopTime;
|
|
static idCVar s_volume;
|
|
static idCVar s_constantAmplitude;
|
|
static idCVar s_playDefaultSound;
|
|
static idCVar s_useOcclusion;
|
|
static idCVar s_subFraction;
|
|
static idCVar s_globalFraction;
|
|
static idCVar s_doorDistanceAdd;
|
|
static idCVar s_singleEmitter;
|
|
static idCVar s_numberOfSpeakers;
|
|
static idCVar s_force22kHz;
|
|
static idCVar s_clipVolumes;
|
|
static idCVar s_realTimeDecoding;
|
|
static idCVar s_useEAXReverb;
|
|
static idCVar s_decompressionLimit;
|
|
|
|
static idCVar s_alReverbGain;
|
|
|
|
static idCVar s_scaleDownAndClamp;
|
|
static idCVar s_alOutputLimiter;
|
|
static idCVar s_alHRTF;
|
|
|
|
static idCVar s_slowAttenuate;
|
|
|
|
static idCVar s_enviroSuitCutoffFreq;
|
|
static idCVar s_enviroSuitCutoffQ;
|
|
static idCVar s_enviroSuitSkipLowpass;
|
|
static idCVar s_enviroSuitSkipReverb;
|
|
|
|
static idCVar s_reverbTime;
|
|
static idCVar s_reverbFeedback;
|
|
static idCVar s_enviroSuitVolumeScale;
|
|
static idCVar s_skipHelltimeFX;
|
|
};
|
|
|
|
extern idSoundSystemLocal soundSystemLocal;
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
This class holds the actual wavefile bitmap, size, and info.
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
const int SCACHE_SIZE = MIXBUFFER_SAMPLES*20; // 1/2 of a second (aroundabout)
|
|
|
|
class idSoundSample {
|
|
public:
|
|
idSoundSample();
|
|
~idSoundSample();
|
|
|
|
idStr name; // name of the sample file
|
|
ID_TIME_T timestamp; // the most recent of all images used in creation, for reloadImages command
|
|
|
|
waveformatex_t objectInfo; // what are we caching
|
|
int objectSize; // size of waveform in samples, excludes the header
|
|
int objectMemSize; // object size in memory
|
|
byte * nonCacheData; // if it's not cached
|
|
byte * amplitudeData; // precomputed min,max amplitude pairs
|
|
ALuint openalBuffer; // openal buffer
|
|
bool hardwareBuffer;
|
|
bool defaultSound;
|
|
bool onDemand;
|
|
bool purged;
|
|
bool levelLoadReferenced; // so we can tell which samples aren't needed any more
|
|
|
|
int LengthIn44kHzSamples() const;
|
|
ID_TIME_T GetNewTimeStamp( void ) const;
|
|
void MakeDefault(); // turns it into a beep
|
|
void Load(); // loads the current sound based on name
|
|
void Reload( bool force ); // reloads if timestamp has changed, or always if force
|
|
void PurgeSoundSample(); // frees all data
|
|
void CheckForDownSample(); // down sample if required
|
|
bool FetchFromCache( int offset, const byte **output, int *position, int *size, const bool allowIO );
|
|
};
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
Sound sample decoder.
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
class idSampleDecoder {
|
|
public:
|
|
static void Init( void );
|
|
static void Shutdown( void );
|
|
static idSampleDecoder *Alloc( void );
|
|
static void Free( idSampleDecoder *decoder );
|
|
static int GetNumUsedBlocks( void );
|
|
static int GetUsedBlockMemory( void );
|
|
|
|
virtual ~idSampleDecoder( void ) {}
|
|
virtual void Decode( idSoundSample *sample, int sampleOffset44k, int sampleCount44k, float *dest ) = 0;
|
|
virtual void ClearDecoder( void ) = 0;
|
|
virtual idSoundSample * GetSample( void ) const = 0;
|
|
virtual int GetLastDecodeTime( void ) const = 0;
|
|
};
|
|
|
|
|
|
/*
|
|
===================================================================================
|
|
|
|
The actual sound cache.
|
|
|
|
===================================================================================
|
|
*/
|
|
|
|
class idSoundCache {
|
|
public:
|
|
idSoundCache();
|
|
~idSoundCache();
|
|
|
|
idSoundSample * FindSound( const idStr &fname, bool loadOnDemandOnly );
|
|
|
|
const int GetNumObjects( void ) { return listCache.Num(); }
|
|
const idSoundSample * GetObject( const int index ) const;
|
|
|
|
void ReloadSounds( bool force );
|
|
|
|
void BeginLevelLoad();
|
|
void EndLevelLoad();
|
|
|
|
void PrintMemInfo( MemInfo_t *mi );
|
|
|
|
private:
|
|
bool insideLevelLoad;
|
|
idList<idSoundSample*> listCache;
|
|
};
|
|
|
|
#endif /* !__SND_LOCAL_H__ */
|