yquake2remaster/src/client/snd_dma.c
Yamagi Burmeister 80c4bf995b Der Versuch den Icculus Quake II Client einmal aufzuraeumen.
Bei dieser initialen Version handelt es sich um den blanken
Client ohne Renderer und Server und Spiele. Ueberfluessige
Sourcedateien wurden geloescht, einige Dateien so verschoben,
dass sich eine sinnvolle Verzeichnisstruktur ergibt. Zudem wurde
eine neue Makefile erstellt.
2009-02-28 14:41:18 +00:00

1242 lines
25 KiB
C

/*
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
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// snd_dma.c -- main control for any streaming sound output device
#include "client.h"
#include "snd_loc.h"
void S_Play(void);
void S_SoundList(void);
void S_Update_();
void S_StopAllSounds(void);
// =======================================================================
// Internal sound data & structures
// =======================================================================
// only begin attenuating sound volumes when outside the FULLVOLUME range
#define SOUND_FULLVOLUME 80
#define SOUND_LOOPATTENUATE 0.003
int s_registration_sequence;
channel_t channels[MAX_CHANNELS];
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;
qboolean s_registering;
int soundtime; // sample PAIRS
int paintedtime; // sample PAIRS
// 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.
#define MAX_SFX (MAX_SOUNDS*2)
sfx_t known_sfx[MAX_SFX];
int num_sfx;
#define MAX_PLAYSOUNDS 128
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_show;
cvar_t *s_mixahead;
cvar_t *s_primary;
int s_rawend;
portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES];
// ====================================================================
// User-setable variables
// ====================================================================
void S_SoundInfo_f(void)
{
if (!sound_started)
{
Com_Printf ("sound system not started\n");
return;
}
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);
Com_Printf("0x%x dma buffer\n", dma.buffer);
}
/*
================
S_Init
================
*/
void S_Init (void)
{
cvar_t *cv;
Com_Printf("\n------- sound initialization -------\n");
cv = Cvar_Get ("s_initsound", "1", 0);
if (!cv->value)
Com_Printf ("not initializing.\n");
else
{
s_volume = Cvar_Get ("s_volume", "0.7", CVAR_ARCHIVE);
s_khz = Cvar_Get ("s_khz", "11", CVAR_ARCHIVE);
s_loadas8bit = Cvar_Get ("s_loadas8bit", "1", CVAR_ARCHIVE);
s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
s_show = Cvar_Get ("s_show", "0", 0);
s_testsound = Cvar_Get ("s_testsound", "0", 0);
s_primary = Cvar_Get ("s_primary", "0", CVAR_ARCHIVE); // win32 specific
Cmd_AddCommand("play", S_Play);
Cmd_AddCommand("stopsound", S_StopAllSounds);
Cmd_AddCommand("soundlist", S_SoundList);
Cmd_AddCommand("soundinfo", S_SoundInfo_f);
if (!SNDDMA_Init())
return;
S_InitScaletable ();
sound_started = 1;
num_sfx = 0;
soundtime = 0;
paintedtime = 0;
Com_Printf ("sound sampling rate: %i\n", dma.speed);
S_StopAllSounds ();
}
Com_Printf("------------------------------------\n");
}
// =======================================================================
// Shutdown sound engine
// =======================================================================
void S_Shutdown(void)
{
int i;
sfx_t *sfx;
if (!sound_started)
return;
SNDDMA_Shutdown();
sound_started = 0;
Cmd_RemoveCommand("play");
Cmd_RemoveCommand("stopsound");
Cmd_RemoveCommand("soundlist");
Cmd_RemoveCommand("soundinfo");
// free all sounds
for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++)
{
if (!sfx->name[0])
continue;
if (sfx->cache)
Z_Free (sfx->cache);
memset (sfx, 0, sizeof(*sfx));
}
num_sfx = 0;
}
// =======================================================================
// Load a sound
// =======================================================================
/*
==================
S_FindName
==================
*/
sfx_t *S_FindName (char *name, qboolean create)
{
int i;
sfx_t *sfx;
if (!name)
Com_Error (ERR_FATAL, "S_FindName: NULL\n");
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);
// see if already loaded
for (i=0 ; i < num_sfx ; i++)
if (!strcmp(known_sfx[i].name, name))
{
return &known_sfx[i];
}
if (!create)
return NULL;
// find a free sfx
for (i=0 ; i < num_sfx ; i++)
if (!known_sfx[i].name[0])
// registration_sequence < s_registration_sequence)
break;
if (i == num_sfx)
{
if (num_sfx == MAX_SFX)
Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
num_sfx++;
}
sfx = &known_sfx[i];
memset (sfx, 0, sizeof(*sfx));
strcpy (sfx->name, name);
sfx->registration_sequence = s_registration_sequence;
return sfx;
}
/*
==================
S_AliasName
==================
*/
sfx_t *S_AliasName (char *aliasname, char *truename)
{
sfx_t *sfx;
char *s;
int i;
s = Z_Malloc (MAX_QPATH);
strcpy (s, truename);
// find a free sfx
for (i=0 ; i < num_sfx ; i++)
if (!known_sfx[i].name[0])
break;
if (i == num_sfx)
{
if (num_sfx == MAX_SFX)
Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
num_sfx++;
}
sfx = &known_sfx[i];
memset (sfx, 0, sizeof(*sfx));
strcpy (sfx->name, aliasname);
sfx->registration_sequence = s_registration_sequence;
sfx->truename = s;
return sfx;
}
/*
=====================
S_BeginRegistration
=====================
*/
void S_BeginRegistration (void)
{
s_registration_sequence++;
s_registering = true;
}
/*
==================
S_RegisterSound
==================
*/
sfx_t *S_RegisterSound (char *name)
{
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;
}
/*
=====================
S_EndRegistration
=====================
*/
void S_EndRegistration (void)
{
int i;
sfx_t *sfx;
int size;
// free any sounds not from this registration sequence
for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++)
{
if (!sfx->name[0])
continue;
if (sfx->registration_sequence != s_registration_sequence)
{ // don't need this sound
if (sfx->cache) // it is possible to have a leftover
Z_Free (sfx->cache); // from a server that didn't finish loading
memset (sfx, 0, sizeof(*sfx));
}
else
{ // make sure it is paged in
if (sfx->cache)
{
size = sfx->cache->length*sfx->cache->width;
Com_PageInMemory ((byte *)sfx->cache, size);
}
}
}
// load everything in
for (i=0, sfx=known_sfx ; i < num_sfx ; i++,sfx++)
{
if (!sfx->name[0])
continue;
S_LoadSound (sfx);
}
s_registering = false;
}
//=============================================================================
/*
=================
S_PickChannel
=================
*/
channel_t *S_PickChannel(int entnum, int entchannel)
{
int ch_idx;
int first_to_die;
int life_left;
channel_t *ch;
if (entchannel<0)
Com_Error (ERR_DROP, "S_PickChannel: entchannel<0");
// 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
first_to_die = ch_idx;
break;
}
// don't let monster sounds override player sounds
if (channels[ch_idx].entnum == cl.playernum+1 && entnum != cl.playernum+1 && channels[ch_idx].sfx)
continue;
if (channels[ch_idx].end - paintedtime < life_left)
{
life_left = channels[ch_idx].end - paintedtime;
first_to_die = ch_idx;
}
}
if (first_to_die == -1)
return NULL;
ch = &channels[first_to_die];
memset (ch, 0, sizeof(*ch));
return ch;
}
/*
=================
S_SpatializeOrigin
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)
{
*left_vol = *right_vol = 255;
return;
}
// calculate stereo seperation and distance attenuation
VectorSubtract(origin, listener_origin, source_vec);
dist = VectorNormalize(source_vec);
dist -= SOUND_FULLVOLUME;
if (dist < 0)
dist = 0; // close enough to be at full volume
dist *= dist_mult; // different attenuation levels
dot = DotProduct(listener_right, source_vec);
if (dma.channels == 1 || !dist_mult)
{ // no attenuation = no spatialization
rscale = 1.0;
lscale = 1.0;
}
else
{
rscale = 0.5 * (1.0 + dot);
lscale = 0.5*(1.0 - dot);
}
// add in distance effect
scale = (1.0 - dist) * rscale;
*right_vol = (int) (master_vol * scale);
if (*right_vol < 0)
*right_vol = 0;
scale = (1.0 - dist) * lscale;
*left_vol = (int) (master_vol * scale);
if (*left_vol < 0)
*left_vol = 0;
}
/*
=================
S_Spatialize
=================
*/
void S_Spatialize(channel_t *ch)
{
vec3_t origin;
// anything coming from the view entity will always be full volume
if (ch->entnum == cl.playernum+1)
{
ch->leftvol = ch->master_vol;
ch->rightvol = ch->master_vol;
return;
}
if (ch->fixed_origin)
{
VectorCopy (ch->origin, origin);
}
else
CL_GetEntitySoundOrigin (ch->entnum, origin);
S_SpatializeOrigin (origin, ch->master_vol, ch->dist_mult, &ch->leftvol, &ch->rightvol);
}
/*
=================
S_AllocPlaysound
=================
*/
playsound_t *S_AllocPlaysound (void)
{
playsound_t *ps;
ps = s_freeplays.next;
if (ps == &s_freeplays)
return NULL; // no free playsounds
// unlink from freelist
ps->prev->next = ps->next;
ps->next->prev = ps->prev;
return ps;
}
/*
=================
S_FreePlaysound
=================
*/
void S_FreePlaysound (playsound_t *ps)
{
// unlink from channel
ps->prev->next = ps->next;
ps->next->prev = ps->prev;
// add to free list
ps->next = s_freeplays.next;
s_freeplays.next->prev = ps;
ps->prev = &s_freeplays;
s_freeplays.next = ps;
}
/*
===============
S_IssuePlaysound
Take the next playsound and begin it on the channel
This is never called directly by S_Play*, but only
by the update loop.
===============
*/
void S_IssuePlaysound (playsound_t *ps)
{
channel_t *ch;
sfxcache_t *sc;
if (s_show->value)
Com_Printf ("Issue %i\n", ps->begin);
// pick a channel to play on
ch = S_PickChannel(ps->entnum, ps->entchannel);
if (!ch)
{
S_FreePlaysound (ps);
return;
}
// spatialize
if (ps->attenuation == ATTN_STATIC)
ch->dist_mult = ps->attenuation * 0.001;
else
ch->dist_mult = ps->attenuation * 0.0005;
ch->master_vol = ps->volume;
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);
ch->end = paintedtime + sc->length;
// free the playsound
S_FreePlaysound (ps);
}
struct sfx_s *S_RegisterSexedSound (entity_state_t *ent, char *base)
{
int n;
char *p;
struct sfx_s *sfx;
FILE *f;
char model[MAX_QPATH];
char sexedFilename[MAX_QPATH];
char maleFilename[MAX_QPATH];
// determine what model the client is using
model[0] = 0;
n = CS_PLAYERSKINS + ent->number - 1;
if (cl.configstrings[n][0])
{
p = strchr(cl.configstrings[n], '\\');
if (p)
{
p += 1;
strcpy(model, p);
p = strchr(model, '/');
if (p)
*p = 0;
}
}
// if we can't figure it out, they're male
if (!model[0])
strcpy(model, "male");
// see if we already know of the model specific sound
Com_sprintf (sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base+1);
sfx = S_FindName (sexedFilename, false);
if (!sfx)
{
// no, so see if it exists
FS_FOpenFile (&sexedFilename[1], &f);
if (f)
{
// yes, close the file and register it
FS_FCloseFile (f);
sfx = S_RegisterSound (sexedFilename);
}
else
{
// no, revert to the male sound in the pak0.pak
Com_sprintf (maleFilename, sizeof(maleFilename), "player/%s/%s", "male", base+1);
sfx = S_AliasName (sexedFilename, maleFilename);
}
}
return sfx;
}
// =======================================================================
// Start a sound effect
// =======================================================================
/*
====================
S_StartSound
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)
{
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);
// make sure the sound is loaded
sc = S_LoadSound (sfx);
if (!sc)
return; // couldn't load the sound's data
vol = fvol*255;
// make the playsound_t
ps = S_AllocPlaysound ();
if (!ps)
return;
if (origin)
{
VectorCopy (origin, ps->origin);
ps->fixed_origin = true;
}
else
ps->fixed_origin = false;
ps->entnum = entnum;
ps->entchannel = entchannel;
ps->attenuation = attenuation;
ps->volume = vol;
ps->sfx = sfx;
// drift s_beginofs
start = cl.frame.servertime * 0.001 * dma.speed + s_beginofs;
if (start < paintedtime)
{
start = paintedtime;
s_beginofs = start - (cl.frame.servertime * 0.001 * dma.speed);
}
else if (start > paintedtime + 0.3 * dma.speed)
{
start = paintedtime + 0.1 * dma.speed;
s_beginofs = start - (cl.frame.servertime * 0.001 * dma.speed);
}
else
{
s_beginofs-=10;
}
if (!timeofs)
ps->begin = paintedtime;
else
ps->begin = start + timeofs * dma.speed;
// sort into the pending sound list
for (sort = s_pendingplays.next ;
sort != &s_pendingplays && sort->begin < ps->begin ;
sort = sort->next)
;
ps->next = sort;
ps->prev = sort->prev;
ps->next->prev = ps;
ps->prev->next = ps;
}
/*
==================
S_StartLocalSound
==================
*/
void S_StartLocalSound (char *sound)
{
sfx_t *sfx;
if (!sound_started)
return;
sfx = S_RegisterSound (sound);
if (!sfx)
{
Com_Printf ("S_StartLocalSound: can't cache %s\n", sound);
return;
}
S_StartSound (NULL, cl.playernum+1, 0, sfx, 1, 1, 0);
}
/*
==================
S_ClearBuffer
==================
*/
void S_ClearBuffer (void)
{
int clear;
if (!sound_started)
return;
s_rawend = 0;
if (dma.samplebits == 8)
clear = 0x80;
else
clear = 0;
SNDDMA_BeginPainting ();
if (dma.buffer) {
/* memset(dma.buffer, clear, dma.samples * dma.samplebits/8); */
int i;
unsigned char *ptr = (unsigned char *)dma.buffer;
/* clear it manually because the buffer might be writeonly (mmap) */
i = dma.samples * dma.samplebits/8;
while (i--) {
*ptr = clear;
ptr++;
}
}
SNDDMA_Submit ();
}
/*
==================
S_StopAllSounds
==================
*/
void S_StopAllSounds(void)
{
int i;
if (!sound_started)
return;
// clear all the playsounds
memset(s_playsounds, 0, sizeof(s_playsounds));
s_freeplays.next = s_freeplays.prev = &s_freeplays;
s_pendingplays.next = s_pendingplays.prev = &s_pendingplays;
for (i=0 ; i<MAX_PLAYSOUNDS ; i++)
{
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];
}
// clear all the channels
memset(channels, 0, sizeof(channels));
S_ClearBuffer ();
}
/*
==================
S_AddLoopSounds
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)
{
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;
if (cl_paused->value)
return;
if (cls.state != ca_active)
return;
if (!cl.sound_prepped)
return;
for (i=0 ; i<cl.frame.num_entities ; i++)
{
num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
ent = &cl_parse_entities[num];
sounds[i] = ent->sound;
}
for (i=0 ; i<cl.frame.num_entities ; i++)
{
if (!sounds[i])
continue;
sfx = cl.sound_precache[sounds[i]];
if (!sfx)
continue; // bad sound effect
sc = sfx->cache;
if (!sc)
continue;
num = (cl.frame.parse_entities + i)&(MAX_PARSE_ENTITIES-1);
ent = &cl_parse_entities[num];
// find the total contribution of all sounds of this type
S_SpatializeOrigin (ent->origin, 255.0, SOUND_LOOPATTENUATE,
&left_total, &right_total);
for (j=i+1 ; j<cl.frame.num_entities ; j++)
{
if (sounds[j] != sounds[i])
continue;
sounds[j] = 0; // don't check this again later
num = (cl.frame.parse_entities + j)&(MAX_PARSE_ENTITIES-1);
ent = &cl_parse_entities[num];
S_SpatializeOrigin (ent->origin, 255.0, SOUND_LOOPATTENUATE,
&left, &right);
left_total += left;
right_total += right;
}
if (left_total == 0 && right_total == 0)
continue; // not audible
// allocate a channel
ch = S_PickChannel(0, 0);
if (!ch)
return;
if (left_total > 255)
left_total = 255;
if (right_total > 255)
right_total = 255;
ch->leftvol = left_total;
ch->rightvol = right_total;
ch->autosound = true; // remove next frame
ch->sfx = sfx;
/*
* PATCH: eliasm
*
* 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;
}
}
}
//=============================================================================
/*
============
S_RawSamples
Cinematic streaming and voice over network
============
*/
void S_RawSamples (int samples, int rate, int width, int channels, byte *data)
{
int i;
int src, dst;
float scale;
if (!sound_started)
return;
if (s_rawend < paintedtime)
s_rawend = paintedtime;
scale = (float)rate / dma.speed;
//Com_Printf ("%i < %i < %i\n", soundtime, paintedtime, s_rawend);
if (channels == 2 && width == 2)
{
if (scale == 1.0)
{ // optimized case
for (i=0 ; i<samples ; i++)
{
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;
}
}
else
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
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;
}
}
}
else if (channels == 1 && width == 2)
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
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;
}
}
else if (channels == 2 && width == 1)
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
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;
}
}
else if (channels == 1 && width == 1)
{
for (i=0 ; ; i++)
{
src = i*scale;
if (src >= samples)
break;
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;
}
}
}
//=============================================================================
/*
============
S_Update
Called once each time through the main loop
============
*/
void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up)
{
int i;
int total;
channel_t *ch;
channel_t *combine;
if (!sound_started)
return;
// 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)
{
S_ClearBuffer ();
return;
}
// rebuild scale tables if volume is modified
if (s_volume->modified)
S_InitScaletable ();
VectorCopy(origin, listener_origin);
VectorCopy(forward, listener_forward);
VectorCopy(right, listener_right);
VectorCopy(up, listener_up);
combine = NULL;
// update spatialization for dynamic sounds
ch = channels;
for (i=0 ; i<MAX_CHANNELS; i++, ch++)
{
if (!ch->sfx)
continue;
if (ch->autosound)
{ // autosounds are regenerated fresh each frame
memset (ch, 0, sizeof(*ch));
continue;
}
S_Spatialize(ch); // respatialize channel
if (!ch->leftvol && !ch->rightvol)
{
memset (ch, 0, sizeof(*ch));
continue;
}
}
// add loopsounds
S_AddLoopSounds ();
//
// debugging output
//
if (s_show->value)
{
total = 0;
ch = channels;
for (i=0 ; i<MAX_CHANNELS; i++, ch++)
if (ch->sfx && (ch->leftvol || ch->rightvol) )
{
Com_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name);
total++;
}
Com_Printf ("----(%i)---- painted: %i\n", total, paintedtime);
}
// mix some sound
S_Update_();
}
void GetSoundtime(void)
{
int samplepos;
static int buffers;
static int oldsamplepos;
int fullsamples;
fullsamples = dma.samples / dma.channels;
// it is possible to miscount buffers if it has wrapped twice between
// calls to S_Update. Oh well.
samplepos = SNDDMA_GetDMAPos();
if (samplepos < oldsamplepos)
{
buffers++; // buffer wrapped
if (paintedtime > 0x40000000)
{ // time to chop things off to avoid 32 bit limits
buffers = 0;
paintedtime = fullsamples;
S_StopAllSounds ();
}
}
oldsamplepos = samplepos;
soundtime = buffers*fullsamples + samplepos/dma.channels;
}
void S_Update_(void)
{
unsigned endtime;
int samps;
if (!sound_started)
return;
SNDDMA_BeginPainting ();
if (!dma.buffer)
return;
// Updates DMA time
GetSoundtime();
// check to make sure that we haven't overshot
if (paintedtime < soundtime)
{
Com_DPrintf ("S_Update_ : overflow\n");
paintedtime = soundtime;
}
// mix ahead of current position
endtime = soundtime + s_mixahead->value * dma.speed;
//endtime = (soundtime + 4096) & ~4095;
// mix to an even submission block size
endtime = (endtime + dma.submission_chunk-1)
& ~(dma.submission_chunk-1);
samps = dma.samples >> (dma.channels-1);
if (endtime - soundtime > samps)
endtime = soundtime + samps;
S_PaintChannels (endtime);
SNDDMA_Submit ();
}
/*
===============================================================================
console functions
===============================================================================
*/
void S_Play(void)
{
int i;
char name[256];
sfx_t *sfx;
i = 1;
while (i<Cmd_Argc())
{
if (!strrchr(Cmd_Argv(i), '.'))
{
strcpy(name, Cmd_Argv(i));
strcat(name, ".wav");
}
else
strcpy(name, Cmd_Argv(i));
sfx = S_RegisterSound(name);
S_StartSound(NULL, cl.playernum+1, 0, sfx, 1.0, 1.0, 0);
i++;
}
}
void S_SoundList(void)
{
int i;
sfx_t *sfx;
sfxcache_t *sc;
int size, total;
total = 0;
for (sfx=known_sfx, i=0 ; i<num_sfx ; i++, sfx++)
{
if (!sfx->registration_sequence)
continue;
sc = sfx->cache;
if (sc)
{
size = sc->length*sc->width*(sc->stereo+1);
total += size;
if (sc->loopstart >= 0)
Com_Printf ("L");
else
Com_Printf (" ");
Com_Printf("(%2db) %6i : %s\n",sc->width*8, size, sfx->name);
}
else
{
if (sfx->name[0] == '*')
Com_Printf(" placeholder : %s\n", sfx->name);
else
Com_Printf(" not loaded : %s\n", sfx->name);
}
}
Com_Printf ("Total resident: %i\n", total);
}