2004-08-23 00:15:46 +00:00
/*
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
2005-06-14 04:52:10 +00:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
2004-08-23 00:15:46 +00:00
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 .
*/
// snd_dma.c -- main control for any streaming sound output devices
# include "quakedef.h"
2012-02-27 12:23:15 +00:00
static void S_Play ( void ) ;
static void S_PlayVol ( void ) ;
static void S_SoundList_f ( void ) ;
static void S_Update_ ( soundcardinfo_t * sc ) ;
2004-08-23 00:15:46 +00:00
void S_StopAllSounds ( qboolean clear ) ;
2012-02-27 12:23:15 +00:00
static void S_StopAllSounds_f ( void ) ;
2004-08-23 00:15:46 +00:00
2012-02-27 12:23:15 +00:00
static void S_UpdateCard ( soundcardinfo_t * sc ) ;
static void S_ClearBuffer ( soundcardinfo_t * sc ) ;
2004-08-23 00:15:46 +00:00
// =======================================================================
// Internal sound data & structures
// =======================================================================
2005-06-14 04:52:10 +00:00
soundcardinfo_t * sndcardinfo ; //the master card.
2004-08-23 00:15:46 +00:00
int snd_blocked = 0 ;
static qboolean snd_ambient = 1 ;
qboolean snd_initialized = false ;
int snd_speed ;
vec3_t listener_origin ;
2010-11-28 19:14:21 +00:00
vec3_t listener_forward = { 1 , 0 , 0 } ;
vec3_t listener_right = { 0 , 1 , 0 } ;
vec3_t listener_up = { 0 , 0 , 1 } ;
2010-12-05 02:46:07 +00:00
vec3_t listener_velocity ;
2004-08-23 00:15:46 +00:00
vec_t sound_nominal_clip_dist = 1000.0 ;
# define MAX_SFX 512
sfx_t * known_sfx ; // hunk allocated [MAX_SFX]
int num_sfx ;
sfx_t * ambient_sfx [ NUM_AMBIENTS ] ;
//int desired_speed = 44100;
int desired_bits = 16 ;
int sound_started = 0 ;
2012-02-27 12:23:15 +00:00
cvar_t bgmvolume = CVARAFD ( " musicvolume " , " 0 " , " bgmvolume " , CVAR_ARCHIVE ,
2011-06-07 23:54:58 +00:00
" Volume level for background music. " ) ;
cvar_t volume = CVARFD ( " volume " , " 0.7 " , CVAR_ARCHIVE ,
" Main volume level for all engine sound. " ) ;
2010-07-11 02:22:39 +00:00
2011-12-26 15:19:13 +00:00
cvar_t nosound = CVARFD ( " nosound " , " 0 " , CVAR_ARCHIVE ,
2011-06-07 23:54:58 +00:00
" Disable all sound from the engine. " ) ;
2010-07-11 02:22:39 +00:00
cvar_t precache = CVARAF ( " s_precache " , " 1 " ,
" precache " , 0 ) ;
2011-06-07 23:54:58 +00:00
cvar_t loadas8bit = CVARAFD ( " s_loadas8bit " , " 0 " ,
2011-12-26 15:19:13 +00:00
" loadas8bit " , CVAR_ARCHIVE ,
2011-06-07 23:54:58 +00:00
" Downsample sounds on load as lower quality 8-bit sound. " ) ;
2010-07-11 02:22:39 +00:00
cvar_t ambient_level = CVARAF ( " s_ambientlevel " , " 0.3 " ,
" ambient_level " , 0 ) ;
cvar_t ambient_fade = CVARAF ( " s_ambientfade " , " 100 " ,
" ambient_fade " , 0 ) ;
cvar_t snd_noextraupdate = CVARAF ( " s_noextraupdate " , " 0 " ,
" snd_noextraupdate " , 0 ) ;
cvar_t snd_show = CVARAF ( " s_show " , " 0 " ,
" snd_show " , 0 ) ;
2012-03-19 06:30:41 +00:00
cvar_t snd_khz = CVARAFD ( " s_khz " , " 44 " ,
" snd_khz " , CVAR_ARCHIVE , " Sound speed, in kilohertz. Common values are 11, 22, 44, 48. " ) ;
2011-06-07 23:54:58 +00:00
cvar_t snd_inactive = CVARAFD ( " s_inactive " , " 0 " ,
" snd_inactive " , 0 ,
2012-03-19 06:30:41 +00:00
" Play sound while application is inactive (ex. tabbed out). Needs a snd_restart if changed. "
2011-06-07 23:54:58 +00:00
) ; //set if you want sound even when tabbed out.
2012-03-19 06:30:41 +00:00
cvar_t _snd_mixahead = CVARAFD ( " s_mixahead " , " 0.08 " ,
" _snd_mixahead " , CVAR_ARCHIVE , " Specifies how many seconds to prebuffer audio. Lower values give less latency, but might result in crackling. Different hardware/drivers have different tolerances. " ) ;
2010-07-11 02:22:39 +00:00
cvar_t snd_leftisright = CVARAF ( " s_swapstereo " , " 0 " ,
" snd_leftisright " , CVAR_ARCHIVE ) ;
cvar_t snd_eax = CVARAF ( " s_eax " , " 0 " ,
" snd_eax " , 0 ) ;
2012-03-19 06:30:41 +00:00
cvar_t snd_speakers = CVARAFD ( " s_numspeakers " , " 2 " ,
" snd_numspeakers " , 0 , " Number of hardware audio channels to use. " DISTRIBUTION " supports up to 6. " ) ;
2010-07-11 02:22:39 +00:00
cvar_t snd_buffersize = CVARAF ( " s_buffersize " , " 0 " ,
" snd_buffersize " , 0 ) ;
cvar_t snd_samplebits = CVARAF ( " s_bits " , " 16 " ,
" snd_samplebits " , CVAR_ARCHIVE ) ;
2011-06-07 23:54:58 +00:00
cvar_t snd_playersoundvolume = CVARAFD ( " s_localvolume " , " 1 " ,
" snd_localvolume " , 0 ,
" Sound level for sounds local or originating from the player such as firing and pain sounds. " ) ; //sugested by crunch
2010-07-11 02:22:39 +00:00
2012-02-27 12:23:15 +00:00
cvar_t snd_playbackrate = CVARFD ( " snd_playbackrate " , " 1 " , CVAR_CHEAT , " Debugging cvar that changes the playback rate of all new sounds. " ) ;
2010-07-11 02:22:39 +00:00
cvar_t snd_linearresample = CVARAF ( " s_linearresample " , " 1 " ,
" snd_linearresample " , 0 ) ;
cvar_t snd_linearresample_stream = CVARAF ( " s_linearresample_stream " , " 0 " ,
" snd_linearresample_stream " , 0 ) ;
2012-03-19 06:30:41 +00:00
cvar_t snd_usemultipledevices = CVARAFD ( " s_multipledevices " , " 0 " ,
" snd_multipledevices " , 0 , " If enabled, all output sound devices in your computer will be initialised for playback, not just the default device. " ) ;
2011-01-29 19:53:38 +00:00
cvar_t snd_driver = CVARAF ( " s_driver " , " " ,
" snd_driver " , 0 ) ;
2005-06-14 04:52:10 +00:00
2010-11-15 03:37:29 +00:00
# ifdef VOICECHAT
2010-11-16 02:03:47 +00:00
static void S_Voip_Play_Callback ( cvar_t * var , char * oldval ) ;
2010-11-20 22:01:16 +00:00
cvar_t cl_voip_send = CVARD ( " cl_voip_send " , " 0 " , " Sends voice-over-ip data to the server whenever it is set " ) ;
cvar_t cl_voip_vad_threshhold = CVARD ( " cl_voip_vad_threshhold " , " 15 " , " This is the threshhold for voice-activation-detection when sending voip data " ) ;
cvar_t cl_voip_vad_delay = CVARD ( " cl_voip_vad_delay " , " 0.3 " , " Keeps sending voice data for this many seconds after voice activation would normally stop " ) ;
2011-12-26 15:19:13 +00:00
cvar_t cl_voip_capturingvol = CVARAFD ( " cl_voip_capturingvol " , " 0.5 " , NULL , CVAR_ARCHIVE , " Volume multiplier applied while capturing, to avoid your audio from being heard by others " ) ;
cvar_t cl_voip_showmeter = CVARAFD ( " cl_voip_showmeter " , " 1 " , NULL , CVAR_ARCHIVE , " Shows your speech volume above the hud. 0=hide, 1=show when transmitting, 2=ignore voice-activation disable " ) ;
2010-11-20 22:01:16 +00:00
2011-12-26 15:19:13 +00:00
cvar_t cl_voip_play = CVARAFDC ( " cl_voip_play " , " 1 " , NULL , CVAR_ARCHIVE , " Enables voip playback. " , S_Voip_Play_Callback ) ;
cvar_t cl_voip_micamp = CVARAFDC ( " cl_voip_micamp " , " 2 " , NULL , CVAR_ARCHIVE , " Amplifies your microphone when using voip. " , 0 ) ;
2010-11-15 03:37:29 +00:00
# endif
2006-05-09 07:26:14 +00:00
extern vfsfile_t * rawwritefile ;
2012-02-27 12:23:15 +00:00
# ifdef MULTITHREAD
void * mixermutex ;
void S_LockMixer ( void )
{
Sys_LockMutex ( mixermutex ) ;
}
void S_UnlockMixer ( void )
{
Sys_UnlockMutex ( mixermutex ) ;
}
# else
void S_LockMixer ( void )
{
}
void S_UnlockMixer ( void )
{
}
# endif
2004-08-23 00:15:46 +00:00
void S_AmbientOff ( void )
{
snd_ambient = false ;
}
void S_AmbientOn ( void )
{
snd_ambient = true ;
}
2009-04-01 22:03:56 +00:00
qboolean S_HaveOutput ( void )
{
return sound_started & & sndcardinfo ;
}
2004-08-23 00:15:46 +00:00
void S_SoundInfo_f ( void )
{
soundcardinfo_t * sc ;
if ( ! sound_started )
{
Con_Printf ( " sound system not started \n " ) ;
return ;
}
if ( ! sndcardinfo )
{
2005-09-26 08:07:26 +00:00
Con_Printf ( " No sound cards \n " ) ;
2004-08-23 00:15:46 +00:00
return ;
}
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
{
Con_Printf ( " %5d stereo \n " , sc - > sn . numchannels - 1 ) ;
Con_Printf ( " %5d samples \n " , sc - > sn . samples ) ;
Con_Printf ( " %5d samplepos \n " , sc - > sn . samplepos ) ;
Con_Printf ( " %5d samplebits \n " , sc - > sn . samplebits ) ;
Con_Printf ( " %5d speed \n " , sc - > sn . speed ) ;
Con_Printf ( " %5d total_channels \n " , sc - > total_chans ) ;
}
}
2010-11-15 02:40:31 +00:00
# ifdef VOICECHAT
2010-11-24 02:54:28 +00:00
# include <speex.h>
# include <speex_preprocess.h>
2010-11-16 02:03:47 +00:00
static struct
2005-08-03 23:14:59 +00:00
{
2010-11-15 02:40:31 +00:00
qboolean inited ;
qboolean loaded ;
dllhandle_t * speexlib ;
dllhandle_t * speexdsplib ;
SpeexBits encbits ;
void * encoder ;
SpeexPreprocessState * preproc ;
unsigned int framesize ;
unsigned int samplerate ;
SpeexBits decbits [ MAX_CLIENTS ] ;
void * decoder [ MAX_CLIENTS ] ;
2010-11-20 22:01:16 +00:00
unsigned char decseq [ MAX_CLIENTS ] ; /*sender's sequence, to detect+cover minor packetloss*/
unsigned char decgen [ MAX_CLIENTS ] ; /*last generation. if it changes, we flush speex to reset packet loss*/
float decamp [ MAX_CLIENTS ] ; /*amplify them by this*/
float lastspoke [ MAX_CLIENTS ] ; /*time when they're no longer considered talking. if future, they're talking*/
unsigned char capturebuf [ 32768 ] ; /*pending data*/
unsigned int capturepos ; /*amount of pending data*/
unsigned int encsequence ; /*the outgoing sequence count*/
unsigned int generation ; /*incremented whenever capture is restarted*/
qboolean wantsend ; /*set if we're capturing data to send*/
float voiplevel ; /*your own voice level*/
unsigned int dumps ; /*trigger a new generation thing after a bit*/
unsigned int keeps ; /*for vad_delay*/
snd_capture_driver_t * driver ; /*capture driver's functions*/
void * driverctx ; /*capture driver context*/
2010-11-15 02:40:31 +00:00
} s_speex ;
2010-11-16 02:03:47 +00:00
static const SpeexMode * ( VARGS * qspeex_lib_get_mode ) ( int mode ) ;
static void ( VARGS * qspeex_bits_init ) ( SpeexBits * bits ) ;
static void ( VARGS * qspeex_bits_reset ) ( SpeexBits * bits ) ;
static int ( VARGS * qspeex_bits_write ) ( SpeexBits * bits , char * bytes , int max_len ) ;
2010-11-15 02:40:31 +00:00
2010-11-16 02:03:47 +00:00
static SpeexPreprocessState * ( VARGS * qspeex_preprocess_state_init ) ( int frame_size , int sampling_rate ) ;
static int ( VARGS * qspeex_preprocess_ctl ) ( SpeexPreprocessState * st , int request , void * ptr ) ;
static int ( VARGS * qspeex_preprocess_run ) ( SpeexPreprocessState * st , spx_int16_t * x ) ;
2010-11-15 02:40:31 +00:00
2010-11-16 02:03:47 +00:00
static void * ( VARGS * qspeex_encoder_init ) ( const SpeexMode * mode ) ;
static int ( VARGS * qspeex_encoder_ctl ) ( void * state , int request , void * ptr ) ;
static int ( VARGS * qspeex_encode_int ) ( void * state , spx_int16_t * in , SpeexBits * bits ) ;
2010-11-15 02:40:31 +00:00
2010-11-16 02:03:47 +00:00
static void * ( VARGS * qspeex_decoder_init ) ( const SpeexMode * mode ) ;
static int ( VARGS * qspeex_decode_int ) ( void * state , SpeexBits * bits , spx_int16_t * out ) ;
static void ( VARGS * qspeex_bits_read_from ) ( SpeexBits * bits , char * bytes , int len ) ;
2010-11-15 02:40:31 +00:00
2010-11-16 02:03:47 +00:00
static dllfunction_t qspeexfuncs [ ] =
2010-11-15 02:40:31 +00:00
{
{ ( void * ) & qspeex_lib_get_mode , " speex_lib_get_mode " } ,
{ ( void * ) & qspeex_bits_init , " speex_bits_init " } ,
{ ( void * ) & qspeex_bits_reset , " speex_bits_reset " } ,
{ ( void * ) & qspeex_bits_write , " speex_bits_write " } ,
{ ( void * ) & qspeex_encoder_init , " speex_encoder_init " } ,
{ ( void * ) & qspeex_encoder_ctl , " speex_encoder_ctl " } ,
{ ( void * ) & qspeex_encode_int , " speex_encode_int " } ,
{ ( void * ) & qspeex_decoder_init , " speex_decoder_init " } ,
{ ( void * ) & qspeex_decode_int , " speex_decode_int " } ,
{ ( void * ) & qspeex_bits_read_from , " speex_bits_read_from " } ,
{ NULL }
} ;
2010-11-16 02:03:47 +00:00
static dllfunction_t qspeexdspfuncs [ ] =
2010-11-15 02:40:31 +00:00
{
{ ( void * ) & qspeex_preprocess_state_init , " speex_preprocess_state_init " } ,
{ ( void * ) & qspeex_preprocess_ctl , " speex_preprocess_ctl " } ,
{ ( void * ) & qspeex_preprocess_run , " speex_preprocess_run " } ,
{ NULL }
} ;
2010-11-20 22:01:16 +00:00
snd_capture_driver_t DSOUND_Capture ;
2011-01-29 19:53:38 +00:00
snd_capture_driver_t OSS_Capture ;
2010-11-20 22:01:16 +00:00
2010-11-16 02:03:47 +00:00
static qboolean S_Speex_Init ( void )
2010-11-15 02:40:31 +00:00
{
int i ;
const SpeexMode * mode ;
if ( s_speex . inited )
return s_speex . loaded ;
s_speex . inited = true ;
s_speex . speexlib = Sys_LoadLibrary ( " libspeex " , qspeexfuncs ) ;
if ( ! s_speex . speexlib )
{
2011-06-05 23:53:33 +00:00
Con_Printf ( " libspeex not found. Voice chat is not available. \n " ) ;
2010-11-15 02:40:31 +00:00
return false ;
}
s_speex . speexdsplib = Sys_LoadLibrary ( " libspeexdsp " , qspeexdspfuncs ) ;
if ( ! s_speex . speexdsplib )
{
2011-06-05 23:53:33 +00:00
Con_Printf ( " libspeexdsp not found. Voice chat is not available. \n " ) ;
2010-11-15 02:40:31 +00:00
return false ;
}
mode = qspeex_lib_get_mode ( SPEEX_MODEID_NB ) ;
qspeex_bits_init ( & s_speex . encbits ) ;
qspeex_bits_reset ( & s_speex . encbits ) ;
s_speex . encoder = qspeex_encoder_init ( mode ) ;
qspeex_encoder_ctl ( s_speex . encoder , SPEEX_GET_FRAME_SIZE , & s_speex . framesize ) ;
qspeex_encoder_ctl ( s_speex . encoder , SPEEX_GET_SAMPLING_RATE , & s_speex . samplerate ) ;
2010-11-20 22:01:16 +00:00
s_speex . samplerate = 11025 ;
qspeex_encoder_ctl ( s_speex . encoder , SPEEX_SET_SAMPLING_RATE , & s_speex . samplerate ) ;
2010-11-15 02:40:31 +00:00
s_speex . preproc = qspeex_preprocess_state_init ( s_speex . framesize , s_speex . samplerate ) ;
i = 1 ;
qspeex_preprocess_ctl ( s_speex . preproc , SPEEX_PREPROCESS_SET_DENOISE , & i ) ;
i = 1 ;
qspeex_preprocess_ctl ( s_speex . preproc , SPEEX_PREPROCESS_SET_AGC , & i ) ;
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
{
qspeex_bits_init ( & s_speex . decbits [ i ] ) ;
qspeex_bits_reset ( & s_speex . decbits [ i ] ) ;
s_speex . decoder [ i ] = qspeex_decoder_init ( mode ) ;
2010-11-16 02:03:47 +00:00
s_speex . decamp [ i ] = 1 ;
2010-11-15 02:40:31 +00:00
}
s_speex . loaded = true ;
return s_speex . loaded ;
}
2010-11-20 22:01:16 +00:00
void S_Voip_Parse ( void )
2010-11-15 02:40:31 +00:00
{
2010-11-20 22:01:16 +00:00
unsigned int sender ;
2011-09-05 01:48:23 +00:00
unsigned int bytes ;
2010-11-15 02:40:31 +00:00
unsigned char data [ 1024 ] , * start ;
short decodebuf [ 1024 ] ;
unsigned int decodesamps , len , newseq , drops ;
2010-11-20 22:01:16 +00:00
unsigned char seq , gen ;
2010-11-16 02:03:47 +00:00
float amp = 1 ;
unsigned int i ;
2010-11-20 22:01:16 +00:00
sender = MSG_ReadByte ( ) ;
gen = MSG_ReadByte ( ) ;
2010-11-15 02:40:31 +00:00
seq = MSG_ReadByte ( ) ;
bytes = MSG_ReadShort ( ) ;
2010-11-20 23:47:52 +00:00
if ( bytes > sizeof ( data ) | | ! cl_voip_play . ival | | ! S_Speex_Init ( ) | | ( gen & 0xf0 ) )
2010-11-15 02:40:31 +00:00
{
MSG_ReadSkip ( bytes ) ;
return ;
}
MSG_ReadData ( data , bytes ) ;
sender & = MAX_CLIENTS - 1 ;
2010-11-16 02:03:47 +00:00
amp = s_speex . decamp [ sender ] ;
2010-11-15 02:40:31 +00:00
decodesamps = 0 ;
newseq = 0 ;
drops = 0 ;
start = data ;
2010-11-20 22:01:16 +00:00
s_speex . lastspoke [ sender ] = realtime + 0.5 ;
if ( s_speex . decgen [ sender ] ! = gen )
{
qspeex_bits_reset ( & s_speex . decbits [ sender ] ) ;
s_speex . decgen [ sender ] = gen ;
s_speex . decseq [ sender ] = seq ;
}
2010-11-15 02:40:31 +00:00
while ( bytes > 0 )
{
if ( decodesamps + s_speex . framesize > sizeof ( decodebuf ) / sizeof ( decodebuf [ 0 ] ) )
{
2010-11-20 22:01:16 +00:00
S_RawAudio ( sender , ( qbyte * ) decodebuf , s_speex . samplerate , decodesamps , 1 , 2 ) ;
2010-11-15 02:40:31 +00:00
decodesamps = 0 ;
}
if ( s_speex . decseq [ sender ] ! = seq )
{
qspeex_decode_int ( s_speex . decoder [ sender ] , NULL , decodebuf + decodesamps ) ;
s_speex . decseq [ sender ] + + ;
drops + + ;
}
else
{
bytes - - ;
len = * start + + ;
qspeex_bits_read_from ( & s_speex . decbits [ sender ] , start , len ) ;
bytes - = len ;
start + = len ;
qspeex_decode_int ( s_speex . decoder [ sender ] , & s_speex . decbits [ sender ] , decodebuf + decodesamps ) ;
newseq + + ;
}
2010-11-16 02:03:47 +00:00
if ( amp ! = 1 )
{
for ( i = decodesamps ; i < decodesamps + s_speex . framesize ; i + + )
decodebuf [ i ] * = amp ;
}
2010-11-15 02:40:31 +00:00
decodesamps + = s_speex . framesize ;
}
s_speex . decseq [ sender ] + = newseq ;
if ( drops )
2010-11-15 03:37:29 +00:00
Con_DPrintf ( " %i dropped audio frames \n " , drops ) ;
2010-11-15 02:40:31 +00:00
if ( decodesamps > 0 )
2010-11-20 22:01:16 +00:00
S_RawAudio ( sender , ( qbyte * ) decodebuf , s_speex . samplerate , decodesamps , 1 , 2 ) ;
2010-11-15 02:40:31 +00:00
}
2010-11-20 22:01:16 +00:00
void S_Voip_Transmit ( unsigned char clc , sizebuf_t * buf )
2010-11-15 02:40:31 +00:00
{
unsigned char outbuf [ 1024 ] ;
unsigned int outpos ; //in bytes
unsigned int encpos ; //in bytes
2010-11-20 22:01:16 +00:00
short * start ;
2010-11-15 02:40:31 +00:00
unsigned char initseq ; //in frames
2010-11-15 03:37:29 +00:00
unsigned int i ;
2010-11-20 22:01:16 +00:00
unsigned int samps ;
float level , f ;
2010-11-15 03:37:29 +00:00
float micamp = cl_voip_micamp . value ;
2010-11-20 22:01:16 +00:00
qboolean voipsendenable = true ;
/*if you're sending sound, you should be prepared to accept others yelling at you to shut up*/
if ( ! cl_voip_play . ival )
voipsendenable = false ;
if ( ! ( cls . fteprotocolextensions2 & PEXT2_VOICECHAT ) )
voipsendenable = false ;
2010-11-15 02:40:31 +00:00
2010-11-20 22:01:16 +00:00
if ( ! voipsendenable )
2005-08-03 23:14:59 +00:00
{
2010-11-20 22:01:16 +00:00
if ( s_speex . driver )
{
if ( s_speex . wantsend )
s_speex . driver - > Stop ( s_speex . driverctx ) ;
s_speex . driver - > Shutdown ( s_speex . driverctx ) ;
s_speex . driverctx = NULL ;
s_speex . driver = NULL ;
}
return ;
2010-11-15 02:40:31 +00:00
}
2010-11-20 22:01:16 +00:00
voipsendenable = cl_voip_send . ival > 0 ;
if ( ! s_speex . driver )
2010-11-15 02:40:31 +00:00
{
2010-11-20 22:01:16 +00:00
s_speex . voiplevel = - 1 ;
/*only init the first time capturing is requested*/
if ( ! voipsendenable )
return ;
/*Add new drivers in order of priority*/
2011-01-29 19:53:38 +00:00
if ( ! s_speex . driver | | ! s_speex . driver - > Init )
2010-11-20 22:01:16 +00:00
s_speex . driver = & DSOUND_Capture ;
2011-01-29 19:53:38 +00:00
if ( ! s_speex . driver | | ! s_speex . driver - > Init )
s_speex . driver = & OSS_Capture ;
2010-11-20 22:01:16 +00:00
/*no way to capture audio, give up*/
2011-04-30 17:21:10 +00:00
if ( ! s_speex . driver | | ! s_speex . driver - > Init )
2010-11-20 22:01:16 +00:00
return ;
/*see if we can init speex...*/
if ( ! S_Speex_Init ( ) )
return ;
s_speex . driverctx = s_speex . driver - > Init ( s_speex . samplerate ) ;
2005-08-03 23:14:59 +00:00
}
2010-11-15 02:40:31 +00:00
2010-11-20 22:01:16 +00:00
/*couldn't init a driver?*/
if ( ! s_speex . driverctx )
2010-11-16 02:03:47 +00:00
{
return ;
}
2010-11-20 22:01:16 +00:00
if ( ! voipsendenable & & s_speex . wantsend )
{
s_speex . wantsend = false ;
s_speex . capturepos + = s_speex . driver - > Update ( s_speex . driverctx , ( unsigned char * ) s_speex . capturebuf + s_speex . capturepos , 1 , sizeof ( s_speex . capturebuf ) - s_speex . capturepos ) ;
s_speex . driver - > Stop ( s_speex . driverctx ) ;
/*note: we still grab audio to flush everything that was captured while it was active*/
}
else if ( voipsendenable & & ! s_speex . wantsend )
{
s_speex . wantsend = true ;
if ( ! s_speex . capturepos )
{ /*if we were actually still sending, it was probably only off for a single frame, in which case don't reset it*/
s_speex . dumps = 0 ;
s_speex . generation + + ;
s_speex . encsequence = 0 ;
qspeex_bits_reset ( & s_speex . encbits ) ;
}
else
{
s_speex . capturepos + = s_speex . driver - > Update ( s_speex . driverctx , ( unsigned char * ) s_speex . capturebuf + s_speex . capturepos , 1 , sizeof ( s_speex . capturebuf ) - s_speex . capturepos ) ;
}
s_speex . driver - > Start ( s_speex . driverctx ) ;
voicevolumemod = cl_voip_capturingvol . value ;
}
s_speex . capturepos + = s_speex . driver - > Update ( s_speex . driverctx , ( unsigned char * ) s_speex . capturebuf + s_speex . capturepos , s_speex . framesize * 2 , sizeof ( s_speex . capturebuf ) - s_speex . capturepos ) ;
if ( ! s_speex . wantsend & & s_speex . capturepos < s_speex . framesize * 2 )
{
s_speex . voiplevel = - 1 ;
s_speex . capturepos = 0 ;
voicevolumemod = 1 ;
2010-11-15 02:40:31 +00:00
return ;
2010-11-20 22:01:16 +00:00
}
2010-11-15 02:40:31 +00:00
2010-11-20 22:01:16 +00:00
initseq = s_speex . encsequence ;
level = 0 ;
samps = 0 ;
for ( encpos = 0 , outpos = 0 ; s_speex . capturepos - encpos > = s_speex . framesize * 2 & & sizeof ( outbuf ) - outpos > 64 ; s_speex . encsequence + + )
2010-11-15 02:40:31 +00:00
{
2010-11-20 22:01:16 +00:00
start = ( short * ) ( s_speex . capturebuf + encpos ) ;
2010-11-15 02:40:31 +00:00
qspeex_preprocess_run ( s_speex . preproc , start ) ;
2010-11-20 22:01:16 +00:00
for ( i = 0 ; i < s_speex . framesize ; i + + )
2010-11-15 03:37:29 +00:00
{
2010-11-20 22:01:16 +00:00
f = start [ i ] * micamp ;
start [ i ] = f ;
f = fabs ( start [ i ] ) ;
level + = f * f ;
2010-11-15 03:37:29 +00:00
}
2010-11-20 22:01:16 +00:00
samps + = s_speex . framesize ;
2010-11-15 03:37:29 +00:00
2010-11-15 02:40:31 +00:00
qspeex_bits_reset ( & s_speex . encbits ) ;
qspeex_encode_int ( s_speex . encoder , start , & s_speex . encbits ) ;
outbuf [ outpos ] = qspeex_bits_write ( & s_speex . encbits , outbuf + outpos + 1 , sizeof ( outbuf ) - ( outpos + 1 ) ) ;
outpos + = 1 + outbuf [ outpos ] ;
encpos + = s_speex . framesize * 2 ;
2010-11-20 22:01:16 +00:00
}
if ( samps )
{
float nl ;
nl = ( 3000 * level ) / ( 32767.0f * 32767 * samps ) ;
s_speex . voiplevel = ( s_speex . voiplevel * 7 + nl ) / 8 ;
if ( s_speex . voiplevel < cl_voip_vad_threshhold . ival & & ! ( cl_voip_send . ival & 2 ) )
{
/*try and dump it, it was too quiet, and they're not pressing +voip*/
if ( s_speex . keeps > samps )
{
/*but not instantly*/
s_speex . keeps - = samps ;
}
else
{
outpos = 0 ;
s_speex . dumps + = samps ;
s_speex . keeps = 0 ;
}
}
2010-11-24 02:54:28 +00:00
else
2010-11-20 22:01:16 +00:00
s_speex . keeps = s_speex . samplerate * cl_voip_vad_delay . value ;
if ( outpos )
{
if ( s_speex . dumps > s_speex . samplerate / 4 )
s_speex . generation + + ;
s_speex . dumps = 0 ;
}
2010-11-15 02:40:31 +00:00
}
if ( outpos & & buf - > maxsize - buf - > cursize > = outpos + 4 )
{
MSG_WriteByte ( buf , clc ) ;
2010-11-20 23:47:52 +00:00
MSG_WriteByte ( buf , ( s_speex . generation & 0x0f ) ) ; /*gonna leave that nibble clear here... in this version, the client will ignore packets with those bits set. can use them for codec or something*/
2010-11-15 02:40:31 +00:00
MSG_WriteByte ( buf , initseq ) ;
MSG_WriteShort ( buf , outpos ) ;
SZ_Write ( buf , outbuf , outpos ) ;
}
/*remove sent data*/
2010-11-20 22:01:16 +00:00
memmove ( s_speex . capturebuf , s_speex . capturebuf + encpos , s_speex . capturepos - encpos ) ;
s_speex . capturepos - = encpos ;
2005-08-03 23:14:59 +00:00
}
2010-12-05 02:54:13 +00:00
void S_Voip_Ignore ( unsigned int slot , qboolean ignore )
2010-12-05 02:46:07 +00:00
{
CL_SendClientCommand ( true , " vignore %i %i " , slot , ignore ) ;
}
2010-11-16 02:03:47 +00:00
static void S_Voip_Enable_f ( void )
2010-11-15 03:37:29 +00:00
{
2010-11-20 22:01:16 +00:00
Cvar_SetValue ( & cl_voip_send , cl_voip_send . ival | 2 ) ;
2010-11-15 03:37:29 +00:00
}
2010-11-16 02:03:47 +00:00
static void S_Voip_Disable_f ( void )
2010-11-15 03:37:29 +00:00
{
2010-11-20 22:01:16 +00:00
Cvar_SetValue ( & cl_voip_send , cl_voip_send . ival & ~ 2 ) ;
2010-11-15 03:37:29 +00:00
}
2010-11-16 02:03:47 +00:00
static void S_Voip_f ( void )
{
2010-11-20 22:01:16 +00:00
int i ;
if ( ! strcmp ( Cmd_Argv ( 1 ) , " maxgain " ) )
{
i = atoi ( Cmd_Argv ( 2 ) ) ;
qspeex_preprocess_ctl ( s_speex . preproc , SPEEX_PREPROCESS_SET_AGC_MAX_GAIN , & i ) ;
}
2010-11-16 02:03:47 +00:00
}
static void S_Voip_Play_Callback ( cvar_t * var , char * oldval )
{
if ( cls . fteprotocolextensions2 & PEXT2_VOICECHAT )
{
if ( var - > ival )
CL_SendClientCommand ( true , " unmuteall " ) ;
else
CL_SendClientCommand ( true , " muteall " ) ;
}
}
void S_Voip_MapChange ( void )
{
Cvar_ForceCallback ( & cl_voip_play ) ;
}
2010-11-20 22:01:16 +00:00
int S_Voip_Loudness ( qboolean ignorevad )
{
if ( s_speex . voiplevel > 100 )
return 100 ;
if ( ! s_speex . driverctx | | ( ! ignorevad & & s_speex . dumps ) )
return - 1 ;
return s_speex . voiplevel ;
}
qboolean S_Voip_Speaking ( unsigned int plno )
{
if ( plno > = MAX_CLIENTS )
return false ;
return s_speex . lastspoke [ plno ] > realtime ;
}
2011-09-05 01:48:23 +00:00
# else
void S_Voip_Parse ( void )
{
unsigned int bytes ;
MSG_ReadByte ( ) ;
MSG_ReadByte ( ) ;
MSG_ReadByte ( ) ;
bytes = MSG_ReadShort ( ) ;
MSG_ReadSkip ( bytes ) ;
}
2010-11-15 02:40:31 +00:00
# endif
2005-08-03 23:14:59 +00:00
2004-08-23 00:15:46 +00:00
2010-03-14 14:35:56 +00:00
sounddriver pOPENAL_InitCard ;
2005-06-14 04:52:10 +00:00
sounddriver pDSOUND_InitCard ;
sounddriver pALSA_InitCard ;
sounddriver pOSS_InitCard ;
2007-09-23 22:00:19 +00:00
sounddriver pMacOS_InitCard ;
2005-06-14 04:52:10 +00:00
sounddriver pSDL_InitCard ;
sounddriver pWAV_InitCard ;
2011-09-22 15:57:16 +00:00
sounddriver pDroid_InitCard ;
2005-11-26 03:02:55 +00:00
sounddriver pAHI_InitCard ;
2012-04-09 19:12:12 +00:00
sounddriver pPPAPI_InitCard ;
2005-11-26 03:02:55 +00:00
typedef struct {
char * name ;
sounddriver * ptr ;
} sdriver_t ;
sdriver_t drivers [ ] = {
//in order of preference
2010-12-05 02:46:07 +00:00
{ " OpenAL " , & pOPENAL_InitCard } , //yay, get someone else to sort out sound support, woot
2010-11-28 19:14:21 +00:00
2010-03-14 14:35:56 +00:00
{ " DSound " , & pDSOUND_InitCard } , //prefered on windows
{ " MacOS " , & pMacOS_InitCard } , //prefered on mac
2011-09-22 15:57:16 +00:00
{ " Droid " , & pDroid_InitCard } , //prefered on android (java thread)
2010-03-14 14:35:56 +00:00
{ " AHI " , & pAHI_InitCard } , //prefered on morphos
2012-04-09 19:12:12 +00:00
{ " PPAPI " , & pPPAPI_InitCard } , //google's native client
2010-03-14 14:35:56 +00:00
{ " SDL " , & pSDL_InitCard } , //prefered on linux
{ " ALSA " , & pALSA_InitCard } , //pure shite
{ " OSS " , & pOSS_InitCard } , //good, but not likely to work any more
{ " WaveOut " , & pWAV_InitCard } , //doesn't work properly in vista, etc.
2005-11-26 03:02:55 +00:00
{ NULL , NULL }
2005-06-14 04:52:10 +00:00
} ;
static int SNDDMA_Init ( soundcardinfo_t * sc , int * cardnum , int * drivernum )
{
2005-11-26 03:02:55 +00:00
sdriver_t * sd ;
2005-06-14 04:52:10 +00:00
int st = 0 ;
memset ( sc , 0 , sizeof ( * sc ) ) ;
2006-05-10 05:18:08 +00:00
// set requested rate
2011-09-03 03:49:43 +00:00
if ( snd_khz . ival > = 1000 )
sc - > sn . speed = snd_khz . ival ;
else if ( snd_khz . ival < = 0 )
2006-05-09 20:43:39 +00:00
sc - > sn . speed = 22050 ;
2009-11-04 21:16:50 +00:00
/* else if (snd_khz.ival >= 195)
2006-05-09 20:43:39 +00:00
sc - > sn . speed = 200000 ;
2009-11-04 21:16:50 +00:00
else if ( snd_khz . ival > = 180 )
2006-05-09 20:43:39 +00:00
sc - > sn . speed = 192000 ;
2009-11-04 21:16:50 +00:00
else if ( snd_khz . ival > = 90 )
2006-05-09 20:43:39 +00:00
sc - > sn . speed = 96000 ; */
2009-11-04 21:16:50 +00:00
else if ( snd_khz . ival > = 45 )
2005-06-14 04:52:10 +00:00
sc - > sn . speed = 48000 ;
2009-11-04 21:16:50 +00:00
else if ( snd_khz . ival > = 30 )
2005-06-14 04:52:10 +00:00
sc - > sn . speed = 44100 ;
2009-11-04 21:16:50 +00:00
else if ( snd_khz . ival > = 20 )
2005-06-14 04:52:10 +00:00
sc - > sn . speed = 22050 ;
2009-11-04 21:16:50 +00:00
else if ( snd_khz . ival > = 10 )
2005-06-14 04:52:10 +00:00
sc - > sn . speed = 11025 ;
2006-05-09 20:43:39 +00:00
else
sc - > sn . speed = 8000 ;
2005-06-14 04:52:10 +00:00
2006-05-10 05:18:08 +00:00
// set requested speaker count
2009-11-04 21:16:50 +00:00
if ( snd_speakers . ival > MAXSOUNDCHANNELS )
2006-05-10 07:35:19 +00:00
sc - > sn . numchannels = MAXSOUNDCHANNELS ;
2009-11-04 21:16:50 +00:00
else if ( snd_speakers . ival > 1 )
sc - > sn . numchannels = ( int ) snd_speakers . ival ;
2006-05-10 05:18:08 +00:00
else
sc - > sn . numchannels = 1 ;
// set requested sample bits
2009-11-04 21:16:50 +00:00
if ( snd_samplebits . ival > = 16 )
2006-05-10 05:18:08 +00:00
sc - > sn . samplebits = 16 ;
else
sc - > sn . samplebits = 8 ;
// set requested buffer size
2009-11-04 21:16:50 +00:00
if ( snd_buffersize . ival > 0 )
sc - > sn . samples = snd_buffersize . ival * sc - > sn . numchannels ;
2006-05-10 05:18:08 +00:00
else
sc - > sn . samples = 0 ;
2011-01-29 19:53:38 +00:00
if ( * snd_driver . string )
{
if ( * drivernum )
return 2 ;
for ( sd = drivers ; sd - > name ; sd + + )
if ( ! Q_strcasecmp ( sd - > name , snd_driver . string ) )
break ;
}
else
sd = & drivers [ * drivernum ] ;
2005-11-26 03:02:55 +00:00
if ( ! sd - > ptr )
2005-06-14 04:52:10 +00:00
return 2 ; //no more cards.
2005-11-26 03:02:55 +00:00
if ( ! * sd - > ptr ) //driver not loaded
{
Con_DPrintf ( " Sound driver %s is not loaded \n " , sd - > name ) ;
2005-06-14 04:52:10 +00:00
st = 2 ;
2005-11-26 03:02:55 +00:00
}
2005-06-14 04:52:10 +00:00
else
2005-11-26 03:02:55 +00:00
{
Con_DPrintf ( " Trying to load a %s sound device \n " , sd - > name ) ;
st = ( * * sd - > ptr ) ( sc , * cardnum ) ;
}
2005-06-14 04:52:10 +00:00
if ( st = = 1 ) //worked
{
* cardnum + = 1 ; //use the next card next time
return st ;
}
else if ( st = = 0 ) //failed, try the next card with this driver.
{
* cardnum + = 1 ;
return SNDDMA_Init ( sc , cardnum , drivernum ) ;
}
else //card number wasn't valid, try the first card of the next driver
{
* cardnum = 0 ;
* drivernum + = 1 ;
return SNDDMA_Init ( sc , cardnum , drivernum ) ;
}
}
2006-09-17 00:59:22 +00:00
void S_DefaultSpeakerConfiguration ( soundcardinfo_t * sc )
{
2010-11-06 23:05:29 +00:00
sc - > dist [ 0 ] = 1 ;
sc - > dist [ 1 ] = 1 ;
sc - > dist [ 2 ] = 1 ;
sc - > dist [ 3 ] = 1 ;
sc - > dist [ 4 ] = 1 ;
sc - > dist [ 5 ] = 1 ;
2011-07-06 01:40:40 +00:00
switch ( sc - > sn . numchannels )
2006-09-17 00:59:22 +00:00
{
2011-07-06 01:40:40 +00:00
case 1 :
VectorSet ( sc - > speakerdir [ 0 ] , 0 , 0 , 0 ) ;
break ;
case 2 :
case 3 :
2010-11-29 03:42:15 +00:00
VectorSet ( sc - > speakerdir [ 0 ] , 0 , - 1 , 0 ) ;
VectorSet ( sc - > speakerdir [ 1 ] , 0 , 1 , 0 ) ;
2011-07-06 01:40:40 +00:00
VectorSet ( sc - > speakerdir [ 2 ] , 0 , 0 , 0 ) ;
break ;
case 4 : // quad
case 5 :
2010-11-06 23:05:29 +00:00
VectorSet ( sc - > speakerdir [ 0 ] , 0.7 , - 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 1 ] , 0.7 , 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 2 ] , - 0.7 , - 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 3 ] , - 0.7 , 0.7 , 0 ) ;
2011-07-06 01:40:40 +00:00
VectorSet ( sc - > speakerdir [ 4 ] , 0 , 0 , 0 ) ;
break ;
case 6 : // 5.1
case 7 :
2010-11-06 23:05:29 +00:00
VectorSet ( sc - > speakerdir [ 0 ] , 0.7 , - 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 1 ] , 0.7 , 0.7 , 0 ) ;
2011-07-06 01:40:40 +00:00
VectorSet ( sc - > speakerdir [ 2 ] , 1 , 0 , 0 ) ;
VectorSet ( sc - > speakerdir [ 3 ] , 0 , 0 , 0 ) ;
VectorSet ( sc - > speakerdir [ 4 ] , - 0.7 , - 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 5 ] , - 0.7 , 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 6 ] , 0 , 0 , 0 ) ;
break ;
case 8 : // 7.1
default :
VectorSet ( sc - > speakerdir [ 0 ] , 0.7 , - 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 1 ] , 0.7 , 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 2 ] , 1 , 0 , 0 ) ;
2010-11-06 23:05:29 +00:00
VectorSet ( sc - > speakerdir [ 3 ] , 0 , 0 , 0 ) ;
VectorSet ( sc - > speakerdir [ 4 ] , - 0.7 , - 0.7 , 0 ) ;
VectorSet ( sc - > speakerdir [ 5 ] , - 0.7 , 0.7 , 0 ) ;
2011-07-06 01:40:40 +00:00
VectorSet ( sc - > speakerdir [ 6 ] , 0 , - 1 , 0 ) ;
VectorSet ( sc - > speakerdir [ 7 ] , 0 , 1 , 0 ) ;
break ;
2006-09-17 00:59:22 +00:00
}
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = =
S_Startup
= = = = = = = = = = = = = = = =
*/
2005-07-14 01:57:34 +00:00
void S_ClearRaw ( void ) ;
2004-08-23 00:15:46 +00:00
void S_Startup ( void )
{
2005-06-14 04:52:10 +00:00
int cardnum , drivernum ;
2004-08-23 00:15:46 +00:00
int warningmessage = 0 ;
int rc ;
soundcardinfo_t * sc ;
if ( ! snd_initialized )
return ;
if ( sound_started )
S_Shutdown ( ) ;
2005-02-06 02:47:36 +00:00
snd_blocked = 0 ;
2006-06-02 17:42:36 +00:00
snd_speed = 0 ;
2005-02-06 02:47:36 +00:00
2006-09-17 00:59:22 +00:00
for ( cardnum = 0 , drivernum = 0 ; ; )
2004-08-23 00:15:46 +00:00
{
2006-09-17 00:59:22 +00:00
sc = Z_Malloc ( sizeof ( soundcardinfo_t ) ) ;
rc = SNDDMA_Init ( sc , & cardnum , & drivernum ) ;
if ( ! rc ) //error stop
2004-08-23 00:15:46 +00:00
{
2006-09-17 00:59:22 +00:00
Con_Printf ( " S_Startup: SNDDMA_Init failed. \n " ) ;
Z_Free ( sc ) ;
break ;
}
if ( rc = = 2 ) //silently stop (no more cards)
{
Z_Free ( sc ) ;
break ;
}
2004-08-23 00:15:46 +00:00
2006-09-17 00:59:22 +00:00
S_DefaultSpeakerConfiguration ( sc ) ;
2004-08-23 00:15:46 +00:00
2006-09-17 00:59:22 +00:00
if ( sndcardinfo )
{ //if the sample speeds of multiple soundcards do not match, it'll fail.
if ( snd_speed ! = sc - > sn . speed )
2004-08-23 00:15:46 +00:00
{
2006-09-17 00:59:22 +00:00
if ( ! warningmessage )
2004-08-23 00:15:46 +00:00
{
2006-09-17 00:59:22 +00:00
Con_Printf ( " S_Startup: Ignoring soundcard %s due to mismatched sample speeds. \n Try running Quake with -singlesound to use just the primary soundcard \n " , sc - > name ) ;
S_ShutdownCard ( sc ) ;
warningmessage = true ;
2004-08-23 00:15:46 +00:00
}
2006-09-17 00:59:22 +00:00
Z_Free ( sc ) ;
continue ;
2004-08-23 00:15:46 +00:00
}
2006-09-17 00:59:22 +00:00
}
else
snd_speed = sc - > sn . speed ;
2004-08-23 00:15:46 +00:00
2006-09-17 00:59:22 +00:00
sc - > next = sndcardinfo ;
sndcardinfo = sc ;
2005-06-14 04:52:10 +00:00
2009-11-04 21:16:50 +00:00
if ( ! snd_usemultipledevices . ival )
2006-09-17 00:59:22 +00:00
break ;
2004-08-23 00:15:46 +00:00
}
2009-04-01 22:03:56 +00:00
sound_started = ! ! sndcardinfo ;
2005-07-14 01:57:34 +00:00
S_ClearRaw ( ) ;
2004-08-23 00:15:46 +00:00
}
2005-06-14 04:52:10 +00:00
void SNDDMA_SetUnderWater ( qboolean underwater )
{
soundcardinfo_t * sc ;
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
sc - > SetWaterDistortion ( sc , underwater ) ;
}
2005-06-18 23:52:42 +00:00
//why isn't this part of S_Restart_f anymore?
//so that the video code can call it directly without flushing the models it's just loaded.
void S_DoRestart ( void )
2005-06-14 04:52:10 +00:00
{
2012-02-27 12:23:15 +00:00
S_StopAllSounds ( true ) ;
S_Shutdown ( ) ;
2009-11-04 21:16:50 +00:00
if ( nosound . ival )
2004-12-02 07:09:14 +00:00
return ;
2004-08-23 00:15:46 +00:00
S_Startup ( ) ;
ambient_sfx [ AMBIENT_WATER ] = S_PrecacheSound ( " ambience/water1.wav " ) ;
2005-06-14 04:52:10 +00:00
ambient_sfx [ AMBIENT_SKY ] = S_PrecacheSound ( " ambience/wind2.wav " ) ;
2004-08-23 00:15:46 +00:00
S_StopAllSounds ( true ) ;
}
2005-06-18 23:52:42 +00:00
void S_Restart_f ( void )
{
S_DoRestart ( ) ;
}
2004-08-23 00:15:46 +00:00
void S_Control_f ( void )
2005-06-14 04:52:10 +00:00
{
2004-08-23 00:15:46 +00:00
int i ;
char * command ;
command = Cmd_Argv ( 1 ) ;
if ( ! Q_strcasecmp ( command , " off " ) )
{
Cache_Flush ( ) ; //forget the old sounds.
2005-06-14 04:52:10 +00:00
S_StopAllSounds ( true ) ;
2004-08-23 00:15:46 +00:00
S_Shutdown ( ) ;
sound_started = 0 ;
}
else if ( ! Q_strcasecmp ( command , " multi " ) | | ! Q_strcasecmp ( command , " multiple " ) )
{
if ( ! Q_strcasecmp ( Cmd_Argv ( 2 ) , " off " ) )
{
2009-11-04 21:16:50 +00:00
if ( snd_usemultipledevices . ival )
2004-08-23 00:15:46 +00:00
{
2005-06-14 04:52:10 +00:00
Cvar_SetValue ( & snd_usemultipledevices , 0 ) ;
S_Restart_f ( ) ;
}
2004-08-23 00:15:46 +00:00
}
2009-11-04 21:16:50 +00:00
else if ( ! snd_usemultipledevices . ival )
2004-08-23 00:15:46 +00:00
{
2005-06-14 04:52:10 +00:00
Cvar_SetValue ( & snd_usemultipledevices , 1 ) ;
2004-08-23 00:15:46 +00:00
S_Restart_f ( ) ;
}
return ;
}
if ( ! Q_strcasecmp ( command , " single " ) )
{
2005-06-14 04:52:10 +00:00
Cvar_SetValue ( & snd_usemultipledevices , 0 ) ;
2004-08-23 00:15:46 +00:00
S_Restart_f ( ) ;
return ;
}
if ( ! Q_strcasecmp ( command , " rate " ) | | ! Q_strcasecmp ( command , " speed " ) )
{
2005-06-14 04:52:10 +00:00
Cvar_SetValue ( & snd_khz , atof ( Cmd_Argv ( 2 ) ) / 1000 ) ;
2004-08-23 00:15:46 +00:00
S_Restart_f ( ) ;
return ;
}
//individual device control
if ( ! Q_strncasecmp ( command , " card " , 4 ) )
{
int card ;
soundcardinfo_t * sc ;
card = atoi ( command + 4 ) ;
2005-01-13 16:29:20 +00:00
for ( i = 0 , sc = sndcardinfo ; i < card & & sc ; i + + , sc = sc - > next )
;
2004-08-23 00:15:46 +00:00
if ( ! sc )
{
Con_Printf ( " Sound card %i is invalid (try resetting first) \n " , card ) ;
return ;
}
if ( Cmd_Argc ( ) < 3 )
{
Con_Printf ( " Scard %i is %s \n " , card , sc - > name ) ;
return ;
}
command = Cmd_Argv ( 2 ) ;
if ( ! Q_strcasecmp ( command , " mono " ) )
{
for ( i = 0 ; i < MAXSOUNDCHANNELS ; i + + )
{
2010-11-06 23:05:29 +00:00
VectorSet ( sc - > speakerdir [ i ] , 0 , 0 , 0 ) ;
2004-08-23 00:15:46 +00:00
sc - > dist [ i ] = 1 ;
}
}
else if ( ! Q_strcasecmp ( command , " standard " ) | | ! Q_strcasecmp ( command , " stereo " ) )
{
for ( i = 0 ; i < MAXSOUNDCHANNELS ; i + + )
{
2010-11-29 03:42:15 +00:00
VectorSet ( sc - > speakerdir [ i ] , 0 , ( i & 1 ) ? 1 : - 1 , 0 ) ;
2004-08-23 00:15:46 +00:00
sc - > dist [ i ] = 1 ;
}
}
else if ( ! Q_strcasecmp ( command , " swap " ) )
{
for ( i = 0 ; i < MAXSOUNDCHANNELS ; i + + )
{
2010-11-06 23:05:29 +00:00
sc - > speakerdir [ i ] [ 1 ] * = - 1 ;
2004-08-23 00:15:46 +00:00
}
}
else if ( ! Q_strcasecmp ( command , " front " ) )
{
for ( i = 0 ; i < MAXSOUNDCHANNELS ; i + + )
{
2010-11-06 23:05:29 +00:00
VectorSet ( sc - > speakerdir [ i ] , 0.7 , ( i & 1 ) ? - 0.7 : 0.7 , 0 ) ;
2004-08-23 00:15:46 +00:00
sc - > dist [ i ] = 1 ;
}
}
else if ( ! Q_strcasecmp ( command , " back " ) )
{
for ( i = 0 ; i < MAXSOUNDCHANNELS ; i + + )
{
2010-11-06 23:05:29 +00:00
VectorSet ( sc - > speakerdir [ i ] , - 0.7 , ( i & 1 ) ? - 0.7 : 0.7 , 0 ) ;
2004-08-23 00:15:46 +00:00
sc - > dist [ i ] = 1 ;
}
}
return ;
}
2010-11-06 23:05:29 +00:00
else
Con_Printf ( " valid commands are: off, single, multi, cardX mono, cardX stereo, cardX front, cardX back \n " ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = =
S_Init
= = = = = = = = = = = = = = = =
*/
void S_Init ( void )
{
int p ;
2005-05-08 06:03:15 +00:00
Con_DPrintf ( " \n Sound Initialization \n " ) ;
2004-08-23 00:15:46 +00:00
Cmd_AddCommand ( " play " , S_Play ) ;
2005-07-08 00:37:52 +00:00
Cmd_AddCommand ( " play2 " , S_Play ) ;
2004-08-23 00:15:46 +00:00
Cmd_AddCommand ( " playvol " , S_PlayVol ) ;
2012-02-27 12:23:15 +00:00
Cmd_AddCommand ( " stopsound " , S_StopAllSounds_f ) ;
Cmd_AddCommand ( " soundlist " , S_SoundList_f ) ;
2004-08-23 00:15:46 +00:00
Cmd_AddCommand ( " soundinfo " , S_SoundInfo_f ) ;
Cmd_AddCommand ( " snd_restart " , S_Restart_f ) ;
Cmd_AddCommand ( " soundcontrol " , S_Control_f ) ;
Cvar_Register ( & nosound , " Sound controls " ) ;
Cvar_Register ( & volume , " Sound controls " ) ;
Cvar_Register ( & precache , " Sound controls " ) ;
Cvar_Register ( & loadas8bit , " Sound controls " ) ;
Cvar_Register ( & bgmvolume , " Sound controls " ) ;
Cvar_Register ( & ambient_level , " Sound controls " ) ;
Cvar_Register ( & ambient_fade , " Sound controls " ) ;
Cvar_Register ( & snd_noextraupdate , " Sound controls " ) ;
Cvar_Register ( & snd_show , " Sound controls " ) ;
Cvar_Register ( & _snd_mixahead , " Sound controls " ) ;
Cvar_Register ( & snd_khz , " Sound controls " ) ;
Cvar_Register ( & snd_leftisright , " Sound controls " ) ;
Cvar_Register ( & snd_eax , " Sound controls " ) ;
Cvar_Register ( & snd_speakers , " Sound controls " ) ;
2006-05-10 05:18:08 +00:00
Cvar_Register ( & snd_buffersize , " Sound controls " ) ;
Cvar_Register ( & snd_samplebits , " Sound controls " ) ;
2012-02-27 12:23:15 +00:00
Cvar_Register ( & snd_playbackrate , " Sound controls " ) ;
2004-08-23 00:15:46 +00:00
2010-11-15 03:37:29 +00:00
# ifdef VOICECHAT
Cvar_Register ( & cl_voip_send , " Voice Chat " ) ;
2010-11-20 22:01:16 +00:00
Cvar_Register ( & cl_voip_vad_threshhold , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_vad_delay , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_capturingvol , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_showmeter , " Voice Chat " ) ;
2010-11-15 03:37:29 +00:00
Cvar_Register ( & cl_voip_play , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_micamp , " Voice Chat " ) ;
Cmd_AddCommand ( " +voip " , S_Voip_Enable_f ) ;
Cmd_AddCommand ( " -voip " , S_Voip_Disable_f ) ;
2010-11-20 22:01:16 +00:00
Cmd_AddCommand ( " voip " , S_Voip_f ) ;
2010-11-15 03:37:29 +00:00
# endif
2004-08-23 00:15:46 +00:00
2004-10-03 22:52:02 +00:00
Cvar_Register ( & snd_inactive , " Sound controls " ) ;
2005-05-26 12:55:34 +00:00
Cvar_Register ( & snd_playersoundvolume , " Sound controls " ) ;
2005-06-14 04:52:10 +00:00
Cvar_Register ( & snd_usemultipledevices , " Sound controls " ) ;
2011-01-29 19:53:38 +00:00
Cvar_Register ( & snd_driver , " Sound controls " ) ;
2005-06-14 04:52:10 +00:00
2006-05-08 06:44:47 +00:00
Cvar_Register ( & snd_linearresample , " Sound controls " ) ;
2006-06-04 01:43:52 +00:00
Cvar_Register ( & snd_linearresample_stream , " Sound controls " ) ;
2006-05-08 06:44:47 +00:00
2012-02-27 12:23:15 +00:00
# ifdef MULTITHREAD
mixermutex = Sys_CreateMutex ( ) ;
# endif
2010-03-14 14:35:56 +00:00
# ifdef AVAIL_OPENAL
OpenAL_CvarInit ( ) ;
# endif
2005-08-12 17:57:22 +00:00
if ( COM_CheckParm ( " -nosound " ) )
{
Cvar_ForceSet ( & nosound , " 1 " ) ;
nosound . flags | = CVAR_NOSET ;
return ;
}
p = COM_CheckParm ( " -soundspeed " ) ;
2011-09-03 03:49:43 +00:00
if ( ! p )
p = COM_CheckParm ( " -sspeed " ) ;
if ( ! p )
p = COM_CheckParm ( " -sndspeed " ) ;
2005-08-12 17:57:22 +00:00
if ( p )
{
if ( p < com_argc - 1 )
2011-09-03 03:49:43 +00:00
Cvar_SetValue ( & snd_khz , atof ( com_argv [ p + 1 ] ) ) ;
2005-08-12 17:57:22 +00:00
else
Sys_Error ( " S_Init: you must specify a speed in KB after -soundspeed " ) ;
}
2005-06-14 04:52:10 +00:00
if ( COM_CheckParm ( " -nomultipledevices " ) | | COM_CheckParm ( " -singlesound " ) )
Cvar_SetValue ( & snd_usemultipledevices , 0 ) ;
if ( COM_CheckParm ( " -multisound " ) )
Cvar_SetValue ( & snd_usemultipledevices , 1 ) ;
2005-05-26 12:55:34 +00:00
2004-08-23 00:15:46 +00:00
if ( host_parms . memsize < 0x800000 )
{
Cvar_Set ( & loadas8bit , " 1 " ) ;
Con_Printf ( " loading all sounds as 8bit \n " ) ;
}
snd_initialized = true ;
2011-04-30 17:21:10 +00:00
known_sfx = Hunk_AllocName ( MAX_SFX * sizeof ( sfx_t ) , " sfx_t " ) ;
2004-08-23 00:15:46 +00:00
num_sfx = 0 ;
// create a piece of DMA memory
if ( sndcardinfo )
Con_SafePrintf ( " Sound sampling rate: %i \n " , sndcardinfo - > sn . speed ) ;
ambient_sfx [ AMBIENT_WATER ] = S_PrecacheSound ( " ambience/water1.wav " ) ;
ambient_sfx [ AMBIENT_SKY ] = S_PrecacheSound ( " ambience/wind2.wav " ) ;
S_StopAllSounds ( true ) ;
}
// =======================================================================
// Shutdown sound engine
// =======================================================================
void S_ShutdownCard ( soundcardinfo_t * sc )
{
2006-09-17 00:59:22 +00:00
soundcardinfo_t * prev ;
2009-03-07 04:37:24 +00:00
# if defined(_WIN32) && defined(AVAIL_DSOUND)
2005-06-04 04:20:20 +00:00
extern int aimedforguid ;
aimedforguid = 0 ;
# endif
2006-09-17 00:59:22 +00:00
if ( sndcardinfo = = sc )
sndcardinfo = sc - > next ;
else
2005-06-14 04:52:10 +00:00
{
2006-09-17 00:59:22 +00:00
for ( prev = sndcardinfo ; prev - > next ; prev = prev - > next )
{
if ( prev - > next = = sc )
prev - > next = sc - > next ;
}
2005-06-14 04:52:10 +00:00
}
2006-09-17 00:59:22 +00:00
sc - > Shutdown ( sc ) ;
Z_Free ( sc ) ;
2004-08-23 00:15:46 +00:00
}
void S_Shutdown ( void )
2005-06-14 04:52:10 +00:00
{
2004-08-23 00:15:46 +00:00
soundcardinfo_t * sc , * next ;
2009-03-07 04:37:24 +00:00
# if defined(_WIN32) && defined(AVAIL_DSOUND)
2006-09-17 00:59:22 +00:00
extern int aimedforguid ;
aimedforguid = 0 ;
# endif
2004-08-23 00:15:46 +00:00
for ( sc = sndcardinfo ; sc ; sc = next )
{
next = sc - > next ;
2006-09-17 00:59:22 +00:00
sc - > Shutdown ( sc ) ;
2004-08-23 00:15:46 +00:00
Z_Free ( sc ) ;
sndcardinfo = next ;
}
sound_started = 0 ;
2012-02-27 12:23:15 +00:00
S_Purge ( false ) ;
2012-05-09 15:30:53 +00:00
num_sfx = 0 ;
2004-08-23 00:15:46 +00:00
}
// =======================================================================
// Load a sound
// =======================================================================
/*
= = = = = = = = = = = = = = = = = =
S_FindName
2012-02-27 12:23:15 +00:00
also touches it
2004-08-23 00:15:46 +00:00
= = = = = = = = = = = = = = = = = =
*/
sfx_t * S_FindName ( char * name )
{
int i ;
sfx_t * sfx ;
if ( ! name )
Sys_Error ( " S_FindName: NULL \n " ) ;
if ( Q_strlen ( name ) > = MAX_OSPATH )
Sys_Error ( " Sound name too long: %s " , name ) ;
// see if already loaded
for ( i = 0 ; i < num_sfx ; i + + )
if ( ! Q_strcmp ( known_sfx [ i ] . name , name ) )
{
2012-02-27 12:23:15 +00:00
known_sfx [ i ] . touched = true ;
2004-08-23 00:15:46 +00:00
return & known_sfx [ i ] ;
}
if ( num_sfx = = MAX_SFX )
Sys_Error ( " S_FindName: out of sfx_t " ) ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
sfx = & known_sfx [ i ] ;
strcpy ( sfx - > name , name ) ;
2012-02-27 12:23:15 +00:00
known_sfx [ i ] . touched = true ;
2004-08-23 00:15:46 +00:00
num_sfx + + ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
return sfx ;
}
2012-02-27 12:23:15 +00:00
void S_Purge ( qboolean retaintouched )
{
sfx_t * sfx ;
int i ;
S_LockMixer ( ) ;
for ( i = 0 ; i < num_sfx ; i + + )
{
sfx = & known_sfx [ i ] ;
/*don't purge the file if its still relevent*/
if ( retaintouched & & sfx - > touched )
continue ;
/*nothing to do if there's no data within*/
if ( ! sfx - > decoder . buf )
continue ;
/*stop the decoder first*/
if ( sfx - > decoder . abort )
sfx - > decoder . abort ( sfx ) ;
/*if there's any data associated still, kill it. if present, it should be a single sfxcache_t (with data in same alloc)*/
if ( sfx - > decoder . buf )
BZ_Free ( sfx - > decoder . buf ) ;
2012-05-09 15:30:53 +00:00
memset ( & sfx - > decoder , 0 , sizeof ( sfx - > decoder ) ) ;
2012-02-27 12:23:15 +00:00
}
S_UnlockMixer ( ) ;
}
2006-10-05 22:11:17 +00:00
void S_ResetFailedLoad ( void )
{
int i ;
for ( i = 0 ; i < num_sfx ; i + + )
known_sfx [ i ] . failedload = false ;
}
2012-02-27 12:23:15 +00:00
void S_UntouchAll ( void )
{
int i ;
for ( i = 0 ; i < num_sfx ; i + + )
known_sfx [ i ] . touched = false ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = =
S_TouchSound
= = = = = = = = = = = = = = = = = =
*/
void S_TouchSound ( char * name )
{
if ( ! sound_started )
return ;
2012-02-27 12:23:15 +00:00
S_FindName ( name ) ;
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = = =
S_PrecacheSound
= = = = = = = = = = = = = = = = = =
*/
sfx_t * S_PrecacheSound ( char * name )
{
sfx_t * sfx ;
2009-11-04 21:16:50 +00:00
if ( nosound . ival )
2004-08-23 00:15:46 +00:00
return NULL ;
sfx = S_FindName ( name ) ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
// cache it in
2009-11-04 21:16:50 +00:00
if ( precache . ival & & sndcardinfo )
2004-08-23 00:15:46 +00:00
S_LoadSound ( sfx ) ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
return sfx ;
}
//=============================================================================
/*
= = = = = = = = = = = = = = = = =
SND_PickChannel
= = = = = = = = = = = = = = = = =
*/
channel_t * SND_PickChannel ( soundcardinfo_t * sc , int entnum , int entchannel )
{
int ch_idx ;
2012-02-27 12:23:15 +00:00
int oldestpos ;
int oldest ;
2004-08-23 00:15:46 +00:00
// Check for replacement sound, or find the best one to replace
2012-02-27 12:23:15 +00:00
oldest = - 1 ;
oldestpos = - 1 ;
2010-11-06 23:05:29 +00:00
for ( ch_idx = DYNAMIC_FIRST ; ch_idx < DYNAMIC_STOP ; ch_idx + + )
2004-08-23 00:15:46 +00:00
{
if ( entchannel ! = 0 // channel 0 never overrides
& & sc - > channel [ ch_idx ] . entnum = = entnum
2011-07-22 20:26:22 +00:00
& & ( sc - > channel [ ch_idx ] . entchannel = = entchannel | | entchannel = = - 1 ) )
2005-07-28 15:33:27 +00:00
{ // always override sound from same entity
2012-02-27 12:23:15 +00:00
oldest = ch_idx ;
2004-08-23 00:15:46 +00:00
break ;
}
// don't let monster sounds override player sounds
2004-10-03 22:52:02 +00:00
if ( sc - > channel [ c h_idx ] . entnum = = cl . playernum [ 0 ] + 1 & & entnum ! = cl . playernum [ 0 ] + 1 & & sc - > channel [ ch_idx ] . sfx )
2004-08-23 00:15:46 +00:00
continue ;
2012-02-27 12:23:15 +00:00
if ( ! sc - > channel [ ch_idx ] . sfx )
2004-08-23 00:15:46 +00:00
{
2012-02-27 12:23:15 +00:00
oldestpos = 0x7fffffff ;
oldest = ch_idx ;
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
else if ( sc - > channel [ ch_idx ] . pos > oldestpos )
{
oldestpos = sc - > channel [ ch_idx ] . pos ;
oldest = ch_idx ;
}
}
2004-08-23 00:15:46 +00:00
2012-02-27 12:23:15 +00:00
if ( oldest = = - 1 )
2004-08-23 00:15:46 +00:00
return NULL ;
2012-02-27 12:23:15 +00:00
if ( sc - > channel [ oldest ] . sfx )
sc - > channel [ oldest ] . sfx = NULL ;
2004-08-23 00:15:46 +00:00
2012-02-27 12:23:15 +00:00
if ( sc - > total_chans < = oldest )
sc - > total_chans = oldest + 1 ;
return & sc - > channel [ oldest ] ;
2005-06-14 04:52:10 +00:00
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = =
SND_Spatialize
= = = = = = = = = = = = = = = = =
*/
void SND_Spatialize ( soundcardinfo_t * sc , channel_t * ch )
{
2010-11-06 23:05:29 +00:00
vec3_t listener_vec ;
2004-08-23 00:15:46 +00:00
vec_t dist ;
2005-01-13 16:29:20 +00:00
vec_t scale ;
2010-11-06 23:05:29 +00:00
vec3_t world_vec ;
2004-08-23 00:15:46 +00:00
sfx_t * snd ;
2005-01-13 16:29:20 +00:00
int i ;
2004-08-23 00:15:46 +00:00
2005-07-28 15:33:27 +00:00
// anything coming from the view entity will always be full volume
2004-10-03 22:52:02 +00:00
if ( ch - > entnum = = - 1 | | ch - > entnum = = cl . playernum [ 0 ] + 1 )
2004-08-23 00:15:46 +00:00
{
2005-01-13 16:29:20 +00:00
for ( i = 0 ; i < sc - > sn . numchannels ; i + + )
{
2008-06-08 14:37:57 +00:00
ch - > vol [ i ] = ch - > master_vol * ( ruleset_allow_localvolume . value ? snd_playersoundvolume . value : 1 ) ;
2005-01-13 16:29:20 +00:00
}
2004-08-23 00:15:46 +00:00
return ;
}
// calculate stereo seperation and distance attenuation
snd = ch - > sfx ;
2010-11-06 23:05:29 +00:00
VectorSubtract ( ch - > origin , listener_origin , world_vec ) ;
2005-06-14 04:52:10 +00:00
2010-11-06 23:05:29 +00:00
dist = VectorNormalize ( world_vec ) * ch - > dist_mult ;
2005-06-14 04:52:10 +00:00
2010-11-06 23:05:29 +00:00
listener_vec [ 1 ] = DotProduct ( listener_right , world_vec ) ;
listener_vec [ 0 ] = DotProduct ( listener_forward , world_vec ) ;
listener_vec [ 2 ] = DotProduct ( listener_up , world_vec ) ;
2004-08-23 00:15:46 +00:00
2009-11-04 21:16:50 +00:00
if ( snd_leftisright . ival )
2010-11-06 23:05:29 +00:00
listener_vec [ 1 ] = - listener_vec [ 1 ] ;
2009-11-04 21:16:50 +00:00
2005-01-13 16:29:20 +00:00
for ( i = 0 ; i < sc - > sn . numchannels ; i + + )
2004-08-23 00:15:46 +00:00
{
2010-11-06 23:05:29 +00:00
scale = ( 1 + DotProduct ( listener_vec , sc - > speakerdir [ i ] ) ) / 2 ;
if ( scale > 1 )
scale = 1 ;
2005-01-13 16:29:20 +00:00
scale = ( 1.0 - dist ) * scale * sc - > dist [ i ] ;
ch - > vol [ i ] = ( int ) ( ch - > master_vol * scale ) ;
if ( ch - > vol [ i ] < 0 )
ch - > vol [ i ] = 0 ;
2004-08-23 00:15:46 +00:00
}
}
// =======================================================================
// Start a sound effect
// =======================================================================
2012-02-27 12:23:15 +00:00
static void S_StartSoundCard ( soundcardinfo_t * sc , int entnum , int entchannel , sfx_t * sfx , vec3_t origin , float fvol , float attenuation , int startpos , float pitchadj )
2004-08-23 00:15:46 +00:00
{
channel_t * target_chan , * check ;
int vol ;
int ch_idx ;
int skip ;
if ( ! sound_started )
return ;
if ( ! sfx )
return ;
2009-11-04 21:16:50 +00:00
if ( nosound . ival )
2004-08-23 00:15:46 +00:00
return ;
2010-12-05 02:46:07 +00:00
if ( pitchadj < = 0 )
2010-11-28 19:14:21 +00:00
pitchadj = 100 ;
2012-02-27 12:23:15 +00:00
pitchadj * = snd_playbackrate . value * ( cls . state ? cl . gamespeed : 1 ) ;
2004-08-23 00:15:46 +00:00
vol = fvol * 255 ;
// pick a channel to play on
target_chan = SND_PickChannel ( sc , entnum , entchannel ) ;
if ( ! target_chan )
return ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
// spatialize
memset ( target_chan , 0 , sizeof ( * target_chan ) ) ;
if ( ! origin )
{
VectorCopy ( listener_origin , target_chan - > origin ) ;
}
else
{
VectorCopy ( origin , target_chan - > origin ) ;
}
target_chan - > dist_mult = attenuation / sound_nominal_clip_dist ;
target_chan - > master_vol = vol ;
target_chan - > entnum = entnum ;
target_chan - > entchannel = entchannel ;
SND_Spatialize ( sc , target_chan ) ;
if ( ! target_chan - > vol [ 0 ] & & ! target_chan - > vol [ 1 ] & & ! target_chan - > vol [ 2 ] & & ! target_chan - > vol [ 3 ] & & ! target_chan - > vol [ 4 ] & & ! target_chan - > vol [ 5 ] )
return ; // not audible at all
// new channel
2012-02-27 12:23:15 +00:00
if ( ! S_LoadSound ( sfx ) )
2004-08-23 00:15:46 +00:00
{
target_chan - > sfx = NULL ;
return ; // couldn't load the sound's data
}
2007-08-23 21:25:18 +00:00
2004-08-23 00:15:46 +00:00
target_chan - > sfx = sfx ;
2010-11-20 22:01:16 +00:00
target_chan - > rate = ( ( 1 < < PITCHSHIFT ) * pitchadj ) / 100 ; /*pitchadj is a percentage*/
2012-02-27 12:23:15 +00:00
if ( target_chan - > rate < 1 ) /*make sure the rate won't crash us*/
target_chan - > rate = 1 ;
2010-11-06 23:05:29 +00:00
target_chan - > pos = startpos * target_chan - > rate ;
2004-11-23 00:18:51 +00:00
target_chan - > looping = false ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
// if an identical sound has also been started this frame, offset the pos
// a bit to keep it from just making the first one louder
2010-11-06 23:05:29 +00:00
check = & sc - > channel [ DYNAMIC_FIRST ] ;
for ( ch_idx = DYNAMIC_FIRST ; ch_idx < DYNAMIC_STOP ; ch_idx + + , check + + )
2004-08-23 00:15:46 +00:00
{
if ( check = = target_chan )
continue ;
if ( check - > sfx = = sfx & & ! check - > pos )
{
skip = rand ( ) % ( int ) ( 0.1 * sc - > sn . speed ) ;
2012-02-27 12:23:15 +00:00
target_chan - > pos - = skip * target_chan - > rate ;
2004-08-23 00:15:46 +00:00
break ;
}
}
2010-12-05 02:46:07 +00:00
if ( sc - > ChannelUpdate )
sc - > ChannelUpdate ( sc , target_chan , true ) ;
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
void S_StartSound ( int entnum , int entchannel , sfx_t * sfx , vec3_t origin , float fvol , float attenuation , float timeofs , float pitchadj )
2006-05-10 07:35:19 +00:00
{
soundcardinfo_t * sc ;
if ( ! sfx | | ! * sfx - > name ) //no named sounds would need specific starting.
return ;
2012-02-27 12:23:15 +00:00
S_LockMixer ( ) ;
2006-05-10 07:35:19 +00:00
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
2012-02-27 12:23:15 +00:00
S_StartSoundCard ( sc , entnum , entchannel , sfx , origin , fvol , attenuation , - ( int ) ( timeofs * sc - > sn . speed ) , pitchadj ) ;
S_UnlockMixer ( ) ;
2004-08-23 00:15:46 +00:00
}
qboolean S_IsPlayingSomewhere ( sfx_t * s )
{
soundcardinfo_t * si ;
int i ;
for ( si = sndcardinfo ; si ; si = si - > next )
{
2006-05-28 21:56:04 +00:00
for ( i = 0 ; i < si - > total_chans ; i + + )
2004-08-23 00:15:46 +00:00
if ( si - > channel [ i ] . sfx = = s )
return true ;
}
return false ;
}
2012-02-27 12:23:15 +00:00
static void S_StopSoundCard ( soundcardinfo_t * sc , int entnum , int entchannel )
2004-08-23 00:15:46 +00:00
{
int i ;
for ( i = 0 ; i < sc - > total_chans ; i + + )
{
if ( sc - > channel [ i ] . entnum = = entnum
2010-08-21 13:31:39 +00:00
& & ( ! entchannel | | sc - > channel [ i ] . entchannel = = entchannel ) )
2004-08-23 00:15:46 +00:00
{
sc - > channel [ i ] . sfx = NULL ;
2010-12-05 02:46:07 +00:00
if ( sc - > ChannelUpdate )
sc - > ChannelUpdate ( sc , & sc - > channel [ i ] , true ) ;
2010-08-21 13:31:39 +00:00
if ( entchannel )
2012-02-27 12:23:15 +00:00
break ;
2004-08-23 00:15:46 +00:00
}
}
}
void S_StopSound ( int entnum , int entchannel )
{
soundcardinfo_t * sc ;
2012-02-27 12:23:15 +00:00
S_LockMixer ( ) ;
2004-08-23 00:15:46 +00:00
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
S_StopSoundCard ( sc , entnum , entchannel ) ;
2012-02-27 12:23:15 +00:00
S_UnlockMixer ( ) ;
2004-08-23 00:15:46 +00:00
}
void S_StopAllSounds ( qboolean clear )
{
int i ;
sfx_t * s ;
soundcardinfo_t * sc ;
2012-02-27 12:23:15 +00:00
if ( ! sound_started )
return ;
S_LockMixer ( ) ;
2004-08-23 00:15:46 +00:00
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
{
2006-05-28 21:56:04 +00:00
for ( i = 0 ; i < sc - > total_chans ; i + + )
2004-08-23 00:15:46 +00:00
if ( sc - > channel [ i ] . sfx )
{
s = sc - > channel [ i ] . sfx ;
sc - > channel [ i ] . sfx = NULL ;
2012-02-27 12:23:15 +00:00
if ( s - > decoder . abort )
2004-08-23 00:15:46 +00:00
if ( ! S_IsPlayingSomewhere ( s ) ) //if we aint playing it elsewhere, free it compleatly.
{
2012-02-27 12:23:15 +00:00
s - > decoder . abort ( s ) ;
2004-08-23 00:15:46 +00:00
}
2010-12-05 02:46:07 +00:00
if ( sc - > ChannelUpdate )
sc - > ChannelUpdate ( sc , & sc - > channel [ i ] , true ) ;
2004-08-23 00:15:46 +00:00
}
2006-05-28 21:56:04 +00:00
sc - > total_chans = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS + NUM_MUSICS ; // no statics
2004-08-23 00:15:46 +00:00
Q_memset ( sc - > channel , 0 , MAX_CHANNELS * sizeof ( channel_t ) ) ;
if ( clear )
S_ClearBuffer ( sc ) ;
}
2012-02-27 12:23:15 +00:00
S_UnlockMixer ( ) ;
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
static void S_StopAllSounds_f ( void )
2004-08-23 00:15:46 +00:00
{
S_StopAllSounds ( true ) ;
}
2012-02-27 12:23:15 +00:00
static void S_ClearBuffer ( soundcardinfo_t * sc )
2004-08-23 00:15:46 +00:00
{
2005-06-14 04:52:10 +00:00
void * buffer ;
2012-04-09 19:12:12 +00:00
unsigned int dummy ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
int clear ;
2006-02-01 22:36:12 +00:00
2004-08-23 00:15:46 +00:00
if ( ! sound_started | | ! sc - > sn . buffer )
return ;
if ( sc - > sn . samplebits = = 8 )
clear = 0x80 ;
else
clear = 0 ;
2012-04-09 19:12:12 +00:00
dummy = 0 ;
buffer = sc - > Lock ( sc , & dummy ) ;
2005-06-14 04:52:10 +00:00
if ( buffer )
{
2008-03-02 10:20:25 +00:00
Q_memset ( buffer , clear , sc - > sn . samples * sc - > sn . samplebits / 8 ) ;
2005-06-14 04:52:10 +00:00
sc - > Unlock ( sc , buffer ) ;
}
2004-08-23 00:15:46 +00:00
}
/*
= = = = = = = = = = = = = = = = =
S_StaticSound
= = = = = = = = = = = = = = = = =
*/
void S_StaticSound ( sfx_t * sfx , vec3_t origin , float vol , float attenuation )
{
channel_t * ss ;
soundcardinfo_t * scard ;
if ( ! sfx )
return ;
2012-02-27 12:23:15 +00:00
S_LockMixer ( ) ;
2004-08-23 00:15:46 +00:00
for ( scard = sndcardinfo ; scard ; scard = scard - > next )
{
if ( scard - > total_chans = = MAX_CHANNELS )
{
Con_Printf ( " total_channels == MAX_CHANNELS \n " ) ;
continue ;
}
2012-02-27 12:23:15 +00:00
if ( ! S_LoadSound ( sfx ) )
break ;
2004-08-23 00:15:46 +00:00
ss = & scard - > channel [ scard - > total_chans ] ;
scard - > total_chans + + ;
2010-12-05 02:46:07 +00:00
ss - > entnum = - 2 ;
2004-08-23 00:15:46 +00:00
ss - > sfx = sfx ;
2010-11-06 23:05:29 +00:00
ss - > rate = 1 < < PITCHSHIFT ;
2004-08-23 00:15:46 +00:00
VectorCopy ( origin , ss - > origin ) ;
ss - > master_vol = vol ;
ss - > dist_mult = ( attenuation / 64 ) / sound_nominal_clip_dist ;
2012-02-27 12:23:15 +00:00
ss - > pos = 0 ;
2004-11-23 00:18:51 +00:00
ss - > looping = true ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
SND_Spatialize ( scard , ss ) ;
2010-12-05 02:46:07 +00:00
if ( scard - > ChannelUpdate )
scard - > ChannelUpdate ( scard , ss , true ) ;
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
S_UnlockMixer ( ) ;
2004-08-23 00:15:46 +00:00
}
//=============================================================================
2009-04-01 22:03:56 +00:00
void S_Music_Clear ( sfx_t * onlyifsample )
{
//stops the current BGM music
//calling this will trigger Media_NextTrack later
sfx_t * s ;
soundcardinfo_t * sc ;
int i ;
2010-11-06 23:05:29 +00:00
for ( i = MUSIC_FIRST ; i < MUSIC_STOP ; i + + )
2009-04-01 22:03:56 +00:00
{
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
{
s = sc - > channel [ i ] . sfx ;
if ( ! s )
continue ;
if ( onlyifsample & & s ! = onlyifsample )
continue ;
2012-02-27 12:23:15 +00:00
sc - > channel [ i ] . pos = 0 ;
2009-04-01 22:03:56 +00:00
sc - > channel [ i ] . sfx = NULL ;
if ( s )
2012-02-27 12:23:15 +00:00
if ( s - > decoder . abort )
2009-04-01 22:03:56 +00:00
if ( ! S_IsPlayingSomewhere ( s ) ) //if we aint playing it elsewhere, free it compleatly.
{
2012-02-27 12:23:15 +00:00
s - > decoder . abort ( s ) ;
// if (s->cache.data)
// Cache_Free(&s->cache);
2009-04-01 22:03:56 +00:00
}
}
}
}
void S_Music_Seek ( float time )
{
soundcardinfo_t * sc ;
int i ;
2010-11-06 23:05:29 +00:00
for ( i = MUSIC_FIRST ; i < MUSIC_STOP ; i + + )
2009-04-01 22:03:56 +00:00
{
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
{
2010-11-06 23:05:29 +00:00
sc - > channel [ i ] . pos + = sc - > sn . speed * time * sc - > channel [ i ] . rate ;
2009-04-01 22:03:56 +00:00
if ( sc - > channel [ i ] . pos < 0 )
{ //clamp to the start of the track
sc - > channel [ i ] . pos = 0 ;
}
//if we seek over the end, ignore it. The sound playing code will spot that.
}
}
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = =
S_UpdateAmbientSounds
= = = = = = = = = = = = = = = = = = =
*/
2009-04-02 22:25:54 +00:00
char * Media_NextTrack ( int musicchannelnum ) ;
2005-11-30 01:20:53 +00:00
mleaf_t * Q1BSP_LeafForPoint ( model_t * model , vec3_t p ) ;
2004-08-23 00:15:46 +00:00
void S_UpdateAmbientSounds ( soundcardinfo_t * sc )
{
mleaf_t * l ;
2010-12-05 02:46:07 +00:00
float vol , oldvol ;
2004-08-23 00:15:46 +00:00
int ambient_channel ;
channel_t * chan ;
int i ;
if ( ! snd_ambient )
return ;
2010-11-06 23:05:29 +00:00
for ( i = MUSIC_FIRST ; i < MUSIC_STOP ; i + + )
2004-08-23 00:15:46 +00:00
{
chan = & sc - > channel [ i ] ;
if ( ! chan - > sfx )
{
2010-11-06 23:05:29 +00:00
char * nexttrack = Media_NextTrack ( i - MUSIC_FIRST ) ;
2004-08-23 00:15:46 +00:00
sfx_t * newmusic ;
if ( nexttrack & & * nexttrack )
{
newmusic = S_PrecacheSound ( nexttrack ) ;
2012-02-27 12:23:15 +00:00
if ( ! newmusic - > failedload )
2005-06-14 04:52:10 +00:00
{
2004-08-23 00:15:46 +00:00
chan - > sfx = newmusic ;
2010-11-06 23:05:29 +00:00
chan - > rate = 1 < < PITCHSHIFT ;
2004-08-23 00:15:46 +00:00
chan - > pos = 0 ;
chan - > vol [ 0 ] = chan - > vol [ 1 ] = chan - > vol [ 2 ] = chan - > vol [ 3 ] = chan - > vol [ 4 ] = chan - > vol [ 5 ] = chan - > master_vol = 100 ;
}
}
}
if ( chan - > sfx )
chan - > vol [ 0 ] = chan - > vol [ 1 ] = chan - > vol [ 2 ] = chan - > vol [ 3 ] = chan - > vol [ 4 ] = chan - > vol [ 5 ] = chan - > master_vol = ( 255 / volume . value ) * bgmvolume . value ;
}
// calc ambient sound levels
2005-08-26 22:56:51 +00:00
if ( ! cl . worldmodel | | cl . worldmodel - > type ! = mod_brush | | cl . worldmodel - > fromgame ! = fg_quake )
2004-08-23 00:15:46 +00:00
return ;
2005-08-26 22:56:51 +00:00
l = Q1BSP_LeafForPoint ( cl . worldmodel , listener_origin ) ;
2004-08-23 00:15:46 +00:00
if ( ! l | | ! ambient_level . value )
{
for ( ambient_channel = 0 ; ambient_channel < NUM_AMBIENTS ; ambient_channel + + )
2010-12-05 02:46:07 +00:00
{
chan = & sc - > channel [ AMBIENT_FIRST + ambient_channel ] ;
chan - > sfx = NULL ;
if ( sc - > ChannelUpdate )
sc - > ChannelUpdate ( sc , chan , true ) ;
}
2004-08-23 00:15:46 +00:00
return ;
}
for ( ambient_channel = 0 ; ambient_channel < NUM_AMBIENTS ; ambient_channel + + )
{
2010-12-05 02:46:07 +00:00
static float level [ NUM_AMBIENTS ] ;
2010-11-06 23:05:29 +00:00
chan = & sc - > channel [ AMBIENT_FIRST + ambient_channel ] ;
chan - > sfx = ambient_sfx [ AMBIENT_FIRST + ambient_channel ] ;
2010-12-05 02:46:07 +00:00
chan - > entnum = - 1 ;
chan - > looping = true ;
2010-11-06 23:05:29 +00:00
chan - > rate = 1 < < PITCHSHIFT ;
2004-08-23 00:15:46 +00:00
VectorCopy ( listener_origin , chan - > origin ) ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
vol = ambient_level . value * l - > ambient_sound_level [ ambient_channel ] ;
if ( vol < 8 )
vol = 0 ;
2010-12-05 02:46:07 +00:00
oldvol = level [ ambient_channel ] ;
2004-08-23 00:15:46 +00:00
// don't adjust volume too fast
2010-12-05 02:46:07 +00:00
if ( level [ ambient_channel ] < vol )
2004-08-23 00:15:46 +00:00
{
2010-12-05 02:46:07 +00:00
level [ ambient_channel ] + = host_frametime * ambient_fade . value ;
if ( level [ ambient_channel ] > vol )
level [ ambient_channel ] = vol ;
2004-08-23 00:15:46 +00:00
}
else if ( chan - > master_vol > vol )
{
2010-12-05 02:46:07 +00:00
level [ ambient_channel ] - = host_frametime * ambient_fade . value ;
if ( level [ ambient_channel ] < vol )
level [ ambient_channel ] = vol ;
2004-08-23 00:15:46 +00:00
}
2005-06-14 04:52:10 +00:00
2010-12-05 02:46:07 +00:00
chan - > master_vol = level [ ambient_channel ] ;
2004-08-23 00:15:46 +00:00
chan - > vol [ 0 ] = chan - > vol [ 1 ] = chan - > vol [ 2 ] = chan - > vol [ 3 ] = chan - > vol [ 4 ] = chan - > vol [ 5 ] = chan - > master_vol ;
2010-12-05 02:46:07 +00:00
if ( sc - > ChannelUpdate )
sc - > ChannelUpdate ( sc , chan , ( oldvol = = 0 ) ^ ( level [ ambient_channel ] = = 0 ) ) ;
2004-08-23 00:15:46 +00:00
}
}
/*
= = = = = = = = = = = =
S_Update
Called once each time through the main loop
= = = = = = = = = = = =
*/
2010-11-06 23:05:29 +00:00
void S_UpdateListener ( vec3_t origin , vec3_t forward , vec3_t right , vec3_t up )
2004-08-23 00:15:46 +00:00
{
2005-02-06 02:47:36 +00:00
VectorCopy ( origin , listener_origin ) ;
VectorCopy ( forward , listener_forward ) ;
VectorCopy ( right , listener_right ) ;
VectorCopy ( up , listener_up ) ;
2004-08-23 00:15:46 +00:00
}
2009-04-01 22:03:56 +00:00
void S_GetListenerInfo ( float * origin , float * forward , float * right , float * up )
{
VectorCopy ( listener_origin , origin ) ;
VectorCopy ( listener_forward , forward ) ;
VectorCopy ( listener_right , right ) ;
VectorCopy ( listener_up , up ) ;
}
2012-02-27 12:23:15 +00:00
static void S_UpdateCard ( soundcardinfo_t * sc )
2004-08-23 00:15:46 +00:00
{
int i , j ;
int total ;
channel_t * ch ;
channel_t * combine ;
2004-10-03 22:52:02 +00:00
if ( ! sound_started )
2004-08-23 00:15:46 +00:00
return ;
2004-10-03 22:52:02 +00:00
if ( ( snd_blocked > 0 ) )
{
if ( ! sc - > inactive_sound )
return ;
}
2005-06-14 04:52:10 +00:00
2010-03-14 14:35:56 +00:00
# ifdef AVAIL_OPENAL
if ( sc - > openal = = 1 )
{
2010-12-05 02:46:07 +00:00
OpenAL_Update_Listener ( listener_origin , listener_forward , listener_right , listener_up , listener_velocity ) ;
2010-03-14 14:35:56 +00:00
}
# endif
2004-08-23 00:15:46 +00:00
// update general area ambient sound sources
S_UpdateAmbientSounds ( sc ) ;
combine = NULL ;
2005-06-14 04:52:10 +00:00
// update spatialization for static and dynamic sounds
2010-11-06 23:05:29 +00:00
ch = sc - > channel + DYNAMIC_FIRST ;
for ( i = DYNAMIC_FIRST ; i < sc - > total_chans ; i + + , ch + + )
2004-08-23 00:15:46 +00:00
{
if ( ! ch - > sfx )
continue ;
SND_Spatialize ( sc , ch ) ; // respatialize channel
if ( ! ch - > vol [ 0 ] & & ! ch - > vol [ 1 ] & & ! ch - > vol [ 2 ] & & ! ch - > vol [ 3 ] & & ! ch - > vol [ 4 ] & & ! ch - > vol [ 5 ] )
continue ;
// try to combine static sounds with a previous channel of the same
// sound effect so we don't mix five torches every frame
2005-06-14 04:52:10 +00:00
2010-11-06 23:05:29 +00:00
if ( i > = DYNAMIC_STOP )
2004-08-23 00:15:46 +00:00
{
// see if it can just use the last one
if ( combine & & combine - > sfx = = ch - > sfx )
{
combine - > vol [ 0 ] + = ch - > vol [ 0 ] ;
combine - > vol [ 1 ] + = ch - > vol [ 1 ] ;
combine - > vol [ 2 ] + = ch - > vol [ 2 ] ;
combine - > vol [ 3 ] + = ch - > vol [ 3 ] ;
combine - > vol [ 4 ] + = ch - > vol [ 4 ] ;
combine - > vol [ 5 ] + = ch - > vol [ 5 ] ;
ch - > vol [ 0 ] = ch - > vol [ 1 ] = ch - > vol [ 2 ] = ch - > vol [ 3 ] = ch - > vol [ 4 ] = ch - > vol [ 5 ] = 0 ;
continue ;
}
// search for one
2010-11-06 23:05:29 +00:00
combine = sc - > channel + DYNAMIC_FIRST ;
for ( j = DYNAMIC_FIRST ; j < i ; j + + , combine + + )
2004-08-23 00:15:46 +00:00
if ( combine - > sfx = = ch - > sfx )
break ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
if ( j = = sc - > total_chans )
{
combine = NULL ;
}
else
{
if ( combine ! = ch )
{
combine - > vol [ 0 ] + = ch - > vol [ 0 ] ;
combine - > vol [ 1 ] + = ch - > vol [ 1 ] ;
combine - > vol [ 2 ] + = ch - > vol [ 2 ] ;
combine - > vol [ 3 ] + = ch - > vol [ 3 ] ;
combine - > vol [ 4 ] + = ch - > vol [ 4 ] ;
combine - > vol [ 5 ] + = ch - > vol [ 5 ] ;
ch - > vol [ 0 ] = ch - > vol [ 1 ] = ch - > vol [ 2 ] = ch - > vol [ 3 ] = ch - > vol [ 4 ] = ch - > vol [ 5 ] = 0 ;
}
continue ;
}
}
}
//
// debugging output
//
2009-11-04 21:16:50 +00:00
if ( snd_show . ival )
2004-08-23 00:15:46 +00:00
{
total = 0 ;
ch = sc - > channel ;
for ( i = 0 ; i < sc - > total_chans ; i + + , ch + + )
if ( ch - > sfx & & ( ch - > vol [ 0 ] | | ch - > vol [ 1 ] ) )
{
// Con_Printf ("%i, %i %i %i %i %i %i %s\n", i, ch->vol[0], ch->vol[1], ch->vol[2], ch->vol[3], ch->vol[4], ch->vol[5], ch->sfx->name);
total + + ;
}
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
Con_Printf ( " ----(%i)---- \n " , total ) ;
}
2005-06-14 04:52:10 +00:00
// mix some sound
2012-02-27 12:23:15 +00:00
if ( sc - > selfpainting )
return ;
if ( snd_blocked > 0 )
{
if ( ! sc - > inactive_sound )
return ;
}
2004-08-23 00:15:46 +00:00
S_Update_ ( sc ) ;
}
2011-01-29 19:53:38 +00:00
int GetSoundtime ( soundcardinfo_t * sc )
2004-08-23 00:15:46 +00:00
{
2005-06-14 04:52:10 +00:00
int samplepos ;
2004-08-23 00:15:46 +00:00
int fullsamples ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
fullsamples = sc - > sn . samples / sc - > sn . numchannels ;
// it is possible to miscount buffers if it has wrapped twice between
// calls to S_Update. Oh well.
2005-06-14 04:52:10 +00:00
samplepos = sc - > GetDMAPos ( sc ) ;
2004-08-23 00:15:46 +00:00
2011-01-29 19:53:38 +00:00
samplepos - = sc - > samplequeue ;
if ( samplepos < 0 )
{
samplepos = 0 ;
}
2004-08-23 00:15:46 +00:00
if ( samplepos < sc - > oldsamplepos )
{
sc - > buffers + + ; // buffer wrapped
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
if ( sc - > paintedtime > 0x40000000 )
{ // time to chop things off to avoid 32 bit limits
sc - > buffers = 0 ;
sc - > paintedtime = fullsamples ;
S_StopAllSounds ( true ) ;
}
}
sc - > oldsamplepos = samplepos ;
2011-01-29 19:53:38 +00:00
return sc - > buffers * fullsamples + samplepos / sc - > sn . numchannels ;
2004-08-23 00:15:46 +00:00
}
2010-11-06 23:05:29 +00:00
void S_Update ( void )
{
soundcardinfo_t * sc ;
2012-02-27 12:23:15 +00:00
S_LockMixer ( ) ;
2010-11-06 23:05:29 +00:00
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
S_UpdateCard ( sc ) ;
2012-02-27 12:23:15 +00:00
S_UnlockMixer ( ) ;
2010-11-06 23:05:29 +00:00
}
2004-08-23 00:15:46 +00:00
void S_ExtraUpdate ( void )
{
soundcardinfo_t * sc ;
2005-02-06 02:47:36 +00:00
if ( ! sound_started )
return ;
2004-08-23 00:15:46 +00:00
# ifdef _WIN32
IN_Accumulate ( ) ;
# endif
2009-11-04 21:16:50 +00:00
if ( snd_noextraupdate . ival )
2004-08-23 00:15:46 +00:00
return ; // don't pollute timings
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
2012-02-27 12:23:15 +00:00
{
if ( sc - > selfpainting )
continue ;
if ( snd_blocked > 0 )
{
if ( ! sc - > inactive_sound )
continue ;
}
2012-04-24 07:59:11 +00:00
S_LockMixer ( ) ;
2004-08-23 00:15:46 +00:00
S_Update_ ( sc ) ;
2012-04-24 07:59:11 +00:00
S_UnlockMixer ( ) ;
2012-02-27 12:23:15 +00:00
}
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
static void S_Update_ ( soundcardinfo_t * sc )
2004-08-23 00:15:46 +00:00
{
2011-01-29 19:53:38 +00:00
int soundtime ; /*in pairs*/
2004-08-23 00:15:46 +00:00
unsigned endtime ;
int samps ;
2005-02-06 02:47:36 +00:00
2004-08-23 00:15:46 +00:00
// Updates DMA time
2011-01-29 19:53:38 +00:00
soundtime = GetSoundtime ( sc ) ;
2004-08-23 00:15:46 +00:00
2011-01-29 19:53:38 +00:00
if ( sc - > samplequeue )
2004-08-23 00:15:46 +00:00
{
2011-01-29 19:53:38 +00:00
/*device uses a write-once queue*/
endtime = soundtime + sc - > samplequeue / sc - > sn . numchannels ;
soundtime = sc - > paintedtime ;
samps = sc - > samplequeue / sc - > sn . numchannels ;
2004-08-23 00:15:46 +00:00
}
2011-01-29 19:53:38 +00:00
else
{
/*device uses memory-mapped output*/
// check to make sure that we haven't overshot
if ( sc - > paintedtime < soundtime )
{
//Con_Printf ("S_Update_ : overflow\n");
sc - > paintedtime = soundtime ;
}
2004-08-23 00:15:46 +00:00
2011-01-29 19:53:38 +00:00
// mix ahead of current position
endtime = soundtime + ( int ) ( _snd_mixahead . value * sc - > sn . speed ) ;
samps = sc - > sn . samples / sc - > sn . numchannels ;
}
2004-08-23 00:15:46 +00:00
if ( endtime - soundtime > samps )
2011-01-29 19:53:38 +00:00
{
2004-08-23 00:15:46 +00:00
endtime = soundtime + samps ;
2011-01-29 19:53:38 +00:00
}
2004-08-23 00:15:46 +00:00
2011-01-29 19:53:38 +00:00
/*DirectSound may have killed us to give priority to another app, ask to restore it*/
2005-06-14 04:52:10 +00:00
if ( sc - > Restore )
sc - > Restore ( sc ) ;
2004-08-23 00:15:46 +00:00
S_PaintChannels ( sc , endtime ) ;
2011-01-29 19:53:38 +00:00
sc - > Submit ( sc , soundtime , endtime ) ;
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
/*
called periodically by dedicated mixer threads .
do any blocking calls AFTER this returns . note that this means you can ' t use the Submit / unlock method to submit blocking audio .
*/
void S_MixerThread ( soundcardinfo_t * sc )
{
S_LockMixer ( ) ;
S_Update_ ( sc ) ;
S_UnlockMixer ( ) ;
}
2004-08-23 00:15:46 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
console functions
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
void S_Play ( void )
{
int i ;
char name [ 256 ] ;
sfx_t * sfx ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
i = 1 ;
while ( i < Cmd_Argc ( ) )
{
if ( ! Q_strrchr ( Cmd_Argv ( i ) , ' . ' ) )
{
2006-04-21 17:23:35 +00:00
Q_strncpyz ( name , Cmd_Argv ( i ) , sizeof ( name ) - 4 ) ;
2004-08-23 00:15:46 +00:00
Q_strcat ( name , " .wav " ) ;
}
else
2006-04-21 17:23:35 +00:00
Q_strncpyz ( name , Cmd_Argv ( i ) , sizeof ( name ) ) ;
2004-08-23 00:15:46 +00:00
sfx = S_PrecacheSound ( name ) ;
2012-02-27 12:23:15 +00:00
S_StartSound ( cl . playernum [ 0 ] + 1 , - 1 , sfx , vec3_origin , 1.0 , 0.0 , 0 , 0 ) ;
2004-08-23 00:15:46 +00:00
i + + ;
}
}
void S_PlayVol ( void )
{
int i ;
float vol ;
char name [ 256 ] ;
sfx_t * sfx ;
2005-06-14 04:52:10 +00:00
2004-08-23 00:15:46 +00:00
i = 1 ;
while ( i < Cmd_Argc ( ) )
{
if ( ! Q_strrchr ( Cmd_Argv ( i ) , ' . ' ) )
{
2006-04-21 16:25:25 +00:00
Q_strncpy ( name , Cmd_Argv ( i ) , sizeof ( name ) - 4 ) ;
2004-08-23 00:15:46 +00:00
Q_strcat ( name , " .wav " ) ;
}
else
2006-04-21 16:25:25 +00:00
Q_strncpy ( name , Cmd_Argv ( i ) , sizeof ( name ) ) ;
2004-08-23 00:15:46 +00:00
sfx = S_PrecacheSound ( name ) ;
vol = Q_atof ( Cmd_Argv ( i + 1 ) ) ;
2012-02-27 12:23:15 +00:00
S_StartSound ( cl . playernum [ 0 ] + 1 , - 1 , sfx , vec3_origin , vol , 0.0 , 0 , 0 ) ;
2004-08-23 00:15:46 +00:00
i + = 2 ;
}
}
2012-02-27 12:23:15 +00:00
void S_SoundList_f ( void )
2004-08-23 00:15:46 +00:00
{
2012-02-27 12:23:15 +00:00
/*
2004-08-23 00:15:46 +00:00
int i ;
sfx_t * sfx ;
sfxcache_t * sc ;
int size , total ;
total = 0 ;
for ( sfx = known_sfx , i = 0 ; i < num_sfx ; i + + , sfx + + )
{
2012-02-27 12:23:15 +00:00
if ( ! sfx - > decoder )
{
Con_Printf ( " S( ) : %s \n " , sfx - > name ) ;
continue ;
}
2004-08-23 00:15:46 +00:00
sc = Cache_Check ( & sfx - > cache ) ;
if ( ! sc )
continue ;
2006-01-28 06:41:20 +00:00
size = sc - > length * sc - > width * ( sc - > numchannels ) ;
2004-08-23 00:15:46 +00:00
total + = size ;
if ( sc - > loopstart > = 0 )
Con_Printf ( " L " ) ;
else
Con_Printf ( " " ) ;
2012-02-27 12:23:15 +00:00
Con_Printf ( " (%2db%2ic) %6i : %s \n " , sc - > width * 8 , sc - > numchannels , size , sfx - > name ) ;
2004-08-23 00:15:46 +00:00
}
Con_Printf ( " Total resident: %i \n " , total ) ;
2012-02-27 12:23:15 +00:00
*/
2004-08-23 00:15:46 +00:00
}
void S_LocalSound ( char * sound )
{
sfx_t * sfx ;
2009-11-04 21:16:50 +00:00
if ( nosound . ival )
2004-08-23 00:15:46 +00:00
return ;
if ( ! sound_started )
return ;
sfx = S_PrecacheSound ( sound ) ;
if ( ! sfx )
{
Con_Printf ( " S_LocalSound: can't cache %s \n " , sound ) ;
return ;
}
2012-02-27 12:23:15 +00:00
S_StartSound ( - 1 , - 1 , sfx , vec3_origin , 1 , 1 , 0 , 0 ) ;
2004-08-23 00:15:46 +00:00
}
typedef struct {
2012-02-27 12:23:15 +00:00
sfxdecode_t decoder ;
2004-08-23 00:15:46 +00:00
qboolean inuse ;
int id ;
sfx_t sfx ;
2012-02-27 12:23:15 +00:00
int numchannels ;
int width ;
int length ;
void * data ;
2004-08-23 00:15:46 +00:00
} streaming_t ;
# define MAX_RAW_SOURCES (MAX_CLIENTS+1)
streaming_t s_streamers [ MAX_RAW_SOURCES ] ;
2005-07-14 01:57:34 +00:00
void S_ClearRaw ( void )
{
memset ( s_streamers , 0 , sizeof ( s_streamers ) ) ;
}
2012-02-27 12:23:15 +00:00
//returns an sfxcache_t stating where the data is
sfxcache_t * S_Raw_Locate ( sfx_t * sfx , sfxcache_t * buf , int start , int length )
{
streaming_t * s = sfx - > decoder . buf ;
if ( buf )
{
buf - > data = s - > data ;
buf - > length = s - > length ;
buf - > loopstart = - 1 ;
buf - > numchannels = s - > numchannels ;
buf - > soundoffset = 0 ;
buf - > speed = snd_speed ;
buf - > width = s - > width ;
}
return buf ;
}
2004-08-23 00:15:46 +00:00
//streaming audio. //this is useful when there is one source, and the sound is to be played with no attenuation
void S_RawAudio ( int sourceid , qbyte * data , int speed , int samples , int channels , int width )
{
soundcardinfo_t * si ;
int i ;
int prepadl ;
2005-07-14 01:57:34 +00:00
int oldlength ;
2004-08-23 00:15:46 +00:00
int spare ;
2005-08-12 19:37:24 +00:00
int outsamples ;
2006-06-04 01:43:52 +00:00
double speedfactor ;
2012-02-27 12:23:15 +00:00
qbyte * newcache ;
2004-08-23 00:15:46 +00:00
streaming_t * s , * free = NULL ;
for ( s = s_streamers , i = 0 ; i < MAX_RAW_SOURCES ; i + + , s + + )
{
if ( ! s - > inuse )
{
2010-11-15 02:40:31 +00:00
if ( ! free )
free = s ;
2004-08-23 00:15:46 +00:00
continue ;
}
if ( s - > id = = sourceid )
break ;
}
2005-07-14 01:57:34 +00:00
if ( ! data )
{
if ( i = = MAX_RAW_SOURCES )
return ; //wierd, it wasn't even playing.
s - > inuse = false ;
2012-02-27 12:23:15 +00:00
S_LockMixer ( ) ;
2005-07-14 01:57:34 +00:00
for ( si = sndcardinfo ; si ; si = si - > next )
2010-11-13 17:22:46 +00:00
for ( i = 0 ; i < si - > total_chans ; i + + )
2005-07-14 01:57:34 +00:00
if ( si - > channel [ i ] . sfx = = & s - > sfx )
{
si - > channel [ i ] . sfx = NULL ;
break ;
}
2012-02-27 12:23:15 +00:00
BZ_Free ( s - > data ) ;
S_UnlockMixer ( ) ;
2005-07-14 01:57:34 +00:00
return ;
}
2012-02-27 12:23:15 +00:00
if ( i = = MAX_RAW_SOURCES | | ! s - > inuse ) //whoops.
2004-08-23 00:15:46 +00:00
{
2012-02-27 12:23:15 +00:00
if ( i = = MAX_RAW_SOURCES )
2004-08-23 00:15:46 +00:00
{
2012-02-27 12:23:15 +00:00
if ( ! free )
{
Con_Printf ( " No free audio streams \n " ) ;
return ;
}
s = free ;
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
s - > sfx . decoder . buf = s ;
s - > sfx . decoder . decodedata = S_Raw_Locate ;
s - > numchannels = channels ;
s - > width = width ;
s - > data = NULL ;
s - > length = 0 ;
s - > id = sourceid ;
s - > inuse = true ;
strcpy ( s - > sfx . name , " raw stream " ) ;
2005-07-14 01:57:34 +00:00
// Con_Printf("Added new raw stream\n");
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
S_LockMixer ( ) ;
if ( s - > width ! = width | | s - > numchannels ! = channels )
2004-08-23 00:15:46 +00:00
{
2012-02-27 12:23:15 +00:00
s - > width = width ;
s - > numchannels = channels ;
s - > length = 0 ;
2005-07-14 01:57:34 +00:00
// Con_Printf("Restarting raw stream\n");
2004-08-23 00:15:46 +00:00
}
2006-06-04 16:02:03 +00:00
speedfactor = ( double ) speed / snd_speed ;
2005-08-12 19:37:24 +00:00
outsamples = samples / speedfactor ;
2012-02-27 12:23:15 +00:00
oldlength = s - > length ;
2005-07-14 01:57:34 +00:00
prepadl = 0x7fffffff ;
2004-08-23 00:15:46 +00:00
for ( si = sndcardinfo ; si ; si = si - > next ) //make sure all cards are playing, and that we still get a prepad if just one is.
{
2006-05-28 21:56:04 +00:00
for ( i = 0 ; i < si - > total_chans ; i + + )
2004-08-23 00:15:46 +00:00
if ( si - > channel [ i ] . sfx = = & s - > sfx )
{
2010-11-20 22:01:16 +00:00
if ( prepadl > ( si - > channel [ i ] . pos > > PITCHSHIFT ) )
prepadl = ( si - > channel [ i ] . pos > > PITCHSHIFT ) ;
2004-08-23 00:15:46 +00:00
break ;
}
}
2005-07-14 01:57:34 +00:00
if ( prepadl = = 0x7fffffff )
2004-08-23 00:15:46 +00:00
{
2009-11-04 21:16:50 +00:00
if ( snd_show . ival )
2005-07-14 01:57:34 +00:00
Con_Printf ( " Wasn't playing \n " ) ;
2004-08-23 00:15:46 +00:00
prepadl = 0 ;
2010-11-20 22:01:16 +00:00
spare = 0 ;
2004-08-23 00:15:46 +00:00
if ( spare > snd_speed )
2005-07-14 01:57:34 +00:00
{
2006-03-11 03:12:10 +00:00
Con_DPrintf ( " Sacrificed raw sound stream \n " ) ;
2004-08-23 00:15:46 +00:00
spare = 0 ; //too far out. sacrifice it all
2005-07-14 01:57:34 +00:00
}
2004-08-23 00:15:46 +00:00
}
else
{
2010-11-20 22:01:16 +00:00
if ( prepadl < 0 )
prepadl = 0 ;
2012-02-27 12:23:15 +00:00
spare = s - > length - prepadl ;
2005-07-14 01:57:34 +00:00
if ( spare < 0 ) //remaining samples since last time
2004-08-23 00:15:46 +00:00
spare = 0 ;
2005-07-14 01:57:34 +00:00
2010-11-20 22:01:16 +00:00
if ( spare > snd_speed * 2 ) // more than 2 seconds of sound
2005-07-14 01:57:34 +00:00
{
2006-03-11 03:12:10 +00:00
Con_DPrintf ( " Sacrificed raw sound stream \n " ) ;
2005-07-14 01:57:34 +00:00
spare = 0 ; //too far out. sacrifice it all
}
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
newcache = BZ_Malloc ( ( spare + outsamples ) * ( s - > numchannels ) * s - > width ) ;
memcpy ( newcache , ( qbyte * ) s - > data + prepadl * ( s - > numchannels ) * s - > width , spare * ( s - > numchannels ) * s - > width ) ;
2004-08-23 00:15:46 +00:00
2012-02-27 12:23:15 +00:00
BZ_Free ( s - > data ) ;
s - > data = newcache ;
2004-08-23 00:15:46 +00:00
2012-02-27 12:23:15 +00:00
s - > length = spare + outsamples ;
2004-08-23 00:15:46 +00:00
{
2006-06-04 01:43:52 +00:00
extern cvar_t snd_linearresample_stream ;
2012-02-27 12:23:15 +00:00
short * outpos = ( short * ) ( ( char * ) s - > data + spare * ( s - > numchannels ) * s - > width ) ;
2010-11-24 02:54:28 +00:00
SND_ResampleStream ( data ,
speed ,
width ,
2006-06-04 01:43:52 +00:00
channels ,
samples ,
2010-11-24 02:54:28 +00:00
outpos ,
snd_speed ,
2012-02-27 12:23:15 +00:00
s - > width ,
s - > numchannels ,
2009-11-04 21:16:50 +00:00
snd_linearresample_stream . ival ) ;
2004-08-23 00:15:46 +00:00
}
for ( si = sndcardinfo ; si ; si = si - > next )
{
2006-05-28 21:56:04 +00:00
for ( i = 0 ; i < si - > total_chans ; i + + )
2004-08-23 00:15:46 +00:00
if ( si - > channel [ i ] . sfx = = & s - > sfx )
{
2010-11-06 23:05:29 +00:00
si - > channel [ i ] . pos - = prepadl * si - > channel [ i ] . rate ;
2005-07-14 01:57:34 +00:00
2012-02-27 12:23:15 +00:00
if ( si - > channel [ i ] . pos < 0 )
2005-07-14 01:57:34 +00:00
si - > channel [ i ] . pos = 0 ;
2004-08-23 00:15:46 +00:00
break ;
}
2006-05-28 21:56:04 +00:00
if ( i = = si - > total_chans ) //this one wasn't playing.
2005-07-14 01:57:34 +00:00
{
2012-02-27 12:23:15 +00:00
channel_t * c = SND_PickChannel ( si , - 1 , 0 ) ;
c - > entnum = - 1 ;
c - > entchannel = 0 ;
c - > dist_mult = 0 ;
c - > looping = false ;
c - > master_vol = 255 ;
c - > pos = 0 ;
c - > rate = 1 < < PITCHSHIFT ;
c - > sfx = & s - > sfx ;
c - > start = 0 ;
SND_Spatialize ( si , c ) ;
2005-07-14 01:57:34 +00:00
}
2004-08-23 00:15:46 +00:00
}
2012-02-27 12:23:15 +00:00
S_UnlockMixer ( ) ;
2004-08-23 00:15:46 +00:00
}