2009-02-28 14:41:18 +00:00
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
|
|
* your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
*
|
|
|
|
* See the GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2010-07-13 18:19:42 +00:00
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
|
|
* USA.
|
2010-06-19 08:47:14 +00:00
|
|
|
*
|
|
|
|
* =======================================================================
|
|
|
|
*
|
2010-07-13 18:19:42 +00:00
|
|
|
* Upper layer of the sound output. This is implemented via DMA, thus
|
|
|
|
* needs a DMA capable lower level implementation for painting the
|
|
|
|
* sounds to the device. Otherwise it'll be horible slow and stuttering.
|
2010-06-19 08:47:14 +00:00
|
|
|
*
|
|
|
|
* =======================================================================
|
|
|
|
*/
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2009-03-03 13:43:32 +00:00
|
|
|
#include "../header/client.h"
|
2010-06-19 19:10:31 +00:00
|
|
|
#include "header/local.h"
|
2010-06-19 19:04:39 +00:00
|
|
|
#include "header/vorbis.h"
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
void S_Play(void);
|
|
|
|
void S_SoundList(void);
|
|
|
|
void S_Update_();
|
|
|
|
void S_StopAllSounds(void);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* only begin attenuating sound volumes when outside the FULLVOLUME range */
|
2009-02-28 14:41:18 +00:00
|
|
|
#define SOUND_FULLVOLUME 80
|
|
|
|
#define SOUND_LOOPATTENUATE 0.003
|
|
|
|
|
|
|
|
int s_registration_sequence;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
channel_t channels[MAX_CHANNELS];
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
qboolean snd_initialized = false;
|
|
|
|
int sound_started=0;
|
|
|
|
|
|
|
|
dma_t dma;
|
|
|
|
|
|
|
|
vec3_t listener_origin;
|
|
|
|
vec3_t listener_forward;
|
|
|
|
vec3_t listener_right;
|
|
|
|
vec3_t listener_up;
|
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
static qboolean s_registering;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
static int soundtime;
|
2010-06-19 08:47:14 +00:00
|
|
|
int paintedtime;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* during registration it is possible to have more sounds
|
|
|
|
than could actually be referenced during gameplay,
|
|
|
|
because we don't want to free anything until we are
|
|
|
|
sure we won't need it. */
|
2010-10-13 18:00:39 +00:00
|
|
|
#define MAX_SFX (MAX_SOUNDS*2)
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx_t known_sfx[MAX_SFX];
|
|
|
|
int num_sfx;
|
|
|
|
|
2010-10-13 18:00:39 +00:00
|
|
|
#define MAX_PLAYSOUNDS 128
|
2009-02-28 14:41:18 +00:00
|
|
|
playsound_t s_playsounds[MAX_PLAYSOUNDS];
|
|
|
|
playsound_t s_freeplays;
|
|
|
|
playsound_t s_pendingplays;
|
|
|
|
|
|
|
|
int s_beginofs;
|
|
|
|
|
|
|
|
cvar_t *s_volume;
|
|
|
|
cvar_t *s_testsound;
|
|
|
|
cvar_t *s_loadas8bit;
|
|
|
|
cvar_t *s_khz;
|
|
|
|
cvar_t *s_mixahead;
|
2010-10-13 17:07:29 +00:00
|
|
|
cvar_t *s_show;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
int s_rawend;
|
2009-02-28 14:41:18 +00:00
|
|
|
portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/*
|
|
|
|
* User-setable variables
|
|
|
|
*/
|
|
|
|
void S_SoundInfo_f(void) {
|
|
|
|
if (!sound_started) {
|
2009-02-28 14:41:18 +00:00
|
|
|
Com_Printf ("sound system not started\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
Com_Printf("%5d stereo\n", dma.channels - 1);
|
|
|
|
Com_Printf("%5d samples\n", dma.samples);
|
|
|
|
Com_Printf("%5d samplepos\n", dma.samplepos);
|
|
|
|
Com_Printf("%5d samplebits\n", dma.samplebits);
|
|
|
|
Com_Printf("%5d submission_chunk\n", dma.submission_chunk);
|
|
|
|
Com_Printf("%5d speed\n", dma.speed);
|
2010-10-13 17:07:29 +00:00
|
|
|
Com_Printf("%p dma buffer\n", dma.buffer);
|
2010-06-19 08:47:14 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_Init (void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
cvar_t *cv;
|
|
|
|
|
|
|
|
Com_Printf("\n------- sound initialization -------\n");
|
|
|
|
|
|
|
|
cv = Cvar_Get ("s_initsound", "1", 0);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!cv->value)
|
|
|
|
Com_Printf ("not initializing.\n");
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
else {
|
2009-02-28 14:41:18 +00:00
|
|
|
s_volume = Cvar_Get ("s_volume", "0.7", CVAR_ARCHIVE);
|
2009-03-10 17:36:01 +00:00
|
|
|
s_khz = Cvar_Get ("s_khz", "22", CVAR_ARCHIVE);
|
2010-02-01 15:09:47 +00:00
|
|
|
s_loadas8bit = Cvar_Get ("s_loadas8bit", "0", CVAR_ARCHIVE);
|
2010-07-13 18:42:12 +00:00
|
|
|
s_mixahead = Cvar_Get ("s_mixahead", "0.14", CVAR_ARCHIVE);
|
2009-02-28 14:41:18 +00:00
|
|
|
s_show = Cvar_Get ("s_show", "0", 0);
|
|
|
|
s_testsound = Cvar_Get ("s_testsound", "0", 0);
|
|
|
|
|
|
|
|
Cmd_AddCommand("play", S_Play);
|
|
|
|
Cmd_AddCommand("stopsound", S_StopAllSounds);
|
|
|
|
Cmd_AddCommand("soundlist", S_SoundList);
|
|
|
|
Cmd_AddCommand("soundinfo", S_SoundInfo_f);
|
2009-10-03 16:06:45 +00:00
|
|
|
Cmd_AddCommand("ogg_init", OGG_Init);
|
|
|
|
Cmd_AddCommand("ogg_shutdown", OGG_Shutdown);
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
if (!SNDDMA_Init())
|
|
|
|
return;
|
|
|
|
|
|
|
|
S_InitScaletable ();
|
|
|
|
|
|
|
|
sound_started = 1;
|
|
|
|
num_sfx = 0;
|
|
|
|
|
|
|
|
soundtime = 0;
|
|
|
|
paintedtime = 0;
|
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
Com_Printf ("sound sampling rate: %i\n", dma.speed);
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
S_StopAllSounds ();
|
2009-10-03 16:06:45 +00:00
|
|
|
OGG_Init();
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Com_Printf("------------------------------------\n");
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/*
|
|
|
|
* Shutdown sound engine
|
|
|
|
*/
|
|
|
|
void S_Shutdown(void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
sfx_t *sfx;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
// free all sounds
|
|
|
|
for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++)
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sfx->name[0])
|
|
|
|
continue;
|
|
|
|
if (sfx->cache)
|
|
|
|
Z_Free (sfx->cache);
|
|
|
|
}
|
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
memset (known_sfx, 0, sizeof(known_sfx));
|
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
num_sfx = 0;
|
2010-10-13 17:07:29 +00:00
|
|
|
sound_started = 0;
|
|
|
|
|
|
|
|
OGG_Shutdown();
|
|
|
|
SNDDMA_Shutdown();
|
|
|
|
|
|
|
|
Cmd_RemoveCommand("soundlist");
|
|
|
|
Cmd_RemoveCommand("soundinfo");
|
|
|
|
Cmd_RemoveCommand("play");
|
|
|
|
Cmd_RemoveCommand("stopsound");
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-19 17:04:32 +00:00
|
|
|
* Returns the name of a sound
|
2010-06-19 08:47:14 +00:00
|
|
|
*/
|
|
|
|
sfx_t *S_FindName (char *name, qboolean create) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
sfx_t *sfx;
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
Com_Error (ERR_FATAL, "S_FindName: NULL\n");
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!name[0])
|
|
|
|
Com_Error (ERR_FATAL, "S_FindName: empty name\n");
|
|
|
|
|
|
|
|
if (strlen(name) >= MAX_QPATH)
|
|
|
|
Com_Error (ERR_FATAL, "Sound name too long: %s", name);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* see if already loaded */
|
2009-02-28 14:41:18 +00:00
|
|
|
for (i=0 ; i < num_sfx ; i++)
|
2010-06-19 08:47:14 +00:00
|
|
|
if (!strcmp(known_sfx[i].name, name)) {
|
2009-02-28 14:41:18 +00:00
|
|
|
return &known_sfx[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!create)
|
|
|
|
return NULL;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* find a free sfx */
|
2009-02-28 14:41:18 +00:00
|
|
|
for (i=0 ; i < num_sfx ; i++)
|
|
|
|
if (!known_sfx[i].name[0])
|
|
|
|
break;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (i == num_sfx) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (num_sfx == MAX_SFX)
|
|
|
|
Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
num_sfx++;
|
|
|
|
}
|
2010-10-13 17:07:29 +00:00
|
|
|
|
|
|
|
if (strlen(name) >= MAX_QPATH-1)
|
|
|
|
Com_Error (ERR_FATAL, "Sound name too long: %s", name);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx = &known_sfx[i];
|
2010-10-13 17:07:29 +00:00
|
|
|
sfx->truename = NULL;
|
2009-02-28 14:41:18 +00:00
|
|
|
strcpy (sfx->name, name);
|
|
|
|
sfx->registration_sequence = s_registration_sequence;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
return sfx;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
sfx_t *S_AliasName (char *aliasname, char *truename) {
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx_t *sfx;
|
|
|
|
char *s;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
s = Z_Malloc (MAX_QPATH);
|
|
|
|
strcpy (s, truename);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* find a free sfx */
|
2009-02-28 14:41:18 +00:00
|
|
|
for (i=0 ; i < num_sfx ; i++)
|
|
|
|
if (!known_sfx[i].name[0])
|
|
|
|
break;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (i == num_sfx) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (num_sfx == MAX_SFX)
|
|
|
|
Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
num_sfx++;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx = &known_sfx[i];
|
2010-10-13 17:07:29 +00:00
|
|
|
sfx->cache = NULL;
|
2009-02-28 14:41:18 +00:00
|
|
|
strcpy (sfx->name, aliasname);
|
|
|
|
sfx->registration_sequence = s_registration_sequence;
|
|
|
|
sfx->truename = s;
|
|
|
|
|
|
|
|
return sfx;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_BeginRegistration (void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
s_registration_sequence++;
|
|
|
|
s_registering = true;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
sfx_t *S_RegisterSound (char *name) {
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx_t *sfx;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sfx = S_FindName (name, true);
|
|
|
|
sfx->registration_sequence = s_registration_sequence;
|
|
|
|
|
|
|
|
if (!s_registering)
|
|
|
|
S_LoadSound (sfx);
|
|
|
|
|
|
|
|
return sfx;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_EndRegistration (void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
sfx_t *sfx;
|
|
|
|
int size;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* free any sounds not from this registration sequence */
|
|
|
|
for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sfx->name[0])
|
|
|
|
continue;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (sfx->registration_sequence != s_registration_sequence) {
|
2010-10-13 17:07:29 +00:00
|
|
|
if (sfx->cache) // it is possible to have a leftover
|
|
|
|
Z_Free (sfx->cache); // from a server that didn't finish loading
|
|
|
|
if (sfx->truename)
|
|
|
|
Z_Free (sfx->truename); // memleak fix from echon
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
sfx->cache = NULL;
|
|
|
|
sfx->name[0] = 0;
|
2010-06-19 08:47:14 +00:00
|
|
|
} else {
|
|
|
|
/* make sure it is paged in, this is a relict of the late 90th */
|
|
|
|
if (sfx->cache) {
|
2009-02-28 14:41:18 +00:00
|
|
|
size = sfx->cache->length*sfx->cache->width;
|
|
|
|
Com_PageInMemory ((byte *)sfx->cache, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* load everything in */
|
|
|
|
for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sfx->name[0])
|
|
|
|
continue;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
S_LoadSound (sfx);
|
|
|
|
}
|
|
|
|
|
|
|
|
s_registering = false;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
channel_t *S_PickChannel(int entnum, int entchannel) {
|
|
|
|
int ch_idx;
|
|
|
|
int first_to_die;
|
|
|
|
int life_left;
|
2009-02-28 14:41:18 +00:00
|
|
|
channel_t *ch;
|
|
|
|
|
|
|
|
if (entchannel<0)
|
|
|
|
Com_Error (ERR_DROP, "S_PickChannel: entchannel<0");
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* Check for replacement sound, or find the best one to replace */
|
|
|
|
first_to_die = -1;
|
|
|
|
life_left = 0x7fffffff;
|
|
|
|
|
|
|
|
for (ch_idx=0 ; ch_idx < MAX_CHANNELS ; ch_idx++) {
|
|
|
|
if (entchannel != 0 /* channel 0 never overrides */
|
|
|
|
&& channels[ch_idx].entnum == entnum
|
|
|
|
&& channels[ch_idx].entchannel == entchannel) {
|
|
|
|
/* always override sound from same entity */
|
2009-02-28 14:41:18 +00:00
|
|
|
first_to_die = ch_idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* don't let monster sounds override player sounds */
|
2009-02-28 14:41:18 +00:00
|
|
|
if (channels[ch_idx].entnum == cl.playernum+1 && entnum != cl.playernum+1 && channels[ch_idx].sfx)
|
|
|
|
continue;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (channels[ch_idx].end - paintedtime < life_left) {
|
2009-02-28 14:41:18 +00:00
|
|
|
life_left = channels[ch_idx].end - paintedtime;
|
|
|
|
first_to_die = ch_idx;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
if (first_to_die == -1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ch = &channels[first_to_die];
|
|
|
|
memset (ch, 0, sizeof(*ch));
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
return ch;
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Used for spatializing channels and autosounds
|
|
|
|
*/
|
|
|
|
void S_SpatializeOrigin (vec3_t origin, float master_vol, float dist_mult, int *left_vol, int *right_vol) {
|
|
|
|
vec_t dot;
|
|
|
|
vec_t dist;
|
|
|
|
vec_t lscale, rscale, scale;
|
|
|
|
vec3_t source_vec;
|
|
|
|
|
|
|
|
if (cls.state != ca_active) {
|
2009-02-28 14:41:18 +00:00
|
|
|
*left_vol = *right_vol = 255;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* calculate stereo seperation and distance attenuation */
|
2009-02-28 14:41:18 +00:00
|
|
|
VectorSubtract(origin, listener_origin, source_vec);
|
|
|
|
|
|
|
|
dist = VectorNormalize(source_vec);
|
|
|
|
dist -= SOUND_FULLVOLUME;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (dist < 0)
|
2010-06-19 17:04:32 +00:00
|
|
|
dist = 0; /* close enough to be at full volume */
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-06-19 17:04:32 +00:00
|
|
|
dist *= dist_mult; /* different attenuation levels */
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
dot = DotProduct(listener_right, source_vec);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (dma.channels == 1 || !dist_mult) {
|
|
|
|
/* no attenuation = no spatialization */
|
2009-03-03 11:00:08 +00:00
|
|
|
rscale = 1.0f;
|
|
|
|
lscale = 1.0f;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else {
|
2009-03-03 11:00:08 +00:00
|
|
|
rscale = 0.5f * (1.0f + dot);
|
|
|
|
lscale = 0.5f * (1.0f - dot);
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* add in distance effect */
|
2009-03-03 11:00:08 +00:00
|
|
|
scale = (1.0f - dist) * rscale;
|
2009-02-28 14:41:18 +00:00
|
|
|
*right_vol = (int) (master_vol * scale);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (*right_vol < 0)
|
|
|
|
*right_vol = 0;
|
|
|
|
|
|
|
|
scale = (1.0 - dist) * lscale;
|
|
|
|
*left_vol = (int) (master_vol * scale);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (*left_vol < 0)
|
|
|
|
*left_vol = 0;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_Spatialize(channel_t *ch) {
|
2009-02-28 14:41:18 +00:00
|
|
|
vec3_t origin;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* anything coming from the view entity will always be full volume */
|
|
|
|
if (ch->entnum == cl.playernum+1) {
|
2009-02-28 14:41:18 +00:00
|
|
|
ch->leftvol = ch->master_vol;
|
|
|
|
ch->rightvol = ch->master_vol;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (ch->fixed_origin) {
|
2009-02-28 14:41:18 +00:00
|
|
|
VectorCopy (ch->origin, origin);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else
|
2009-02-28 14:41:18 +00:00
|
|
|
CL_GetEntitySoundOrigin (ch->entnum, origin);
|
|
|
|
|
2009-03-03 11:00:08 +00:00
|
|
|
S_SpatializeOrigin (origin, (float)ch->master_vol, ch->dist_mult, &ch->leftvol, &ch->rightvol);
|
2010-06-19 08:47:14 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
playsound_t *S_AllocPlaysound (void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
playsound_t *ps;
|
|
|
|
|
|
|
|
ps = s_freeplays.next;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (ps == &s_freeplays)
|
2010-10-13 18:00:39 +00:00
|
|
|
{
|
2010-06-19 08:47:14 +00:00
|
|
|
return NULL; /* no free playsounds, this results in stuttering an cracking */
|
2010-10-13 18:00:39 +00:00
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* unlink from freelist */
|
2009-02-28 14:41:18 +00:00
|
|
|
ps->prev->next = ps->next;
|
|
|
|
ps->next->prev = ps->prev;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
return ps;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_FreePlaysound (playsound_t *ps) {
|
|
|
|
/* unlink from channel */
|
2009-02-28 14:41:18 +00:00
|
|
|
ps->prev->next = ps->next;
|
|
|
|
ps->next->prev = ps->prev;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* add to free list */
|
2009-02-28 14:41:18 +00:00
|
|
|
ps->next = s_freeplays.next;
|
|
|
|
s_freeplays.next->prev = ps;
|
|
|
|
ps->prev = &s_freeplays;
|
|
|
|
s_freeplays.next = ps;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Take the next playsound and begin it on the channel
|
|
|
|
* This is never called directly by S_Play*, but only
|
|
|
|
* by the update loop.
|
2009-02-28 14:41:18 +00:00
|
|
|
*/
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_IssuePlaysound (playsound_t *ps) {
|
2009-02-28 14:41:18 +00:00
|
|
|
channel_t *ch;
|
|
|
|
sfxcache_t *sc;
|
|
|
|
|
|
|
|
if (s_show->value)
|
|
|
|
Com_Printf ("Issue %i\n", ps->begin);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
/* pick a channel to play on */
|
2009-02-28 14:41:18 +00:00
|
|
|
ch = S_PickChannel(ps->entnum, ps->entchannel);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (!ch) {
|
2009-02-28 14:41:18 +00:00
|
|
|
S_FreePlaysound (ps);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* spatialize */
|
2009-02-28 14:41:18 +00:00
|
|
|
if (ps->attenuation == ATTN_STATIC)
|
2009-03-03 11:00:08 +00:00
|
|
|
ch->dist_mult = ps->attenuation * 0.001f;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
else
|
2009-03-03 11:00:08 +00:00
|
|
|
ch->dist_mult = ps->attenuation * 0.0005f;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
ch->master_vol = (int)ps->volume;
|
2009-02-28 14:41:18 +00:00
|
|
|
ch->entnum = ps->entnum;
|
|
|
|
ch->entchannel = ps->entchannel;
|
|
|
|
ch->sfx = ps->sfx;
|
|
|
|
VectorCopy (ps->origin, ch->origin);
|
|
|
|
ch->fixed_origin = ps->fixed_origin;
|
|
|
|
|
|
|
|
S_Spatialize(ch);
|
|
|
|
|
|
|
|
ch->pos = 0;
|
|
|
|
sc = S_LoadSound (ch->sfx);
|
2010-06-19 08:47:14 +00:00
|
|
|
ch->end = paintedtime + sc->length;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* free the playsound */
|
2009-02-28 14:41:18 +00:00
|
|
|
S_FreePlaysound (ps);
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
struct sfx_s *S_RegisterSexedSound (entity_state_t *ent, char *base) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int n;
|
|
|
|
char *p;
|
2010-10-13 17:07:29 +00:00
|
|
|
int len;
|
2009-02-28 14:41:18 +00:00
|
|
|
struct sfx_s *sfx;
|
|
|
|
char model[MAX_QPATH];
|
|
|
|
char sexedFilename[MAX_QPATH];
|
|
|
|
char maleFilename[MAX_QPATH];
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* determine what model the client is using */
|
2009-02-28 14:41:18 +00:00
|
|
|
model[0] = 0;
|
|
|
|
n = CS_PLAYERSKINS + ent->number - 1;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (cl.configstrings[n][0]) {
|
2009-02-28 14:41:18 +00:00
|
|
|
p = strchr(cl.configstrings[n], '\\');
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (p) {
|
2009-02-28 14:41:18 +00:00
|
|
|
p += 1;
|
|
|
|
strcpy(model, p);
|
|
|
|
p = strchr(model, '/');
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (p)
|
2010-10-13 17:07:29 +00:00
|
|
|
p[0] = 0;
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
/* if we can't figure it out, they're male */
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!model[0])
|
|
|
|
strcpy(model, "male");
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* see if we already know of the model specific sound */
|
2009-02-28 14:41:18 +00:00
|
|
|
Com_sprintf (sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base+1);
|
|
|
|
sfx = S_FindName (sexedFilename, false);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (!sfx) {
|
2010-10-13 17:07:29 +00:00
|
|
|
// no, so see if it exists
|
|
|
|
//FS_FOpenFile (&sexedFilename[1], &f, false);
|
|
|
|
len = FS_LoadFile (&sexedFilename[1], NULL);
|
|
|
|
if (len != -1)
|
|
|
|
{
|
|
|
|
// yes, close the file and register it
|
|
|
|
//FS_FCloseFile (f);
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx = S_RegisterSound (sexedFilename);
|
2010-10-13 17:07:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no, revert to the male sound in the pak0.pak
|
|
|
|
Com_sprintf (maleFilename, sizeof(maleFilename), "player/male/%s", base+1);
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx = S_AliasName (sexedFilename, maleFilename);
|
|
|
|
}
|
2010-10-13 17:07:29 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return sfx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Validates the parms and ques the sound up if pos is NULL, the sound
|
|
|
|
* will be dynamically sourced from the entity Entchannel 0 will never
|
|
|
|
* override a playing sound
|
|
|
|
*/
|
|
|
|
void S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs) {
|
2009-02-28 14:41:18 +00:00
|
|
|
sfxcache_t *sc;
|
|
|
|
int vol;
|
|
|
|
playsound_t *ps, *sort;
|
|
|
|
int start;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!sfx)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sfx->name[0] == '*')
|
|
|
|
sfx = S_RegisterSexedSound(&cl_entities[entnum].current, sfx->name);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* make sure the sound is loaded */
|
2009-02-28 14:41:18 +00:00
|
|
|
sc = S_LoadSound (sfx);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sc)
|
2010-06-19 08:47:14 +00:00
|
|
|
return; /* couldn't load the sound's data */
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
vol = fvol*255;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* make the playsound_t */
|
2009-02-28 14:41:18 +00:00
|
|
|
ps = S_AllocPlaysound ();
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!ps)
|
|
|
|
return;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (origin) {
|
2009-02-28 14:41:18 +00:00
|
|
|
VectorCopy (origin, ps->origin);
|
|
|
|
ps->fixed_origin = true;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else
|
2009-02-28 14:41:18 +00:00
|
|
|
ps->fixed_origin = false;
|
|
|
|
|
|
|
|
ps->entnum = entnum;
|
|
|
|
ps->entchannel = entchannel;
|
|
|
|
ps->attenuation = attenuation;
|
|
|
|
ps->volume = vol;
|
|
|
|
ps->sfx = sfx;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* drift s_beginofs */
|
2009-03-03 11:00:08 +00:00
|
|
|
start = (int)(cl.frame.servertime * 0.001f * dma.speed + s_beginofs);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (start < paintedtime) {
|
2009-02-28 14:41:18 +00:00
|
|
|
start = paintedtime;
|
2009-03-03 11:00:08 +00:00
|
|
|
s_beginofs = (int)(start - (cl.frame.servertime * 0.001f * dma.speed));
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else if (start > paintedtime + 0.3f * dma.speed) {
|
2009-03-03 11:00:08 +00:00
|
|
|
start = (int)(paintedtime + 0.1f * dma.speed);
|
|
|
|
s_beginofs = (int)(start - (cl.frame.servertime * 0.001f * dma.speed));
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else {
|
2009-02-28 14:41:18 +00:00
|
|
|
s_beginofs-=10;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!timeofs)
|
|
|
|
ps->begin = paintedtime;
|
|
|
|
else
|
2009-03-03 11:00:08 +00:00
|
|
|
ps->begin = (int)(start + timeofs * dma.speed);
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
ps->volume = fvol*255;
|
2009-03-10 20:29:15 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* sort into the pending sound list */
|
|
|
|
for (sort = s_pendingplays.next ;
|
|
|
|
sort != &s_pendingplays && sort->begin < ps->begin ;
|
|
|
|
sort = sort->next)
|
|
|
|
;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
ps->next = sort;
|
|
|
|
ps->prev = sort->prev;
|
|
|
|
|
|
|
|
ps->next->prev = ps;
|
|
|
|
ps->prev->next = ps;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_StartLocalSound (char *sound) {
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx_t *sfx;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx = S_RegisterSound (sound);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (!sfx) {
|
2009-02-28 14:41:18 +00:00
|
|
|
Com_Printf ("S_StartLocalSound: can't cache %s\n", sound);
|
|
|
|
return;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-10-13 18:00:39 +00:00
|
|
|
S_StartSound (NULL, cl.playernum+1, 0, sfx, 1, 1, 0);
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_ClearBuffer (void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int clear;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
|
|
|
s_rawend = 0;
|
|
|
|
|
|
|
|
if (dma.samplebits == 8)
|
|
|
|
clear = 0x80;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
else
|
|
|
|
clear = 0;
|
|
|
|
|
|
|
|
SNDDMA_BeginPainting ();
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (dma.buffer) {
|
2010-10-13 18:00:39 +00:00
|
|
|
int i;
|
|
|
|
unsigned char *ptr = (unsigned char *)dma.buffer;
|
|
|
|
|
|
|
|
i = dma.samples * dma.samplebits / 8;
|
|
|
|
while (i--) {
|
|
|
|
*ptr = clear;
|
|
|
|
ptr++;
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
SNDDMA_Submit ();
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_StopAllSounds(void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* clear all the playsounds */
|
2009-02-28 14:41:18 +00:00
|
|
|
memset(s_playsounds, 0, sizeof(s_playsounds));
|
|
|
|
s_freeplays.next = s_freeplays.prev = &s_freeplays;
|
|
|
|
s_pendingplays.next = s_pendingplays.prev = &s_pendingplays;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
for (i=0 ; i<MAX_PLAYSOUNDS ; i++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
s_playsounds[i].prev = &s_freeplays;
|
|
|
|
s_playsounds[i].next = s_freeplays.next;
|
|
|
|
s_playsounds[i].prev->next = &s_playsounds[i];
|
|
|
|
s_playsounds[i].next->prev = &s_playsounds[i];
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* clear all the channels */
|
2009-02-28 14:41:18 +00:00
|
|
|
memset(channels, 0, sizeof(channels));
|
|
|
|
|
|
|
|
S_ClearBuffer ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Entities with a ->sound field will generated looped sounds
|
|
|
|
* that are automatically started, stopped, and merged together
|
|
|
|
* as the entities are sent to the client
|
|
|
|
*/
|
|
|
|
void S_AddLoopSounds (void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i, j;
|
|
|
|
int sounds[MAX_EDICTS];
|
|
|
|
int left, right, left_total, right_total;
|
|
|
|
channel_t *ch;
|
|
|
|
sfx_t *sfx;
|
|
|
|
sfxcache_t *sc;
|
|
|
|
int num;
|
|
|
|
entity_state_t *ent;
|
2010-10-13 17:07:29 +00:00
|
|
|
vec3_t origin;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
if (cl_paused->value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (cls.state != ca_active)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!cl.sound_prepped)
|
|
|
|
return;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
for (i=0 ; i<cl.frame.num_entities ; i++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
|
|
|
|
ent = &cl_parse_entities[num];
|
|
|
|
sounds[i] = ent->sound;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
for (i=0 ; i<cl.frame.num_entities ; i++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sounds[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sfx = cl.sound_precache[sounds[i]];
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sfx)
|
2010-06-19 08:47:14 +00:00
|
|
|
continue; /* bad sound effect */
|
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
sc = sfx->cache;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!sc)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
|
|
|
|
ent = &cl_parse_entities[num];
|
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
CL_GetEntitySoundOrigin(ent->number, origin);
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* find the total contribution of all sounds of this type */
|
2010-10-13 17:07:29 +00:00
|
|
|
S_SpatializeOrigin (ent->origin, 255.0f, SOUND_LOOPATTENUATE, &left_total, &right_total);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
for (j=i+1 ; j<cl.frame.num_entities ; j++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (sounds[j] != sounds[i])
|
|
|
|
continue;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
sounds[j] = 0; /* don't check this again later */
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
num = (cl.frame.parse_entities + j)&(MAX_PARSE_ENTITIES-1);
|
|
|
|
ent = &cl_parse_entities[num];
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
S_SpatializeOrigin (ent->origin, 255.0f, SOUND_LOOPATTENUATE,
|
|
|
|
&left, &right);
|
2009-02-28 14:41:18 +00:00
|
|
|
left_total += left;
|
|
|
|
right_total += right;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (left_total == 0 && right_total == 0)
|
2010-06-19 08:47:14 +00:00
|
|
|
continue; /* not audible */
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* allocate a channel */
|
2009-02-28 14:41:18 +00:00
|
|
|
ch = S_PickChannel(0, 0);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!ch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (left_total > 255)
|
|
|
|
left_total = 255;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (right_total > 255)
|
|
|
|
right_total = 255;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
ch->leftvol = left_total;
|
|
|
|
ch->rightvol = right_total;
|
2010-06-19 17:04:32 +00:00
|
|
|
ch->autosound = true; /* remove next frame */
|
2009-02-28 14:41:18 +00:00
|
|
|
ch->sfx = sfx;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
/* Sometimes, the sc->length argument can become 0,
|
|
|
|
and in that case we get a SIGFPE in the next
|
|
|
|
modulo operation. The workaround checks for this
|
|
|
|
situation and in that case, sets the pos and end
|
|
|
|
parameters to 0.
|
|
|
|
|
|
|
|
ch->pos = paintedtime % sc->length;
|
|
|
|
ch->end = paintedtime + sc->length - ch->pos; */
|
|
|
|
if( sc->length == 0 ) {
|
|
|
|
ch->pos = 0;
|
|
|
|
ch->end = 0;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
ch->pos = paintedtime % sc->length;
|
|
|
|
ch->end = paintedtime + sc->length - ch->pos;
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Cinematic streaming and voice over network
|
2010-07-13 08:54:23 +00:00
|
|
|
* This could be used for chat over network, but that
|
|
|
|
* would be terrible slow. Still we can not remove this
|
|
|
|
* as it's used by taunts and maybe some mods out there
|
2010-06-19 08:47:14 +00:00
|
|
|
*/
|
|
|
|
void S_RawSamples (int samples, int rate, int width, int channels, byte *data) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
int src, dst;
|
|
|
|
float scale;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (s_rawend < paintedtime)
|
|
|
|
s_rawend = paintedtime;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
scale = (float)rate / dma.speed;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (channels == 2 && width == 2) {
|
|
|
|
if (scale == 1.0) {
|
|
|
|
/* optimized case */
|
|
|
|
for (i=0 ; i<samples ; i++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
dst = s_rawend&(MAX_RAW_SAMPLES-1);
|
|
|
|
s_rawend++;
|
|
|
|
s_rawsamples[dst].left =
|
|
|
|
LittleShort(((short *)data)[i*2]) << 8;
|
|
|
|
s_rawsamples[dst].right =
|
|
|
|
LittleShort(((short *)data)[i*2+1]) << 8;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
for (i=0 ; ; i++) {
|
2009-03-03 11:00:08 +00:00
|
|
|
src = (int)(i*scale);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (src >= samples)
|
|
|
|
break;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
dst = s_rawend&(MAX_RAW_SAMPLES-1);
|
|
|
|
s_rawend++;
|
|
|
|
s_rawsamples[dst].left =
|
|
|
|
LittleShort(((short *)data)[src*2]) << 8;
|
|
|
|
s_rawsamples[dst].right =
|
|
|
|
LittleShort(((short *)data)[src*2+1]) << 8;
|
|
|
|
}
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else if (channels == 1 && width == 2) {
|
|
|
|
for (i=0 ; ; i++) {
|
2009-03-03 11:00:08 +00:00
|
|
|
src = (int)(i*scale);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (src >= samples)
|
|
|
|
break;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
dst = s_rawend&(MAX_RAW_SAMPLES-1);
|
|
|
|
s_rawend++;
|
|
|
|
s_rawsamples[dst].left =
|
|
|
|
LittleShort(((short *)data)[src]) << 8;
|
|
|
|
s_rawsamples[dst].right =
|
|
|
|
LittleShort(((short *)data)[src]) << 8;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else if (channels == 2 && width == 1) {
|
|
|
|
for (i=0 ; ; i++) {
|
2009-03-03 11:00:08 +00:00
|
|
|
src = (int)(i*scale);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (src >= samples)
|
|
|
|
break;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
dst = s_rawend&(MAX_RAW_SAMPLES-1);
|
|
|
|
s_rawend++;
|
|
|
|
s_rawsamples[dst].left =
|
|
|
|
((char *)data)[src*2] << 16;
|
|
|
|
s_rawsamples[dst].right =
|
|
|
|
((char *)data)[src*2+1] << 16;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else if (channels == 1 && width == 1) {
|
|
|
|
for (i=0 ; ; i++) {
|
2009-03-03 11:00:08 +00:00
|
|
|
src = (int)(i*scale);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (src >= samples)
|
|
|
|
break;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
dst = s_rawend&(MAX_RAW_SAMPLES-1);
|
|
|
|
s_rawend++;
|
|
|
|
s_rawsamples[dst].left =
|
|
|
|
(((byte *)data)[src]-128) << 16;
|
|
|
|
s_rawsamples[dst].right = (((byte *)data)[src]-128) << 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-19 08:47:14 +00:00
|
|
|
* Called once each time through the main loop
|
|
|
|
*/
|
|
|
|
void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
int total;
|
|
|
|
channel_t *ch;
|
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* if the laoding plaque is up, clear everything
|
|
|
|
out to make sure we aren't looping a dirty
|
|
|
|
dma buffer while loading */
|
|
|
|
if (cls.disable_screen) {
|
2010-10-13 17:07:29 +00:00
|
|
|
S_ClearBuffer();
|
2009-02-28 14:41:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* rebuild scale tables if volume is modified */
|
2009-02-28 14:41:18 +00:00
|
|
|
if (s_volume->modified)
|
|
|
|
S_InitScaletable ();
|
|
|
|
|
|
|
|
VectorCopy(origin, listener_origin);
|
|
|
|
VectorCopy(forward, listener_forward);
|
|
|
|
VectorCopy(right, listener_right);
|
|
|
|
VectorCopy(up, listener_up);
|
|
|
|
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* update spatialization for dynamic sounds */
|
2009-02-28 14:41:18 +00:00
|
|
|
ch = channels;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
for (i=0 ; i<MAX_CHANNELS; i++, ch++) {
|
2009-02-28 14:41:18 +00:00
|
|
|
if (!ch->sfx)
|
|
|
|
continue;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (ch->autosound) {
|
|
|
|
/* autosounds are regenerated fresh each frame */
|
2009-02-28 14:41:18 +00:00
|
|
|
memset (ch, 0, sizeof(*ch));
|
|
|
|
continue;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-06-19 17:04:32 +00:00
|
|
|
S_Spatialize(ch); /* respatialize channel */
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
if (!ch->leftvol && !ch->rightvol) {
|
2009-02-28 14:41:18 +00:00
|
|
|
memset (ch, 0, sizeof(*ch));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* add loopsounds */
|
2009-02-28 14:41:18 +00:00
|
|
|
S_AddLoopSounds ();
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* debugging output */
|
|
|
|
if (s_show->value) {
|
2009-02-28 14:41:18 +00:00
|
|
|
total = 0;
|
|
|
|
ch = channels;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
for (i=0 ; i<MAX_CHANNELS; i++, ch++)
|
2010-06-19 08:47:14 +00:00
|
|
|
if (ch->sfx && (ch->leftvol || ch->rightvol) ) {
|
2009-02-28 14:41:18 +00:00
|
|
|
Com_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
|
|
|
|
total++;
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
Com_Printf ("----(%i)---- painted: %i\n", total, paintedtime);
|
|
|
|
}
|
2010-10-13 17:07:29 +00:00
|
|
|
/* mix some sound */
|
|
|
|
S_Update_();
|
2009-10-03 16:06:45 +00:00
|
|
|
|
|
|
|
/* stream music */
|
|
|
|
OGG_Stream();
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void GetSoundtime(void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int samplepos;
|
|
|
|
static int buffers;
|
|
|
|
static int oldsamplepos;
|
|
|
|
int fullsamples;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
fullsamples = dma.samples / dma.channels;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* it is possible to miscount buffers if it has wrapped twice between
|
|
|
|
calls to S_Update. Oh well. This a hack around that. */
|
2009-02-28 14:41:18 +00:00
|
|
|
samplepos = SNDDMA_GetDMAPos();
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
if (samplepos < oldsamplepos) {
|
|
|
|
buffers++; /* buffer wrapped */
|
|
|
|
|
|
|
|
if (paintedtime > 0x40000000) {
|
|
|
|
/* time to chop things off to avoid 32 bit limits */
|
2009-02-28 14:41:18 +00:00
|
|
|
buffers = 0;
|
|
|
|
paintedtime = fullsamples;
|
|
|
|
S_StopAllSounds ();
|
|
|
|
}
|
|
|
|
}
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
oldsamplepos = samplepos;
|
|
|
|
|
|
|
|
soundtime = buffers*fullsamples + samplepos/dma.channels;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_Update_(void) {
|
|
|
|
unsigned endtime;
|
|
|
|
int samps;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
if (!sound_started)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SNDDMA_BeginPainting ();
|
|
|
|
|
|
|
|
if (!dma.buffer)
|
|
|
|
return;
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* Updates DMA time */
|
2009-02-28 14:41:18 +00:00
|
|
|
GetSoundtime();
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* check to make sure that we haven't overshot */
|
|
|
|
if (paintedtime < soundtime) {
|
2009-02-28 14:41:18 +00:00
|
|
|
Com_DPrintf ("S_Update_ : overflow\n");
|
|
|
|
paintedtime = soundtime;
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* mix ahead of current position */
|
2009-03-03 11:00:08 +00:00
|
|
|
endtime = (int)(soundtime + s_mixahead->value * dma.speed);
|
2009-02-28 14:41:18 +00:00
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
/* mix to an even submission block size */
|
2009-02-28 14:41:18 +00:00
|
|
|
endtime = (endtime + dma.submission_chunk-1)
|
2010-06-19 08:47:14 +00:00
|
|
|
& ~(dma.submission_chunk-1);
|
2009-02-28 14:41:18 +00:00
|
|
|
samps = dma.samples >> (dma.channels-1);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
if (endtime - soundtime > samps)
|
|
|
|
endtime = soundtime + samps;
|
|
|
|
|
|
|
|
S_PaintChannels (endtime);
|
|
|
|
|
|
|
|
SNDDMA_Submit ();
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_Play(void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
char name[256];
|
|
|
|
sfx_t *sfx;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
i = 1;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
while (i<Cmd_Argc()) {
|
|
|
|
if (!strrchr(Cmd_Argv(i), '.')) {
|
|
|
|
strncpy(name, Cmd_Argv(i), sizeof(name)-5);
|
2009-02-28 14:41:18 +00:00
|
|
|
strcat(name, ".wav");
|
2010-06-19 08:47:14 +00:00
|
|
|
|
|
|
|
} else
|
2009-03-03 11:00:08 +00:00
|
|
|
strncpy(name, Cmd_Argv(i), sizeof(name)-1);
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
if (strstr(name, "..") || name[0] == '/' || name[0] == '\\') {
|
|
|
|
Com_Printf ("Bad filename %s\n", name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
sfx = S_RegisterSound(name);
|
2010-10-13 17:07:29 +00:00
|
|
|
{
|
|
|
|
S_StartSound(NULL, cl.playernum+1, 0, sfx, 1.0, 1.0, 0);
|
|
|
|
}
|
2009-02-28 14:41:18 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-19 08:47:14 +00:00
|
|
|
void S_SoundList(void) {
|
2009-02-28 14:41:18 +00:00
|
|
|
int i;
|
|
|
|
sfx_t *sfx;
|
|
|
|
sfxcache_t *sc;
|
|
|
|
int size, total;
|
2010-10-13 17:07:29 +00:00
|
|
|
int numsounds;
|
2009-02-28 14:41:18 +00:00
|
|
|
|
|
|
|
total = 0;
|
2010-10-13 17:07:29 +00:00
|
|
|
numsounds = 0;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2010-10-13 17:07:29 +00:00
|
|
|
for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
|
|
|
|
{
|
|
|
|
if (!sfx->name[0])
|
2009-02-28 14:41:18 +00:00
|
|
|
continue;
|
2010-06-19 08:47:14 +00:00
|
|
|
|
2009-02-28 14:41:18 +00:00
|
|
|
sc = sfx->cache;
|
2010-10-13 17:07:29 +00:00
|
|
|
if (sc)
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
size = sc->length*sc->width*(sc->stereo+1);
|
|
|
|
total += size;
|
2010-10-13 17:07:29 +00:00
|
|
|
Com_Printf("%s(%2db) %8i : %s\n", sc->loopstart != -1 ? "L" : " ", sc->width*8, size, sfx->name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-02-28 14:41:18 +00:00
|
|
|
if (sfx->name[0] == '*')
|
2010-10-13 17:07:29 +00:00
|
|
|
Com_Printf(" placeholder : %s\n", sfx->name);
|
2009-02-28 14:41:18 +00:00
|
|
|
else
|
2010-10-13 17:07:29 +00:00
|
|
|
Com_Printf(" not loaded : %s\n", sfx->name);
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-10-13 17:07:29 +00:00
|
|
|
numsounds++;
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-10-13 17:07:29 +00:00
|
|
|
Com_Printf ("Total resident: %i bytes (%.2f MB) in %d sounds\n", total, (float)total / 1024 / 1024, numsounds);
|
2009-02-28 14:41:18 +00:00
|
|
|
}
|
2010-06-19 17:04:32 +00:00
|
|
|
|