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 ) ;
2012-07-05 19:42:36 +00:00
static sfx_t * S_FindName ( char * name ) ;
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 ;
2012-11-27 03:23:19 +00:00
# define MAX_SFX 2048
2004-08-23 00:15:46 +00:00
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 ) ;
2013-04-07 08:59:05 +00:00
cvar_t snd_mixerthread = CVARAD ( " s_mixerthread " , " 1 " ,
" snd_mixerthread " , " When enabled sound mixing will be run on a separate thread. Currently supported only by directsound. Other drivers may unconditionally thread audio. Set to 0 only if you have issues. " ) ;
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 " ) ;
2013-05-31 01:16:07 +00:00
cvar_t cl_voip_test = CVARD ( " cl_voip_test " , " 0 " , " If 1, enables you to hear your own voice directly, bypassing the server and thus without networking latency, but is fine for checking audio levels. Note that sv_voip_echo can be set if you want to include latency and packetloss considerations, but setting that cvar requires server admin access and is thus much harder to use. " ) ;
2010-11-20 22:01:16 +00:00
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 " ) ;
2013-05-31 01:16:07 +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. Does not affect game volume when other speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used). " ) ;
cvar_t cl_voip_showmeter = CVARAFD ( " cl_voip_showmeter " , " 1 " , NULL , CVAR_ARCHIVE , " Shows your speech volume above the standard hud. 0=hide, 1=show when transmitting, 2=ignore voice-activation disable " ) ;
2010-11-20 22:01:16 +00:00
2013-05-31 01:16:07 +00:00
cvar_t cl_voip_play = CVARAFDC ( " cl_voip_play " , " 1 " , NULL , CVAR_ARCHIVE , " Enables voip playback. Value is a volume scaler. " , S_Voip_Play_Callback ) ;
cvar_t cl_voip_ducking = CVARAFD ( " cl_voip_ducking " , " 0.5 " , NULL , CVAR_ARCHIVE , " Scales game audio by this much when someone is talking to you. Does not affect your speaker volume when you speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used). " ) ;
2011-12-26 15:19:13 +00:00
cvar_t cl_voip_micamp = CVARAFDC ( " cl_voip_micamp " , " 2 " , NULL , CVAR_ARCHIVE , " Amplifies your microphone when using voip. " , 0 ) ;
2013-05-31 01:16:07 +00:00
cvar_t cl_voip_codec = CVARAFDC ( " cl_voip_codec " , " 0 " , NULL , CVAR_ARCHIVE , " 0: speex. 1: raw. 2: opus. " , 0 ) ;
cvar_t cl_voip_noisefilter = CVARAFDC ( " cl_voip_noisefilter " , " 1 " , NULL , CVAR_ARCHIVE , " Enable the use of the noise cancelation filter, which also normalises microphone volume levels. " , 0 ) ;
cvar_t cl_voip_autogain = CVARAFDC ( " cl_voip_autogain " , " 0 " , NULL , CVAR_ARCHIVE , " Attempts to normalize your voice levels to a standard level. Useful for lazy people, but interferes with voice activation levels. " , 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 )
{
2013-04-13 08:15:18 +00:00
int i , j ;
int active , known ;
2004-08-23 00:15:46 +00:00
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 )
{
2013-04-13 08:15:18 +00:00
Con_Printf ( " Audio Device: %s \n " , sc - > name ) ;
Con_Printf ( " %d channels, %gkhz, %d bit audio%s \n " , sc - > sn . numchannels , sc - > sn . speed / 1000.0 , sc - > sn . samplebits , sc - > selfpainting ? " , threaded " : " " ) ;
Con_Printf ( " %d samples in buffer \n " , sc - > sn . samples ) ;
for ( i = 0 , active = 0 , known = 0 ; i < sc - > total_chans ; i + + )
{
if ( sc - > channel [ i ] . sfx )
{
known + + ;
for ( j = 0 ; j < MAXSOUNDCHANNELS ; j + + )
{
if ( sc - > channel [ i ] . vol [ j ] )
{
active + + ;
break ;
}
}
if ( j < MAXSOUNDCHANNELS )
Con_Printf ( " %s (%i %i, %g %g %g, active) \n " , sc - > channel [ i ] . sfx - > name , sc - > channel [ i ] . entnum , sc - > channel [ i ] . entchannel , sc - > channel [ i ] . origin [ 0 ] , sc - > channel [ i ] . origin [ 1 ] , sc - > channel [ i ] . origin [ 2 ] ) ;
else
Con_DPrintf ( " %s (%i %i, %g %g %g, inactive) \n " , sc - > channel [ i ] . sfx - > name , sc - > channel [ i ] . entnum , sc - > channel [ i ] . entchannel , sc - > channel [ i ] . origin [ 0 ] , sc - > channel [ i ] . origin [ 1 ] , sc - > channel [ i ] . origin [ 2 ] ) ;
}
}
Con_Printf ( " %d/%d/%d/%d active/known/highest/max \n " , active , known , sc - > total_chans , MAX_CHANNELS ) ;
for ( i = 0 ; i < sc - > sn . numchannels ; i + + )
{
Con_Printf ( " chan %i: fwd:%-4g rt:%-4g up:%-4g dist:%-4g \n " , i , sc - > speakerdir [ i ] [ 0 ] , sc - > speakerdir [ i ] [ 1 ] , sc - > speakerdir [ i ] [ 2 ] , sc - > dist [ i ] ) ;
}
2004-08-23 00:15:46 +00:00
}
}
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>
2013-05-31 01:16:07 +00:00
enum
{
VOIP_SPEEX = 0 , //original supported codec
VOIP_RAW = 1 , //support is not recommended.
VOIP_OPUS = 2 , //supposed to be better than speex.
VOIP_INVALID = 16 //not currently generating audio.
} ;
2010-11-16 02:03:47 +00:00
static struct
2005-08-03 23:14:59 +00:00
{
2013-05-31 01:16:07 +00:00
struct
{
qboolean inited ;
qboolean loaded ;
dllhandle_t * speexlib ;
SpeexBits encbits ;
SpeexBits decbits [ MAX_CLIENTS ] ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
const SpeexMode * mode ;
} speex ;
struct
{
qboolean inited ;
qboolean loaded ;
dllhandle_t * speexdsplib ;
SpeexPreprocessState * preproc ; //filter out noise
int curframesize ;
int cursamplerate ;
} speexdsp ;
struct
{
qboolean inited ;
qboolean loaded ;
dllhandle_t * opuslib ;
} opus ;
unsigned char enccodec ;
2010-11-15 02:40:31 +00:00
void * encoder ;
2013-05-31 01:16:07 +00:00
unsigned int encframesize ;
unsigned int encsamplerate ;
2010-11-15 02:40:31 +00:00
void * decoder [ MAX_CLIENTS ] ;
2013-05-31 01:16:07 +00:00
unsigned char deccodec [ 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*/
2013-05-31 01:16:07 +00:00
unsigned int decsamplerate [ MAX_CLIENTS ] ;
unsigned int decframesize [ MAX_CLIENTS ] ;
2010-11-20 22:01:16 +00:00
float lastspoke [ MAX_CLIENTS ] ; /*time when they're no longer considered talking. if future, they're talking*/
2013-05-31 01:16:07 +00:00
float lastspoke_any ;
2010-11-20 22:01:16 +00:00
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*/
2013-05-31 01:16:07 +00:00
snd_capture_driver_t * cdriver ; /*capture driver's functions*/
void * cdriverctx ; /*capture driver context*/
} s_voip ;
# define OPUS_APPLICATION_VOIP 2048
# define OPUS_RESET_STATE 4028
# ifdef OPUS_STATIC
# include "opus.h"
# define qopus_encoder_create opus_encoder_create
# define qopus_encoder_destroy opus_encoder_destroy
# define qopus_encoder_ctl opus_encoder_ctl
# define qopus_encode opus_encode
# define qopus_decoder_create opus_decoder_create
# define qopus_decoder_destroy opus_decoder_destroy
# define qopus_decoder_ctl opus_decoder_ctl
# define qopus_decode opus_decode
# else
# define opus_int32 int
# define opus_int16 short
# define OpusEncoder void
# define OpusDecoder void
static OpusEncoder * ( VARGS * qopus_encoder_create ) ( opus_int32 Fs , int channels , int application , int * error ) ;
static void ( VARGS * qopus_encoder_destroy ) ( OpusEncoder * st ) ;
static int ( VARGS * qopus_encoder_ctl ) ( OpusEncoder * st , int request , . . . ) ;
static opus_int32 ( VARGS * qopus_encode ) ( OpusEncoder * st , const opus_int16 * pcm , int frame_size , unsigned char * data , opus_int32 max_data_bytes ) ;
static OpusDecoder * ( VARGS * qopus_decoder_create ) ( opus_int32 Fs , int channels , int * error ) ;
static void ( VARGS * qopus_decoder_destroy ) ( OpusDecoder * st ) ;
static int ( VARGS * qopus_decoder_ctl ) ( OpusDecoder * st , int request , . . . ) ;
static int ( VARGS * qopus_decode ) ( OpusDecoder * st , const unsigned char * data , opus_int32 len , opus_int16 * pcm , int frame_size , int decode_fec ) ;
static dllfunction_t qopusfuncs [ ] =
{
{ ( void * ) & qopus_encoder_create , " opus_encoder_create " } ,
{ ( void * ) & qopus_encoder_destroy , " opus_encoder_destroy " } ,
{ ( void * ) & qopus_encoder_ctl , " opus_encoder_ctl " } ,
{ ( void * ) & qopus_encode , " opus_encode " } ,
{ ( void * ) & qopus_decoder_create , " opus_decoder_create " } ,
{ ( void * ) & qopus_decoder_destroy , " opus_decoder_destroy " } ,
{ ( void * ) & qopus_decoder_ctl , " opus_decoder_ctl " } ,
{ ( void * ) & qopus_decode , " opus_decode " } ,
{ NULL }
} ;
# endif
2010-11-15 02:40:31 +00:00
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# ifdef SPEEX_STATIC
# define qspeex_lib_get_mode speex_lib_get_mode
# define qspeex_bits_init speex_bits_init
# define qspeex_bits_reset speex_bits_reset
# define qspeex_bits_write speex_bits_write
# define qspeex_preprocess_state_init speex_preprocess_state_init
2013-05-31 01:28:59 +00:00
# define qspeex_preprocess_state_destroy speex_preprocess_state_destroy
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# define qspeex_preprocess_ctl speex_preprocess_ctl
# define qspeex_preprocess_run speex_preprocess_run
# define qspeex_encoder_init speex_encoder_init
2013-05-31 01:28:59 +00:00
# define qspeex_encoder_destroy speex_encoder_destroy
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# define qspeex_encoder_ctl speex_encoder_ctl
# define qspeex_encode_int speex_encode_int
# define qspeex_decoder_init speex_decoder_init
2013-05-31 01:28:59 +00:00
# define qspeex_decoder_destroy speex_decoder_destroy
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# define qspeex_decode_int speex_decode_int
# define qspeex_bits_read_from speex_bits_read_from
# else
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 ) ;
2013-05-31 01:16:07 +00:00
static void ( VARGS * qspeex_preprocess_state_destroy ) ( SpeexPreprocessState * st ) ;
2010-11-16 02:03:47 +00:00
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 ) ;
2013-05-31 01:16:07 +00:00
static void ( VARGS * qspeex_decoder_destroy ) ( void * state ) ;
2010-11-16 02:03:47 +00:00
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 " } ,
2013-05-31 01:16:07 +00:00
{ ( void * ) & qspeex_decoder_destroy , " speex_decoder_destroy " } ,
2010-11-15 02:40:31 +00:00
{ ( 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 " } ,
2013-05-31 01:16:07 +00:00
{ ( void * ) & qspeex_preprocess_state_destroy , " speex_preprocess_state_destroy " } ,
2010-11-15 02:40:31 +00:00
{ ( void * ) & qspeex_preprocess_ctl , " speex_preprocess_ctl " } ,
{ ( void * ) & qspeex_preprocess_run , " speex_preprocess_run " } ,
{ NULL }
} ;
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# endif
2010-11-15 02:40:31 +00:00
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
2013-05-31 01:16:07 +00:00
static qboolean S_SpeexDSP_Init ( void )
2010-11-15 02:40:31 +00:00
{
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# ifndef SPEEX_STATIC
2013-05-31 01:16:07 +00:00
if ( s_voip . speexdsp . inited )
return s_voip . speexdsp . loaded ;
s_voip . speexdsp . inited = true ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
s_voip . speexdsp . speexdsplib = Sys_LoadLibrary ( " libspeexdsp " , qspeexdspfuncs ) ;
if ( ! s_voip . speexdsp . speexdsplib )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
Con_Printf ( " libspeexdsp not found. Your mic may be noisy. \n " ) ;
2010-11-15 02:40:31 +00:00
return false ;
}
2013-05-31 01:16:07 +00:00
# endif
s_voip . speexdsp . loaded = true ;
return s_voip . speexdsp . loaded ;
}
static qboolean S_Speex_Init ( void )
{
# ifndef SPEEX_STATIC
if ( s_voip . speex . inited )
return s_voip . speex . loaded ;
s_voip . speex . inited = true ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
s_voip . speex . speexlib = Sys_LoadLibrary ( " libspeex " , qspeexfuncs ) ;
if ( ! s_voip . speex . speexlib )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
Con_Printf ( " libspeex not found. Voice chat is not available. \n " ) ;
2010-11-15 02:40:31 +00:00
return false ;
}
Android: fat presses, vibrator, onscreen keyboard, keep-screen-on, console scaling, touch-based console scrolling, additional bindables.
Some memory leaks fixed.
latency with the nq protocol over loopback is much reduced.
Terrain: now mostly a property of a (q1 for now) bsp map, file format changed, glsl now built in, terrain editor builtin improved/changed, holes supported.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4067 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-07-14 16:25:18 +00:00
# endif
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
s_voip . speex . mode = qspeex_lib_get_mode ( SPEEX_MODEID_NB ) ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
s_voip . speex . loaded = true ;
return s_voip . speex . loaded ;
}
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
static qboolean S_Opus_Init ( void )
{
# ifndef OPUS_STATIC
# ifdef _WIN32
char * modulename = " libopus-0 " ARCH_DL_POSTFIX ;
# else
char * modulename = " libopus " ARCH_DL_POSTFIX " .0 " ;
# endif
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
if ( s_voip . opus . inited )
return s_voip . opus . loaded ;
s_voip . opus . inited = true ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
s_voip . opus . opuslib = Sys_LoadLibrary ( modulename , qopusfuncs ) ;
if ( ! s_voip . opus . opuslib )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
Con_Printf ( " %s not found. Voice chat is not available. \n " , modulename ) ;
return false ;
2010-11-15 02:40:31 +00:00
}
2013-05-31 01:16:07 +00:00
# endif
s_voip . opus . loaded = true ;
return s_voip . opus . loaded ;
2010-11-15 02:40:31 +00:00
}
2013-05-31 01:16:07 +00:00
void S_Voip_Decode ( unsigned int sender , unsigned int codec , unsigned int gen , unsigned char seq , unsigned int bytes , unsigned char * data )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
unsigned char * start ;
short decodebuf [ 8192 ] ;
2010-11-15 02:40:31 +00:00
unsigned int decodesamps , len , newseq , drops ;
2013-05-31 01:16:07 +00:00
int r ;
2010-11-20 22:01:16 +00:00
2013-05-31 01:16:07 +00:00
if ( sender > = MAX_CLIENTS )
2010-11-15 02:40:31 +00:00
return ;
2010-11-16 02:03:47 +00:00
2010-11-15 02:40:31 +00:00
decodesamps = 0 ;
newseq = 0 ;
drops = 0 ;
start = data ;
2010-11-20 22:01:16 +00:00
2013-05-31 01:16:07 +00:00
s_voip . lastspoke [ sender ] = realtime + 0.5 ;
if ( s_voip . lastspoke [ sender ] > s_voip . lastspoke_any )
s_voip . lastspoke_any = s_voip . lastspoke [ sender ] ;
//if they re-started speaking, flush any old state to avoid things getting weirdly delayed and reset the codec properly.
if ( s_voip . decgen [ sender ] ! = gen | | s_voip . deccodec [ sender ] ! = codec )
2010-11-20 22:01:16 +00:00
{
2013-05-31 01:16:07 +00:00
S_RawAudio ( sender , NULL , s_voip . decsamplerate [ sender ] , 0 , 1 , 2 , 0 ) ;
if ( s_voip . deccodec [ sender ] ! = codec )
{
//make sure old state is closed properly.
switch ( s_voip . deccodec [ sender ] )
{
case VOIP_SPEEX :
qspeex_decoder_destroy ( s_voip . decoder [ sender ] ) ;
break ;
case VOIP_OPUS :
qopus_decoder_destroy ( s_voip . decoder [ sender ] ) ;
break ;
}
s_voip . decoder [ sender ] = NULL ;
s_voip . deccodec [ sender ] = VOIP_INVALID ;
}
switch ( codec )
{
default : //codec not supported.
return ;
case VOIP_SPEEX :
if ( ! S_Speex_Init ( ) )
return ; //speex not usable.
s_voip . decsamplerate [ sender ] = 11025 ;
s_voip . decframesize [ sender ] = 160 ;
if ( ! s_voip . decoder [ sender ] )
{
qspeex_bits_init ( & s_voip . speex . decbits [ sender ] ) ;
qspeex_bits_reset ( & s_voip . speex . decbits [ sender ] ) ;
s_voip . decoder [ sender ] = qspeex_decoder_init ( s_voip . speex . mode ) ;
if ( ! s_voip . decoder [ sender ] )
return ;
}
else
qspeex_bits_reset ( & s_voip . speex . decbits [ sender ] ) ;
break ;
case VOIP_OPUS :
if ( ! S_Opus_Init ( ) )
return ;
//the lazy way to reset the codec!
if ( ! s_voip . decoder [ sender ] )
{
s_voip . decframesize [ sender ] = ( sizeof ( decodebuf ) / sizeof ( decodebuf [ 0 ] ) ) / 2 ; //this is the maximum size in a single frame.
//opus outputs to 8, 12, 16, 24, or 48khz. pick whichever has least excess samples and resample to fit it.
if ( snd_speed < = 8000 )
s_voip . decsamplerate [ sender ] = 8000 ;
else if ( snd_speed < = 12000 )
s_voip . decsamplerate [ sender ] = 12000 ;
else if ( snd_speed < = 16000 )
s_voip . decsamplerate [ sender ] = 16000 ;
else if ( snd_speed < = 24000 )
s_voip . decsamplerate [ sender ] = 24000 ;
else
s_voip . decsamplerate [ sender ] = 48000 ;
s_voip . decoder [ sender ] = qopus_decoder_create ( s_voip . decsamplerate [ sender ] , 1 /*FIXME: support stereo where possible*/ , NULL ) ;
if ( ! s_voip . decoder [ sender ] )
return ;
}
else
qopus_decoder_ctl ( s_voip . decoder [ sender ] , OPUS_RESET_STATE ) ;
break ;
}
s_voip . deccodec [ sender ] = codec ;
s_voip . decgen [ sender ] = gen ;
s_voip . decseq [ sender ] = seq ;
2010-11-20 22:01:16 +00:00
}
2010-11-15 02:40:31 +00:00
while ( bytes > 0 )
{
2013-05-31 01:16:07 +00:00
if ( decodesamps + s_voip . decframesize [ sender ] > sizeof ( decodebuf ) / sizeof ( decodebuf [ 0 ] ) )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
S_RawAudio ( sender , ( qbyte * ) decodebuf , s_voip . decsamplerate [ sender ] , decodesamps , 1 , 2 , cl_voip_play . value ) ;
2010-11-15 02:40:31 +00:00
decodesamps = 0 ;
}
2013-05-31 01:16:07 +00:00
switch ( codec )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
default :
bytes = 0 ;
break ;
case VOIP_SPEEX :
if ( s_voip . decseq [ sender ] ! = seq )
{
//tell speex about missing packets
qspeex_decode_int ( s_voip . decoder [ sender ] , NULL , decodebuf + decodesamps ) ;
drops + + ;
s_voip . decseq [ sender ] + + ;
}
else
{
//yay, we got data
bytes - - ;
len = * start + + ;
if ( bytes < len )
break ;
qspeex_bits_read_from ( & s_voip . speex . decbits [ sender ] , start , len ) ;
bytes - = len ;
start + = len ;
qspeex_decode_int ( s_voip . decoder [ sender ] , & s_voip . speex . decbits [ sender ] , decodebuf + decodesamps ) ;
newseq + + ;
}
decodesamps + = s_voip . decframesize [ sender ] ;
break ;
case VOIP_OPUS :
2010-11-15 02:40:31 +00:00
bytes - - ;
len = * start + + ;
2013-05-31 01:16:07 +00:00
if ( bytes < len )
break ;
// len = bytes;
if ( s_voip . decseq [ sender ] ! = seq )
{
r = qopus_decode ( s_voip . decoder [ sender ] , NULL , len , decodebuf + decodesamps , sizeof ( decodebuf ) / sizeof ( decodebuf [ 0 ] ) - decodesamps , false ) ;
drops + + ;
s_voip . decseq [ sender ] + + ;
}
else
{
r = qopus_decode ( s_voip . decoder [ sender ] , start , len , decodebuf + decodesamps , sizeof ( decodebuf ) / sizeof ( decodebuf [ 0 ] ) - decodesamps , false ) ;
newseq + + ;
}
if ( r > 0 )
decodesamps + = r ;
2010-11-15 02:40:31 +00:00
bytes - = len ;
start + = len ;
2013-05-31 01:16:07 +00:00
s_voip . decseq [ sender ] = seq + 1 ;
break ;
2010-11-16 02:03:47 +00:00
}
2010-11-15 02:40:31 +00:00
}
2013-05-31 01:16:07 +00:00
s_voip . decseq [ sender ] + = newseq ;
2010-11-15 02:40:31 +00:00
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 )
2013-05-31 01:16:07 +00:00
S_RawAudio ( sender , ( qbyte * ) decodebuf , s_voip . decsamplerate [ sender ] , decodesamps , 1 , 2 , cl_voip_play . value ) ;
2010-11-15 02:40:31 +00:00
}
2013-05-31 01:16:07 +00:00
void S_Voip_Parse ( void )
{
unsigned int sender ;
unsigned int bytes ;
unsigned char data [ 1024 ] ;
unsigned char seq , gen ;
unsigned char codec ;
sender = MSG_ReadByte ( ) ;
gen = MSG_ReadByte ( ) ;
codec = gen > > 4 ;
gen & = 0x0f ;
seq = MSG_ReadByte ( ) ;
bytes = MSG_ReadShort ( ) ;
if ( bytes > sizeof ( data ) | | cl_voip_play . value < = 0 )
{
MSG_ReadSkip ( bytes ) ;
return ;
}
MSG_ReadData ( data , bytes ) ;
sender % = MAX_CLIENTS ;
//if testing, don't get confused if the server is echoing voice too!
if ( cl_voip_test . ival )
if ( sender = = cl . playernum [ 0 ] )
return ;
S_Voip_Decode ( sender , codec , gen , seq , bytes , data ) ;
}
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
{
2013-05-31 01:16:07 +00:00
unsigned char outbuf [ 8192 ] ;
2010-11-15 02:40:31 +00:00
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 ;
2013-05-31 01:16:07 +00:00
int len ;
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 ;
2013-05-31 01:16:07 +00:00
int voipcodec = cl_voip_codec . ival ;
2010-11-20 22:01:16 +00:00
2013-05-31 01:16:07 +00:00
if ( buf )
{
/*if you're sending sound, you should be prepared to accept others yelling at you to shut up*/
if ( cl_voip_play . value < = 0 )
voipsendenable = false ;
if ( ! ( cls . fteprotocolextensions2 & PEXT2_VOICECHAT ) )
voipsendenable = false ;
}
else
voipsendenable = cl_voip_test . ival ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
voicevolumemod = s_voip . lastspoke_any > realtime ? cl_voip_ducking . value : 1 ;
if ( ! voipsendenable | | ( voipcodec ! = s_voip . enccodec & & s_voip . cdriver ) )
2005-08-03 23:14:59 +00:00
{
2013-05-31 01:16:07 +00:00
if ( s_voip . cdriver )
{
if ( s_voip . cdriverctx )
{
if ( s_voip . wantsend )
{
s_voip . cdriver - > Stop ( s_voip . cdriverctx ) ;
s_voip . wantsend = false ;
}
s_voip . cdriver - > Shutdown ( s_voip . cdriverctx ) ;
s_voip . cdriverctx = NULL ;
}
s_voip . cdriver = NULL ;
}
switch ( s_voip . enccodec )
2010-11-20 22:01:16 +00:00
{
2013-05-31 01:16:07 +00:00
case VOIP_SPEEX :
break ;
case VOIP_OPUS :
qopus_encoder_destroy ( s_voip . encoder ) ;
break ;
2010-11-20 22:01:16 +00:00
}
2013-05-31 01:16:07 +00:00
s_voip . encoder = NULL ;
s_voip . enccodec = VOIP_INVALID ;
if ( ! voipsendenable )
return ;
2010-11-15 02:40:31 +00:00
}
2010-11-20 22:01:16 +00:00
voipsendenable = cl_voip_send . ival > 0 ;
2013-05-31 01:16:07 +00:00
if ( ! s_voip . cdriver )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
s_voip . voiplevel = - 1 ;
2010-11-20 22:01:16 +00:00
/*only init the first time capturing is requested*/
if ( ! voipsendenable )
return ;
/*Add new drivers in order of priority*/
2013-05-31 01:16:07 +00:00
if ( ! s_voip . cdriver | | ! s_voip . cdriver - > Init )
s_voip . cdriver = & DSOUND_Capture ;
if ( ! s_voip . cdriver | | ! s_voip . cdriver - > Init )
s_voip . cdriver = & OSS_Capture ;
2010-11-20 22:01:16 +00:00
/*no way to capture audio, give up*/
2013-05-31 01:16:07 +00:00
if ( ! s_voip . cdriver | | ! s_voip . cdriver - > Init )
2010-11-20 22:01:16 +00:00
return ;
2013-05-31 01:16:07 +00:00
/*see if we can init our encoding codec...*/
switch ( voipcodec )
{
case VOIP_SPEEX :
if ( ! S_Speex_Init ( ) )
{
Con_Printf ( " Unable to use speex codec - not installed \n " ) ;
return ;
}
qspeex_bits_init ( & s_voip . speex . encbits ) ;
qspeex_bits_reset ( & s_voip . speex . encbits ) ;
s_voip . encoder = qspeex_encoder_init ( s_voip . speex . mode ) ;
if ( ! s_voip . encoder )
return ;
qspeex_encoder_ctl ( s_voip . encoder , SPEEX_GET_FRAME_SIZE , & s_voip . encframesize ) ;
qspeex_encoder_ctl ( s_voip . encoder , SPEEX_GET_SAMPLING_RATE , & s_voip . encsamplerate ) ;
s_voip . encsamplerate = 11025 ;
qspeex_encoder_ctl ( s_voip . encoder , SPEEX_SET_SAMPLING_RATE , & s_voip . encsamplerate ) ;
break ;
case VOIP_OPUS :
if ( ! S_Opus_Init ( ) )
{
Con_Printf ( " Unable to use opus codec - not installed \n " ) ;
return ;
}
//use whatever is convienient.
s_voip . encsamplerate = 48000 ;
s_voip . encframesize = s_voip . encsamplerate / 400 ; //2.5ms frames, at a minimum.
s_voip . encoder = qopus_encoder_create ( s_voip . encsamplerate , 1 , OPUS_APPLICATION_VOIP , NULL ) ;
if ( ! s_voip . encoder )
return ;
// opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps));
// opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
// opus_encoder_ctl(enc, OPUS_SET_VBR(use_vbr));
// opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr));
// opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
// opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(use_inbandfec));
// opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(forcechannels));
// opus_encoder_ctl(enc, OPUS_SET_DTX(use_dtx));
// opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
// opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&skip));
// opus_encoder_ctl(enc, OPUS_SET_LSB_DEPTH(16));
break ;
default :
Con_Printf ( " Unable to use that codec - not implemented \n " ) ;
//can't start up other coedcs, cos we're too lame.
2010-11-20 22:01:16 +00:00
return ;
2013-05-31 01:16:07 +00:00
}
s_voip . enccodec = voipcodec ;
s_voip . cdriverctx = s_voip . cdriver - > Init ( s_voip . encsamplerate ) ;
2010-11-20 22:01:16 +00:00
2013-05-31 01:16:07 +00:00
if ( ! s_voip . cdriverctx )
Con_Printf ( " No microphone detected \n " ) ;
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?*/
2013-05-31 01:16:07 +00:00
if ( ! s_voip . cdriverctx )
2010-11-16 02:03:47 +00:00
{
return ;
}
2013-05-31 01:16:07 +00:00
if ( ! voipsendenable & & s_voip . wantsend )
2010-11-20 22:01:16 +00:00
{
2013-05-31 01:16:07 +00:00
s_voip . wantsend = false ;
s_voip . capturepos + = s_voip . cdriver - > Update ( s_voip . cdriverctx , ( unsigned char * ) s_voip . capturebuf + s_voip . capturepos , 1 , sizeof ( s_voip . capturebuf ) - s_voip . capturepos ) ;
s_voip . cdriver - > Stop ( s_voip . cdriverctx ) ;
2010-11-20 22:01:16 +00:00
/*note: we still grab audio to flush everything that was captured while it was active*/
}
2013-05-31 01:16:07 +00:00
else if ( voipsendenable & & ! s_voip . wantsend )
2010-11-20 22:01:16 +00:00
{
2013-05-31 01:16:07 +00:00
s_voip . wantsend = true ;
if ( ! s_voip . capturepos )
2010-11-20 22:01:16 +00:00
{ /*if we were actually still sending, it was probably only off for a single frame, in which case don't reset it*/
2013-05-31 01:16:07 +00:00
s_voip . dumps = 0 ;
s_voip . generation + + ;
s_voip . encsequence = 0 ;
//reset codecs so they start with a clean slate when new audio blocks are generated.
switch ( s_voip . enccodec )
{
case VOIP_SPEEX :
qspeex_bits_reset ( & s_voip . speex . encbits ) ;
break ;
case VOIP_OPUS :
qopus_encoder_ctl ( s_voip . encoder , OPUS_RESET_STATE ) ;
break ;
}
2010-11-20 22:01:16 +00:00
}
else
{
2013-05-31 01:16:07 +00:00
s_voip . capturepos + = s_voip . cdriver - > Update ( s_voip . cdriverctx , ( unsigned char * ) s_voip . capturebuf + s_voip . capturepos , 1 , sizeof ( s_voip . capturebuf ) - s_voip . capturepos ) ;
2010-11-20 22:01:16 +00:00
}
2013-05-31 01:16:07 +00:00
s_voip . cdriver - > Start ( s_voip . cdriverctx ) ;
2010-11-20 22:01:16 +00:00
}
2013-05-31 01:16:07 +00:00
if ( s_voip . wantsend )
voicevolumemod = min ( voicevolumemod , cl_voip_capturingvol . value ) ;
s_voip . capturepos + = s_voip . cdriver - > Update ( s_voip . cdriverctx , ( unsigned char * ) s_voip . capturebuf + s_voip . capturepos , s_voip . encframesize * 2 , sizeof ( s_voip . capturebuf ) - s_voip . capturepos ) ;
2010-11-20 22:01:16 +00:00
2013-05-31 01:16:07 +00:00
if ( ! s_voip . wantsend & & s_voip . capturepos < s_voip . encframesize * 2 )
2010-11-20 22:01:16 +00:00
{
2013-05-31 01:16:07 +00:00
s_voip . voiplevel = - 1 ;
s_voip . capturepos = 0 ;
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
2013-05-31 01:16:07 +00:00
initseq = s_voip . encsequence ;
2010-11-20 22:01:16 +00:00
level = 0 ;
samps = 0 ;
2013-05-31 01:16:07 +00:00
//*2 for 16bit audio input.
for ( encpos = 0 , outpos = 0 ; s_voip . capturepos - encpos > = s_voip . encframesize * 2 & & sizeof ( outbuf ) - outpos > 64 ; s_voip . encsequence + + )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
start = ( short * ) ( s_voip . capturebuf + encpos ) ;
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
if ( cl_voip_noisefilter . ival | | cl_voip_autogain . ival )
{
if ( ! s_voip . speexdsp . preproc | | cl_voip_noisefilter . modified | | cl_voip_noisefilter . modified | | s_voip . speexdsp . curframesize ! = s_voip . encframesize | | s_voip . speexdsp . cursamplerate ! = s_voip . encsamplerate )
{
if ( s_voip . speexdsp . preproc )
qspeex_preprocess_state_destroy ( s_voip . speexdsp . preproc ) ;
s_voip . speexdsp . preproc = NULL ;
if ( S_SpeexDSP_Init ( ) )
{
int i ;
s_voip . speexdsp . preproc = qspeex_preprocess_state_init ( s_voip . encframesize , s_voip . encsamplerate ) ;
i = cl_voip_noisefilter . ival ;
qspeex_preprocess_ctl ( s_voip . speexdsp . preproc , SPEEX_PREPROCESS_SET_DENOISE , & i ) ;
i = cl_voip_autogain . ival ;
qspeex_preprocess_ctl ( s_voip . speexdsp . preproc , SPEEX_PREPROCESS_SET_AGC , & i ) ;
s_voip . speexdsp . curframesize = s_voip . encframesize ;
s_voip . speexdsp . cursamplerate = s_voip . encsamplerate ;
}
}
if ( s_voip . speexdsp . preproc )
qspeex_preprocess_run ( s_voip . speexdsp . preproc , start ) ;
}
2010-11-15 02:40:31 +00:00
2013-05-31 01:16:07 +00:00
for ( i = 0 ; i < s_voip . encframesize ; 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
}
2013-05-31 01:16:07 +00:00
switch ( s_voip . enccodec )
{
case VOIP_SPEEX :
qspeex_bits_reset ( & s_voip . speex . encbits ) ;
qspeex_encode_int ( s_voip . encoder , start , & s_voip . speex . encbits ) ;
len = qspeex_bits_write ( & s_voip . speex . encbits , outbuf + ( outpos + 1 ) , sizeof ( outbuf ) - ( outpos + 1 ) ) ;
if ( len < 0 | | len > 255 )
len = 0 ;
outbuf [ outpos ] = len ;
outpos + = 1 + len ;
break ;
case VOIP_OPUS :
len = qopus_encode ( s_voip . encoder , start , s_voip . encframesize , outbuf + ( outpos + 1 ) , max ( 255 , sizeof ( outbuf ) - ( outpos + 1 ) ) ) ;
if ( len = = 1 ) //packet does not need to be transmitted if it returns 1, supposedly. crazyness.
len = 0 ;
else if ( len > 0 )
{
outbuf [ outpos ] = len ;
outpos + = 1 + len ;
}
else
{
//error!
Con_Printf ( " Opus encoding error: %i \n " , len ) ;
}
break ;
default :
outbuf [ outpos ] = 0 ;
break ;
}
samps + = s_voip . encframesize ;
encpos + = s_voip . encframesize * 2 ;
2010-11-20 22:01:16 +00:00
}
if ( samps )
{
float nl ;
nl = ( 3000 * level ) / ( 32767.0f * 32767 * samps ) ;
2013-05-31 01:16:07 +00:00
s_voip . voiplevel = ( s_voip . voiplevel * 7 + nl ) / 8 ;
if ( s_voip . voiplevel < cl_voip_vad_threshhold . ival & & ! ( cl_voip_send . ival & 2 ) )
2010-11-20 22:01:16 +00:00
{
/*try and dump it, it was too quiet, and they're not pressing +voip*/
2013-05-31 01:16:07 +00:00
if ( s_voip . keeps > samps )
2010-11-20 22:01:16 +00:00
{
/*but not instantly*/
2013-05-31 01:16:07 +00:00
s_voip . keeps - = samps ;
2010-11-20 22:01:16 +00:00
}
else
{
outpos = 0 ;
2013-05-31 01:16:07 +00:00
s_voip . dumps + = samps ;
s_voip . keeps = 0 ;
2010-11-20 22:01:16 +00:00
}
}
2010-11-24 02:54:28 +00:00
else
2013-05-31 01:16:07 +00:00
s_voip . keeps = s_voip . encsamplerate * cl_voip_vad_delay . value ;
2010-11-20 22:01:16 +00:00
if ( outpos )
{
2013-05-31 01:16:07 +00:00
if ( s_voip . dumps > s_voip . encsamplerate / 4 )
s_voip . generation + + ;
s_voip . dumps = 0 ;
2010-11-20 22:01:16 +00:00
}
2010-11-15 02:40:31 +00:00
}
2013-05-31 01:16:07 +00:00
if ( outpos & & ( ! buf | | buf - > maxsize - buf - > cursize > = outpos + 4 ) )
2010-11-15 02:40:31 +00:00
{
2013-05-31 01:16:07 +00:00
if ( buf )
{
MSG_WriteByte ( buf , clc ) ;
MSG_WriteByte ( buf , ( s_voip . enccodec < < 4 ) | ( s_voip . 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*/
MSG_WriteByte ( buf , initseq ) ;
MSG_WriteShort ( buf , outpos ) ;
SZ_Write ( buf , outbuf , outpos ) ;
}
if ( cl_voip_test . ival )
S_Voip_Decode ( cl . playernum [ 0 ] , s_voip . enccodec , s_voip . generation & 0x0f , initseq , outpos , outbuf ) ;
//update our own lastspoke, so queries shows that we're speaking when we're speaking in a generic way, even if we can't hear ourselves.
//but don't update general lastspoke, so ducking applies only when others speak. use capturingvol for yourself. they're more explicit that way.
s_voip . lastspoke [ cl . playernum [ 0 ] ] = realtime + 0.5 ;
2010-11-15 02:40:31 +00:00
}
/*remove sent data*/
2013-05-31 01:16:07 +00:00
if ( encpos )
{
memmove ( s_voip . capturebuf , s_voip . capturebuf + encpos , s_voip . capturepos - encpos ) ;
s_voip . 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 ) ) ;
2013-05-31 01:16:07 +00:00
if ( s_voip . speexdsp . preproc )
qspeex_preprocess_ctl ( s_voip . speexdsp . preproc , SPEEX_PREPROCESS_SET_AGC_MAX_GAIN , & i ) ;
2010-11-20 22:01:16 +00:00
}
2010-11-16 02:03:47 +00:00
}
static void S_Voip_Play_Callback ( cvar_t * var , char * oldval )
{
if ( cls . fteprotocolextensions2 & PEXT2_VOICECHAT )
{
2013-05-31 01:16:07 +00:00
if ( var - > value > 0 )
2010-11-16 02:03:47 +00:00
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 )
{
2013-05-31 01:16:07 +00:00
if ( s_voip . voiplevel > 100 )
2010-11-20 22:01:16 +00:00
return 100 ;
2013-05-31 01:16:07 +00:00
if ( ! s_voip . cdriverctx | | ( ! ignorevad & & s_voip . dumps ) )
2010-11-20 22:01:16 +00:00
return - 1 ;
2013-05-31 01:16:07 +00:00
return s_voip . voiplevel ;
2010-11-20 22:01:16 +00:00
}
qboolean S_Voip_Speaking ( unsigned int plno )
{
if ( plno > = MAX_CLIENTS )
return false ;
2013-05-31 01:16:07 +00:00
return s_voip . lastspoke [ plno ] > realtime ;
}
void S_Voip_Init ( void )
{
int i ;
for ( i = 0 ; i < MAX_CLIENTS ; i + + )
s_voip . deccodec [ i ] = VOIP_INVALID ;
s_voip . enccodec = VOIP_INVALID ;
Cvar_Register ( & cl_voip_send , " Voice Chat " ) ;
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 " ) ;
Cvar_Register ( & cl_voip_play , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_test , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_ducking , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_micamp , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_codec , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_noisefilter , " Voice Chat " ) ;
Cvar_Register ( & cl_voip_autogain , " Voice Chat " ) ;
Cmd_AddCommand ( " +voip " , S_Voip_Enable_f ) ;
Cmd_AddCommand ( " -voip " , S_Voip_Disable_f ) ;
Cmd_AddCommand ( " voip " , S_Voip_f ) ;
2010-11-20 22:01:16 +00:00
}
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 ;
2012-09-30 05:52:03 +00:00
sounddriver pSNDIO_InitCard ;
2005-06-14 04:52:10 +00:00
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 ;
2013-03-12 23:09:25 +00:00
# ifdef NACL
extern sounddriver pPPAPI_InitCard ;
# endif
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
2012-09-30 05:52:03 +00:00
{ " Droid " , & pDroid_InitCard } , //prefered on android (java thread)
2010-03-14 14:35:56 +00:00
{ " AHI " , & pAHI_InitCard } , //prefered on morphos
2013-03-12 23:09:25 +00:00
# ifdef NACL
2012-04-09 19:12:12 +00:00
{ " PPAPI " , & pPPAPI_InitCard } , //google's native client
2013-03-12 23:09:25 +00:00
# endif
2012-09-30 05:52:03 +00:00
{ " SNDIO " , & pSNDIO_InitCard } , //prefered on OpenBSD
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
}
2012-11-27 03:23:19 +00:00
sound_started = true ; //!!sndcardinfo;
2005-07-14 01:57:34 +00:00
S_ClearRaw ( ) ;
2012-05-14 01:41:08 +00:00
CL_InitTEntSounds ( ) ;
2013-03-12 22:53:23 +00:00
ambient_sfx [ AMBIENT_WATER ] = S_PrecacheSound ( " ambience/water1.wav " ) ;
ambient_sfx [ AMBIENT_SKY ] = S_PrecacheSound ( " ambience/wind2.wav " ) ;
2004-08-23 00:15:46 +00:00
}
2012-07-05 19:42:36 +00:00
void S_SetUnderWater ( qboolean underwater )
2005-06-14 04:52:10 +00:00
{
soundcardinfo_t * sc ;
for ( sc = sndcardinfo ; sc ; sc = sc - > next )
2012-09-30 05:52:03 +00:00
if ( sc - > SetWaterDistortion )
sc - > SetWaterDistortion ( sc , underwater ) ;
2005-06-14 04:52:10 +00:00
}
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-07-05 19:42:36 +00:00
int i ;
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 ( ) ;
S_StopAllSounds ( true ) ;
2012-07-05 19:42:36 +00:00
for ( i = 1 ; i < MAX_SOUNDS ; i + + )
{
if ( ! cl . sound_name [ i ] [ 0 ] )
break ;
cl . sound_precache [ i ] = S_FindName ( cl . sound_name [ i ] ) ;
}
2004-08-23 00:15:46 +00:00
}
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
2013-05-31 01:16:07 +00:00
S_Voip_Init ( ) ;
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 " ) ;
2013-04-07 08:59:05 +00:00
# ifdef MULTITHREAD
Cvar_Register ( & snd_mixerthread , " Sound controls " ) ;
# endif
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 ;
}
// =======================================================================
// Shutdown sound engine
// =======================================================================
void S_ShutdownCard ( soundcardinfo_t * sc )
{
2006-09-17 00:59:22 +00:00
soundcardinfo_t * prev ;
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 ;
2006-09-17 00:59:22 +00:00
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
= = = = = = = = = = = = = = = = = =
*/
2012-07-05 19:42:36 +00:00
static sfx_t * S_FindName ( char * name )
2004-08-23 00:15:46 +00:00
{
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 ;
2013-03-12 22:53:23 +00:00
//make sure ambients are kept. silly ambients.
if ( retaintouched )
{
ambient_sfx [ AMBIENT_WATER ] = S_PrecacheSound ( " ambience/water1.wav " ) ;
ambient_sfx [ AMBIENT_SKY ] = S_PrecacheSound ( " ambience/wind2.wav " ) ;
}
2013-03-12 23:09:49 +00:00
if ( ! num_sfx )
return ;
2012-02-27 12:23:15 +00:00
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 [ ch_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 ;
2013-05-31 01:16:07 +00:00
int i , v ;
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
2013-05-31 01:16:07 +00:00
if ( ch - > flags & CF_ABSVOLUME )
{
v = ch - > master_vol ;
for ( i = 0 ; i < sc - > sn . numchannels ; i + + )
{
ch - > vol [ i ] = v ;
}
return ;
}
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
{
2013-05-31 01:16:07 +00:00
v = ch - > master_vol * ( ruleset_allow_localvolume . value ? snd_playersoundvolume . value : 1 ) * volume . value * voicevolumemod ;
v = bound ( 0 , v , 255 ) ;
2005-01-13 16:29:20 +00:00
for ( i = 0 ; i < sc - > sn . numchannels ; i + + )
{
2013-05-31 01:16:07 +00:00
ch - > vol [ i ] = v ;
2005-01-13 16:29:20 +00:00
}
2004-08-23 00:15:46 +00:00
return ;
}
// calculate stereo seperation and distance attenuation
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
2013-03-12 22:53:23 +00:00
//rotate the world_vec into listener space, so that the audio direction stored in the speakerdir array can be used directly.
2010-11-06 23:05:29 +00:00
listener_vec [ 0 ] = DotProduct ( listener_forward , world_vec ) ;
2013-03-12 22:53:23 +00:00
listener_vec [ 1 ] = DotProduct ( listener_right , world_vec ) ;
2010-11-06 23:05:29 +00:00
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
{
2013-03-12 22:53:23 +00:00
scale = 1 + DotProduct ( listener_vec , sc - > speakerdir [ i ] ) ;
2005-01-13 16:29:20 +00:00
scale = ( 1.0 - dist ) * scale * sc - > dist [ i ] ;
2013-05-31 01:16:07 +00:00
v = ch - > master_vol * scale * volume . value * voicevolumemod ;
ch - > vol [ i ] = bound ( 0 , v , 255 ) ;
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 ) ;
}
2013-05-31 01:16:07 +00:00
target_chan - > flags = 0 ;
2004-08-23 00:15:46 +00:00
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 ;
------------------------------------------------------------------------
r4169 | acceptthis | 2013-01-17 08:55:12 +0000 (Thu, 17 Jan 2013) | 31 lines
removed MAX_VISEDICTS limit.
PEXT2_REPLACEMENTDELTAS tweaked, now has 4 million entity limit. still not enabled by default.
TE_BEAM now maps to a separate TEQW_BEAM to avoid conflicts with QW.
added android multitouch emulation for windows/rawinput (in_simulatemultitouch).
split topcolor/bottomcolor from scoreboard, for dp's colormap|1024 feature.
now using utf-8 for windows consoles.
qcc warnings/errors now give clickable console links for quick+easy editing.
disabled menutint when the currently active item changes contrast or gamma (for OneManClan).
Added support for drawfont/drawfontscale.
tweaked the qcvm a little to reduce the number of pointers.
.doll file loading. still experimental and will likely crash. requires csqc active, even if its a dummy progs. this will be fixed in time. Still other things that need cleaning up.
windows: gl_font "?" shows the standard windows font-selection dialog, and can be used to select windows fonts. not all work. and you probably don't want to use windings.
fixed splitscreen support when playing mvds. added mini-scoreboards to splitscreen.
editor/debugger now shows asm if there's no linenumber info. also, pressing f1 for help shows the shortcuts.
Added support for .framegroups files for psk(psa) and iqm formats.
True support for ezquake's colour codes. Mutually exclusive with background colours.
path command output slightly more readable.
added support for digest_hex (MD4, SHA1, CRC16).
skingroups now colourmap correctly.
Fix terrain colour hints, and litdata from the wrong bsp.
fix ftp dual-homed issue. support epsv command, and enable ipv6 (eprt still not supported).
remove d3d11 compilation from the makefile. the required headers are not provided by mingw, and are not available to the build bot, so don't bother.
fix v *= v.x and similar opcodes.
fteqcc: fixed support for áéÃóú type chars in names. utf-8 files now properly supported (even with the utf-8 bom/identifier). utf-16 also supported.
fteqcc: fixed '#if 1 == 3 && 4' parsing.
fteqcc: -Werror acts on the warning, rather than as a separate error. Line numbers are thus more readable.
fteqcc: copyright message now includes compile date instead.
fteqccgui: the treeview control is now coloured depending on whether there were warnings/errors in the last compile.
fteqccgui: the output window is now focused and scrolls down as compilation progresses.
pr_dumpplatform command dumps out some pragmas to convert more serious warnings to errors. This is to avoid the infamous 'fteqcc sucks cos my code sucks' issue.
rewrote prespawn/modelist/soundlist code. server tracks progress now.
------------------------------------------------------------------------
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4167 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-03-12 22:29:40 +00:00
if ( cls . demoseeking )
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-05-14 01:41:08 +00:00
if ( newmusic & & ! 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 ;
}
}
}
if ( chan - > sfx )
2013-05-31 01:16:07 +00:00
{
chan - > master_vol = 255 ; //bypasses volume cvar completely.
chan - > vol [ 0 ] = chan - > vol [ 1 ] = chan - > vol [ 2 ] = chan - > vol [ 3 ] = chan - > vol [ 4 ] = chan - > vol [ 5 ] = bound ( 0 , chan - > master_vol * bgmvolume . value * voicevolumemod , 255 ) ;
}
2004-08-23 00:15:46 +00:00
}
// 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 ) ;
2013-03-12 22:53:23 +00:00
if ( ! l | | ambient_level . value < = 0 )
2004-08-23 00:15:46 +00:00
{
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 ] ;
2013-05-31 01:16:07 +00:00
chan - > vol [ 0 ] = chan - > vol [ 1 ] = chan - > vol [ 2 ] = chan - > vol [ 3 ] = chan - > vol [ 4 ] = chan - > vol [ 5 ] = bound ( 0 , chan - > master_vol * ( volume . value * voicevolumemod ) , 255 ) ;
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
2012-10-14 09:00:49 +00:00
INS_Accumulate ( ) ;
2004-08-23 00:15:46 +00:00
# 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
{
int i ;
sfx_t * sfx ;
sfxcache_t * sc ;
2012-10-08 04:36:10 +00:00
sfxcache_t scachebuf ;
2004-08-23 00:15:46 +00:00
int size , total ;
2012-10-08 04:36:10 +00:00
int duration ;
S_LockMixer ( ) ;
2004-08-23 00:15:46 +00:00
total = 0 ;
for ( sfx = known_sfx , i = 0 ; i < num_sfx ; i + + , sfx + + )
{
2012-10-08 04:36:10 +00:00
if ( sfx - > decoder . decodedata )
2012-02-27 12:23:15 +00:00
{
2012-10-08 04:36:10 +00:00
sc = sfx - > decoder . decodedata ( sfx , & scachebuf , 0 , 0x0fffffff ) ;
if ( ! sc )
{
Con_Printf ( " S( ) : %s \n " , sfx - > name ) ;
continue ;
}
2012-02-27 12:23:15 +00:00
}
2012-10-08 04:36:10 +00:00
else
sc = sfx - > decoder . buf ;
2004-08-23 00:15:46 +00:00
if ( ! sc )
2012-10-08 04:36:10 +00:00
{
Con_Printf ( " ?( ) : %s \n " , sfx - > name ) ;
2004-08-23 00:15:46 +00:00
continue ;
2012-10-08 04:36:10 +00:00
}
size = ( sc - > soundoffset + sc - > length ) * sc - > width * ( sc - > numchannels ) ;
duration = ( sc - > soundoffset + sc - > length ) / sc - > speed ;
2004-08-23 00:15:46 +00:00
total + = size ;
if ( sc - > loopstart > = 0 )
Con_Printf ( " L " ) ;
else
Con_Printf ( " " ) ;
2012-10-08 04:36:10 +00:00
Con_Printf ( " (%2db%2ic) %6i %2is : %s \n " , sc - > width * 8 , sc - > numchannels , size , duration , sfx - > name ) ;
2004-08-23 00:15:46 +00:00
}
Con_Printf ( " Total resident: %i \n " , total ) ;
2012-10-08 04:36:10 +00:00
S_UnlockMixer ( ) ;
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
2013-05-31 01:16:07 +00:00
void S_RawAudio ( int sourceid , qbyte * data , int speed , int samples , int channels , int width , float volume )
2004-08-23 00:15:46 +00:00
{
soundcardinfo_t * si ;
int i ;
int prepadl ;
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 ;
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 ) ;
2013-05-31 01:16:07 +00:00
c - > flags = CF_ABSVOLUME ;
2012-02-27 12:23:15 +00:00
c - > entnum = - 1 ;
c - > entchannel = 0 ;
c - > dist_mult = 0 ;
c - > looping = false ;
2013-05-31 01:16:07 +00:00
c - > master_vol = 255 * volume ;
2012-02-27 12:23:15 +00:00
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
}