/* Copyright (C) 1996-1997 Id Software, Inc. This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sound.h -- client sound i/o functions #ifndef __SOUND__ #define __SOUND__ //#define MIXER_F32 #define MAXSOUNDCHANNELS 8 //on a per device basis //pitch/rate changes require that we track stuff with subsample precision. //this can result in some awkward overflows. #define ssamplepos_t qintptr_t #define usamplepos_t quintptr_t #define PITCHSHIFT 6 /*max audio file length = ((1<<32)>>PITCHSHIFT)/KHZ*/ struct sfx_s; typedef struct { struct sfxcache_s *(QDECL *decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length); //return true when done. float (QDECL *querydata) (struct sfx_s *sfx, struct sfxcache_s *buf, char *title, size_t titlesize); //reports length + original format info without actually decoding anything. void (QDECL *ended) (struct sfx_s *sfx); //sound stopped playing and is now silent (allow rewinding or something). void (QDECL *purge) (struct sfx_s *sfx); //sound is being purged from memory. destroy everything. void *buf; } sfxdecode_t; enum { SLS_NOTLOADED, //not tried to load it SLS_LOADING, //loading it on a worker thread. SLS_LOADED, //currently in memory and usable. SLS_FAILED //already tried to load it. it won't work. not found, invalid format, etc }; typedef struct sfx_s { char name[MAX_OSPATH]; sfxdecode_t decoder; int loadstate; //no more super-spammy qboolean touched:1; //if the sound is still relevent qboolean syspath:1; //if the sound is still relevent int loopstart; //-1 or sample index to begin looping at once the sample ends } sfx_t; typedef enum { #ifdef FTE_TARGET_WEB QAF_BLOB=0, #endif QAF_S8=1, //QAF_U8=0x80|1, QAF_S16=2, //QAF_S32=4, #ifdef MIXER_F32 QAF_F32=0x80|4, #endif #define QAF_BYTES(v) (v&0x7f) //to make memory allocation easier. } qaudiofmt_t; // !!! if this is changed, it much be changed in asm_i386.h too !!! typedef struct sfxcache_s { usamplepos_t length; //sample count unsigned int speed; qaudiofmt_t format; unsigned int numchannels; usamplepos_t soundoffset; //byte index into the sound qbyte *data; // variable sized } sfxcache_t; typedef struct { int numchannels; // this many samples per frame int samples; // mono samples in buffer (individual, non grouped) int samplepos; // in mono samples int samplebytes; // per channel (NOT per frame) enum { QSF_INVALID, //not selected yet... QSF_EXTERNALMIXER, //this sample format is totally irrelevant as this device uses some sort of external mixer. QSF_U8, //FIXME: more unsigned formats need changes to S_ClearBuffer QSF_S8, //signed 8bit format is actually quite rare. QSF_S16, //normal format // QSF_X8_S24, //upper 8 bits unused. hopefully we don't need any packed thing // QSF_S32, //lower 8 bits probably unused. this makes overflow detection messy. QSF_F32, //modern mixers can use SSE/SIMD stuff, and we can skip clamping so this can be quite nippy. } sampleformat; int speed; // this many frames per second unsigned char *buffer; // pointer to mixed pcm buffer (not directly used by mixer) } dma_t; //client and server #define CF_SV_RELIABLE 1 // send reliably #define CF_NET_SENTVELOCITY CF_SV_RELIABLE #define CF_FORCELOOP 2 // forces looping. set on static sounds. #define CF_NOSPACIALISE 4 // these sounds are played at a fixed volume in both speakers, but still gets quieter with distance. //#define CF_PAUSED 8 // rate = 0. or something. #define CF_CL_ABSVOLUME 16 // ignores volume cvar (but not mastervolume). this is ignored if received from the server because there's no practical way for the server to respect the client's preferences. //#define CF_SV_RESERVED CF_CL_ABSVOLUME #define CF_NOREVERB 32 // disables reverb on this channel, if possible. #define CF_FOLLOW 64 // follows the owning entity (stops moving if we lose track) #define CF_NOREPLACE 128 // start sound event is ignored if there's already a sound playing on that entchannel (probably paired with CF_FORCELOOP). #define CF_SV_UNICAST 256 // serverside only. the sound is sent to msg_entity only. #define CF_SV_SENDVELOCITY 512 // serverside hint that velocity is important #define CF_CLI_AUTOSOUND 1024 // generated from q2 entities, which avoids breaking regular sounds, using it outside the sound system will probably break things. #define CF_CLI_INACTIVE 2048 // try to play even when inactive #ifdef Q3CLIENT #define CF_CLI_NODUPES 4096 // block multiple identical sounds being started on the same entity within rapid succession (regardless of channel). required by quake3. #endif #define CF_NETWORKED (CF_NOSPACIALISE|CF_NOREVERB|CF_FORCELOOP|CF_FOLLOW|CF_NOREPLACE) typedef struct { sfx_t *sfx; // sfx number int vol[MAXSOUNDCHANNELS]; // volume, 0.8 fixed point. ssamplepos_t pos; // sample position in sfx, <0 means delay sound start (shifted up by PITCHSHIFT) int rate; // fixed point rate scaling int flags; // cf_ flags int entnum; // to allow overriding a specific sound int entchannel; // to avoid overriding a specific sound too easily vec3_t origin; // origin of sound effect vec3_t velocity; // velocity of sound effect vec_t dist_mult; // distance multiplier (attenuation/clipK) int master_vol; // 0-255 master volume #ifdef Q3CLIENT unsigned int starttime; // start time, to replicate q3's 50ms embargo on duped sounds. #endif } channel_t; struct soundcardinfo_s; typedef struct soundcardinfo_s soundcardinfo_t; extern struct sndreverbproperties_s { int modificationcount; struct reverbproperties_s { //note: this struct originally comes from openal's eaxreverb //it is shared with gamecode float flDensity; float flDiffusion; float flGain; float flGainHF; float flGainLF; float flDecayTime; float flDecayHFRatio; float flDecayLFRatio; float flReflectionsGain; float flReflectionsDelay; float flReflectionsPan[3]; float flLateReverbGain; float flLateReverbDelay; float flLateReverbPan[3]; float flEchoTime; float flEchoDepth; float flModulationTime; float flModulationDepth; float flAirAbsorptionGainHF; float flHFReference; float flLFReference; float flRoomRolloffFactor; int iDecayHFLimit; } props; } *reverbproperties; extern size_t numreverbproperties; //reverbproperties_s presets, from efx-presets.h //mostly for testing #define REVERB_PRESET_PSYCHOTIC \ { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } //default reverb 1 #define REVERB_PRESET_UNDERWATER \ { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } void S_Init (void); void S_Startup (void); void S_EnumerateDevices(void); void S_Shutdown (qboolean final); float S_GetSoundTime(int entnum, int entchannel); float S_GetChannelLevel(int entnum, int entchannel); void S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags); float S_UpdateSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t velocity, float fvol, float attenuation, float timeofs, float pitchadj, unsigned int flags); void S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation); void S_StopSound (int entnum, int entchannel); void S_StopAllSounds(qboolean clear); void S_UpdateListener(int seat, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, size_t reverbtype, vec3_t velocity); qboolean S_UpdateReverb(size_t reverbtype, void *reverb, size_t reverbsize); void S_GetListenerInfo(int seat, float *origin, float *forward, float *right, float *up); void S_Update (void); void S_ExtraUpdate (void); void S_MixerThread(soundcardinfo_t *sc); void S_Purge(qboolean retaintouched); void S_LockMixer(void); void S_UnlockMixer(void); qboolean S_HaveOutput(void); void S_Music_Clear(sfx_t *onlyifsample); void S_Music_Seek(float time); qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration, char *title, size_t titlesize); qboolean S_Music_Playing(int musicchannel); float Media_CrossFade(int musicchanel, float vol, float time); //queries the volume we're meant to be playing (checks for fade out). -1 for no more, otherwise returns vol. sfx_t *Media_NextTrack(int musicchanel, float *time); //queries the track we're meant to be playing now. sfx_t *S_FindName (const char *name, qboolean create, qboolean syspath); sfx_t *S_PrecacheSound2 (const char *sample, qboolean syspath); #define S_PrecacheSound(s) S_PrecacheSound2(s,false) void S_UntouchAll(void); void S_ClearPrecache (void); void S_BeginPrecaching (void); void S_EndPrecaching (void); void S_PaintChannels(soundcardinfo_t *sc, int endtime); void S_InitPaintChannels (soundcardinfo_t *sc); soundcardinfo_t *S_SetupDeviceSeat(char *driver, char *device, int seat); void S_ShutdownCard (soundcardinfo_t *sc); void S_DefaultSpeakerConfiguration(soundcardinfo_t *sc); void S_ResetFailedLoad(void); #ifdef PEXT2_VOICECHAT void S_Voip_Parse(void); #endif #ifdef VOICECHAT extern cvar_t snd_voip_showmeter; void S_Voip_Transmit(unsigned char clc, sizebuf_t *buf); void S_Voip_MapChange(void); int S_Voip_Loudness(qboolean ignorevad); //-1 for not capturing, otherwise between 0 and 100 int S_Voip_ClientLoudness(unsigned int plno); qboolean S_Voip_Speaking(unsigned int plno); void S_Voip_Ignore(unsigned int plno, qboolean ignore); #else #define S_Voip_Loudness() -1 #define S_Voip_Speaking(p) false #define S_Voip_Ignore(p,s) #endif qboolean S_IsPlayingSomewhere(sfx_t *s); //qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth, int insamps, int inloopstart, qbyte *data); // picks a channel based on priorities, empty slots, number of channels channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel); void SND_ResampleStream (void *in, int inrate, qaudiofmt_t inwidth, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outwidth, int outchannels, int resampstyle); // restart entire sound subsystem (doesn't flush old sounds, so make sure that happens) void S_DoRestart (qboolean onlyifneeded); void S_Restart_f (void); //plays streaming audio void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, qaudiofmt_t width, float volume); void CLVC_Poll (void); void SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width); // ==================================================================== // User-setable variables // ==================================================================== #define MAX_DYNAMIC_CHANNELS 64 /*playing sounds (identical ones merge)*/ #define NUM_MUSICS 1 #define AMBIENT_FIRST 0 #define AMBIENT_STOP NUM_AMBIENTS #define MUSIC_FIRST AMBIENT_STOP #define MUSIC_STOP (MUSIC_FIRST + NUM_MUSICS) #define DYNAMIC_FIRST MUSIC_STOP #define DYNAMIC_STOP (DYNAMIC_FIRST + MAX_DYNAMIC_CHANNELS) // // Fake dma is a synchronous faking of the DMA progress used for // isolating performance in the renderer. The fakedma_updates is // number of times S_Update() is called per second. // extern int snd_speed; extern cvar_t snd_nominaldistance; extern cvar_t snd_loadas8bit; extern cvar_t bgmvolume; extern cvar_t volume, mastervolume; extern cvar_t snd_capture; extern cvar_t nosound; extern float voicevolumemod; extern qboolean snd_initialized; extern cvar_t snd_mixerthread; extern int snd_blocked; void S_LocalSound (const char *s); void S_LocalSound2 (const char *sound, int channel, float volume); qboolean S_LoadSound (sfx_t *s, qboolean forcedecode); typedef qboolean (QDECL *S_LoadSound_t) (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode); qboolean S_RegisterSoundInputPlugin(void *module, S_LoadSound_t loadfnc); //called to register additional sound input plugins void S_UnregisterSoundInputModule(void *module); void S_AmbientOff (void); void S_AmbientOn (void); //inititalisation functions. typedef struct { const char *name; //must be a single token, with no : qboolean (QDECL *InitCard) (soundcardinfo_t *sc, const char *cardname); //NULL for default device. qboolean (QDECL *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename)); void (QDECL *RegisterCvars) (void); } sounddriver_t; /*typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum); extern sounddriver pOPENAL_InitCard; extern sounddriver pDSOUND_InitCard; extern sounddriver pALSA_InitCard; extern sounddriver pSNDIO_InitCard; extern sounddriver pOSS_InitCard; extern sounddriver pSDL_InitCard; extern sounddriver pWAV_InitCard; extern sounddriver pAHI_InitCard; */ typedef enum { CUR_SPACIALISEONLY = 0, //for ticking over, respacialising, etc CUR_UPDATE = (1u<<1), //flags/rate/offset changed without changing the sound itself CUR_SOUNDCHANGE = (1u<<2), //the audio file changed too. reset everything. CUR_EVERYTHING = CUR_UPDATE|CUR_SOUNDCHANGE } chanupdatereason_t; struct soundcardinfo_s { //windows has one defined AFTER directsound char name[256]; //a description of the card. char guid[256]; //device name as detected (so input code can create sound devices without bugging out too much) struct soundcardinfo_s *next; int seat; //speaker orientations for spacialisation. float dist[MAXSOUNDCHANNELS]; vec3_t speakerdir[MAXSOUNDCHANNELS]; //info on which sound effects are playing //FIXME: use a linked list channel_t *channel; size_t total_chans; size_t max_chans; float ambientlevels[NUM_AMBIENTS]; //we use a float instead of the channel's int volume value to avoid framerate dependancies with slow transitions. //mixer volatile dma_t sn; //why is this volatile? qboolean inactive_sound; //continue mixing for this card even when the window isn't active. qboolean selfpainting; //allow the sound code to call the right functions when it feels the need (not properly supported). int paintedtime; //used in the mixer as last-written pos (in frames) int oldsamplepos; //this is used to track buffer wraps int buffers; //used to keep track of how many buffer wraps for consistant sound int samplequeue; //this is the number of samples the device can enqueue. if set, DMAPos returns the write point (rather than hardware read point) (in samplepairs). //callbacks void *(*Lock) (soundcardinfo_t *sc, unsigned int *startoffset); //grab a pointer to the hardware ringbuffer or whatever. startoffset is the starting offset. you can set it to 0 and bump the start offset if you need. void (*Unlock) (soundcardinfo_t *sc, void *buffer); //release the hardware ringbuffer memory void (*Submit) (soundcardinfo_t *sc, int start, int end); //if the ringbuffer is emulated, this is where you should push it to the device. void (*Shutdown) (soundcardinfo_t *sc); //kill the device unsigned int (*GetDMAPos) (soundcardinfo_t *sc); //get the current point that the hardware is reading from (the return value should not wrap, at least not very often) void (*SetEnvironmentReverb) (soundcardinfo_t *sc, size_t reverb); //if you have eax enabled, change the environment. generally this is a stub. optional. void (*Restore) (soundcardinfo_t *sc); //called before lock/unlock/lock/unlock/submit. optional void (*ChannelUpdate) (soundcardinfo_t *sc, channel_t *channel, chanupdatereason_t schanged); //properties of a sound effect changed. this is to notify hardware mixers. optional. void (*ListenerUpdate) (soundcardinfo_t *sc, int entnum, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t velocity); //player moved or something. this is to notify hardware mixers. optional. ssamplepos_t (*GetChannelPos) (soundcardinfo_t *sc, channel_t *channel); //queries a hardware mixer's channel position (essentially returns channel->pos, except more up to date) //driver-specific - if you need more stuff, you should just shove it in the handle pointer void *thread; void *handle; int snd_sent; int snd_completed; int audio_fd; }; extern soundcardinfo_t *sndcardinfo; typedef struct { int apiver; char *drivername; qboolean (QDECL *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename)); void *(QDECL *Init) (int samplerate, const char *device); /*create a new context*/ void (QDECL *Start) (void *ctx); /*begin grabbing new data, old data is potentially flushed*/ unsigned int (QDECL *Update) (void *ctx, unsigned char *buffer, unsigned int minbytes, unsigned int maxbytes); /*grab the data into a different buffer*/ void (QDECL *Stop) (void *ctx); /*stop grabbing new data, old data may remain*/ void (QDECL *Shutdown) (void *ctx); /*destroy everything*/ } snd_capture_driver_t; #endif