?? snd_dma.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)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -