Skip to content
This repository was archived by the owner on Dec 11, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions inc/client/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 8

// only begin attenuating sound volumes when outside the FULLVOLUME range
#define SOUND_FULLVOLUME 80

#define SOUND_LOOPATTENUATE 0.003f

#define SOUND_LOOPATTENUATE_MULT 0.0006f

#if USE_CLIENT

#define MAX_LOCAL_SERVERS 16
Expand Down
1 change: 1 addition & 0 deletions inc/shared/shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,7 @@ enum {
};

// sound attenuation values
#define ATTN_LOOP_NONE -1 // ugly hack for remaster
#define ATTN_NONE 0 // full volume the entire level
#define ATTN_NORM 1
#define ATTN_IDLE 2
Expand Down
4 changes: 2 additions & 2 deletions src/client/sound/al.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,8 @@ static void AL_AddLoopSounds(void)
ch->autoframe = s_framecount;
ch->sfx = sfx;
ch->entnum = ent->number;
ch->master_vol = 1.0f;
ch->dist_mult = SOUND_LOOPATTENUATE;
ch->master_vol = S_GetEntityLoopVolume(ent);
ch->dist_mult = S_GetEntityLoopDistMult(ent);
ch->end = s_paintedtime + sc->length;

AL_PlayChannel(ch);
Expand Down
18 changes: 10 additions & 8 deletions src/client/sound/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ static void SpatializeOrigin(const vec3_t origin, float master_vol, float dist_m
dist = 0; // close enough to be at full volume
dist *= dist_mult; // different attenuation levels

if (dma.channels == 1) {
if (dma.channels == 1 || !dist_mult) {
rscale = 1.0f;
lscale = 1.0f;
} else {
Expand Down Expand Up @@ -727,7 +727,7 @@ static void AddLoopSounds(void)
{
int i, j;
int sounds[MAX_EDICTS];
float left, right, left_total, right_total;
float left, right, left_total, right_total, vol, att;
channel_t *ch;
sfx_t *sfx;
sfxcache_t *sc;
Expand All @@ -754,10 +754,12 @@ static void AddLoopSounds(void)
num = (cl.frame.firstEntity + i) & PARSE_ENTITIES_MASK;
ent = &cl.entityStates[num];

vol = S_GetEntityLoopVolume(ent);
att = S_GetEntityLoopDistMult(ent);

// find the total contribution of all sounds of this type
CL_GetEntitySoundOrigin(ent->number, origin);
SpatializeOrigin(origin, 1.0f, SOUND_LOOPATTENUATE,
&left_total, &right_total);
SpatializeOrigin(origin, vol, att, &left_total, &right_total);
for (j = i + 1; j < cl.frame.numEntities; j++) {
if (sounds[j] != sounds[i])
continue;
Expand All @@ -767,8 +769,8 @@ static void AddLoopSounds(void)
ent = &cl.entityStates[num];

CL_GetEntitySoundOrigin(ent->number, origin);
SpatializeOrigin(origin, 1.0f, SOUND_LOOPATTENUATE,
&left, &right);
SpatializeOrigin(origin, S_GetEntityLoopVolume(ent),
S_GetEntityLoopDistMult(ent), &left, &right);
left_total += left;
right_total += right;
}
Expand All @@ -783,8 +785,8 @@ static void AddLoopSounds(void)

ch->leftvol = min(left_total, 1.0f);
ch->rightvol = min(right_total, 1.0f);
ch->master_vol = 1.0f;
ch->dist_mult = SOUND_LOOPATTENUATE; // for S_IsFullVolume()
ch->master_vol = vol;
ch->dist_mult = att; // for S_IsFullVolume()
ch->autosound = true; // remove next frame
ch->sfx = sfx;
ch->pos = s_paintedtime % sc->length;
Expand Down
20 changes: 20 additions & 0 deletions src/client/sound/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,26 @@ void S_BuildSoundList(int *sounds)
}
}

float S_GetEntityLoopVolume(const centity_state_t *ent)
{
if (ent->loop_volume)
return ent->loop_volume;

return 1.0f;
}

float S_GetEntityLoopDistMult(const centity_state_t *ent)
{
if (ent->loop_attenuation) {
if (ent->loop_attenuation == ATTN_LOOP_NONE)
return 0;
if (ent->loop_attenuation != ATTN_STATIC)
return ent->loop_attenuation * SOUND_LOOPATTENUATE_MULT;
}

return SOUND_LOOPATTENUATE;
}

/*
============
S_Update
Expand Down
35 changes: 26 additions & 9 deletions src/client/sound/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ static bool GetWavinfo(sizebuf_t *sz)
case 16:
s_info.width = 2;
break;
case 24:
s_info.width = 3;
break;
default:
Com_DPrintf("%s has bad width\n", s_info.name);
return false;
Expand Down Expand Up @@ -179,6 +182,27 @@ static bool GetWavinfo(sizebuf_t *sz)
return true;
}

static void ConvertSamples(void)
{
uint16_t *data = (uint16_t *)s_info.data;
int count = s_info.samples * s_info.channels;

// sigh. truncate 24 bit to 16
if (s_info.width == 3) {
for (int i = 0; i < count; i++)
data[i] = RL32(&s_info.data[i * 3]) >> 8;
s_info.width = 2;
return;
}

#if USE_BIG_ENDIAN
if (s_info.width == 2) {
for (int i = 0; i < count; i++)
data[i] = LittleShort(data[i]);
}
#endif
}

/*
==============
S_LoadSound
Expand Down Expand Up @@ -227,15 +251,8 @@ sfxcache_t *S_LoadSound(sfx_t *s)
goto fail;
}

#if USE_BIG_ENDIAN
if (s_info.format == FORMAT_PCM && s_info.width == 2) {
uint16_t *data = (uint16_t *)s_info.data;
int count = s_info.samples * s_info.channels;

for (int i = 0; i < count; i++)
data[i] = LittleShort(data[i]);
}
#endif
if (s_info.format == FORMAT_PCM)
ConvertSamples();

sc = s_api.upload_sfx(s);

Expand Down
7 changes: 2 additions & 5 deletions src/client/sound/sound.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ extern const sndapi_t snd_openal;

//====================================================================

// only begin attenuating sound volumes when outside the FULLVOLUME range
#define SOUND_FULLVOLUME 80

#define SOUND_LOOPATTENUATE 0.003f

extern sndstarted_t s_started;
extern bool s_active;
extern sndapi_t s_api;
Expand Down Expand Up @@ -180,5 +175,7 @@ sfxcache_t *S_LoadSound(sfx_t *s);
channel_t *S_PickChannel(int entnum, int entchannel);
void S_IssuePlaysound(playsound_t *ps);
void S_BuildSoundList(int *sounds);
float S_GetEntityLoopVolume(const centity_state_t *ent);
float S_GetEntityLoopDistMult(const centity_state_t *ent);

bool OGG_Load(sizebuf_t *sz);
19 changes: 15 additions & 4 deletions src/common/msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -480,12 +480,18 @@ void MSG_PackEntity(entity_packed_t *out, const entity_state_t *in, const entity
out->alpha = Q_clip_uint8(ext->alpha * 255.0f);
out->scale = Q_clip_uint8(ext->scale * 16.0f);
out->loop_volume = Q_clip_uint8(ext->loop_volume * 255.0f);
out->loop_attenuation = Q_clip_uint8(ext->loop_attenuation * 64.0f);
// encode ATTN_STATIC (192) as 0, and ATTN_LOOP_NONE (-1) as 192
if (ext->loop_attenuation == ATTN_LOOP_NONE) {
out->loop_attenuation = 192;
} else {
out->loop_attenuation = Q_clip_uint8(ext->loop_attenuation * 64.0f);
if (out->loop_attenuation == 192)
out->loop_attenuation = 0;
}
// save network bandwidth
if (out->alpha == 255) out->alpha = 0;
if (out->scale == 16) out->scale = 0;
if (out->loop_volume == 255) out->loop_volume = 0;
if (out->loop_attenuation == 192) out->loop_attenuation = 0;
}
}

Expand Down Expand Up @@ -1897,8 +1903,13 @@ void MSG_ParseDeltaEntity(entity_state_t *to,
to->sound = w & 0x3fff;
if (w & 0x4000)
ext->loop_volume = MSG_ReadByte() / 255.0f;
if (w & 0x8000)
ext->loop_attenuation = MSG_ReadByte() / 64.0f;
if (w & 0x8000) {
int b = MSG_ReadByte();
if (b == 192)
ext->loop_attenuation = ATTN_LOOP_NONE;
else
ext->loop_attenuation = b / 64.0f;
}
} else {
to->sound = MSG_ReadByte();
}
Expand Down
72 changes: 45 additions & 27 deletions src/server/entities.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,31 @@ fix_old_origin(client_t *client, entity_packed_t *state, edict_t *ent, int e)
}
#endif

static bool SV_EntityVisible(client_t *client, edict_t *ent, byte *mask)
{
if (ent->num_clusters == -1)
// too many leafs for individual check, go by headnode
return CM_HeadnodeVisible(CM_NodeNum(client->cm, ent->headnode), mask);

// check individual leafs
for (int i = 0; i < ent->num_clusters; i++)
if (Q_IsBitSet(mask, ent->clusternums[i]))
return true;

return false;
}

static bool SV_EntityAttenuatedAway(vec3_t org, edict_t *ent)
{
float dist = Distance(org, ent->s.origin);
float dist_mult = SOUND_LOOPATTENUATE;

if (ent->x.loop_attenuation && ent->x.loop_attenuation != ATTN_STATIC)
dist_mult = ent->x.loop_attenuation * SOUND_LOOPATTENUATE_MULT;

return (dist - SOUND_FULLVOLUME) * dist_mult > 1.0f;
}

/*
=============
SV_BuildClientFrame
Expand All @@ -384,7 +409,7 @@ copies off the playerstat and areabits.
*/
void SV_BuildClientFrame(client_t *client)
{
int e, i;
int e;
vec3_t org;
edict_t *ent;
edict_t *clent;
Expand Down Expand Up @@ -492,7 +517,7 @@ void SV_BuildClientFrame(client_t *client)
ent_visible = true;

// ignore if not touching a PV leaf
if (ent != clent) {
if (ent != clent && !(client->csr->extended && ent->svflags & SVF_NOCULL)) {
// check area
if (clientcluster >= 0 && !CM_AreasConnected(client->cm, clientarea, ent->areanum)) {
// doors can legally straddle two areas, so
Expand All @@ -502,34 +527,27 @@ void SV_BuildClientFrame(client_t *client)
}
}

if (ent_visible)
{
// beams just check one point for PHS
if (ent->s.renderfx & RF_BEAM) {
if (!Q_IsBitSet(clientphs, ent->clusternums[0]))
ent_visible = false;
}
else {
if (cull_nonvisible_entities) {
if (ent->num_clusters == -1) {
// too many leafs for individual check, go by headnode
if (!CM_HeadnodeVisible(CM_NodeNum(client->cm, ent->headnode), clientpvs))
ent_visible = false;
} else {
// check individual leafs
for (i = 0; i < ent->num_clusters; i++)
if (Q_IsBitSet(clientpvs, ent->clusternums[i]))
break;
if (i == ent->num_clusters)
ent_visible = false; // not visible
}
}
// beams just check one point for PHS
// remaster uses different sound culling rules
bool beam_cull = ent->s.renderfx & RF_BEAM;
bool sound_cull = client->csr->extended && ent->s.sound;

if (beam_cull || cull_nonvisible_entities) {
if (!SV_EntityVisible(client, ent, (beam_cull || sound_cull) ? clientphs : clientpvs))
ent_visible = false; // not visible
}

// don't send sounds if they will be attenuated away
if (sound_cull) {
if (SV_EntityAttenuatedAway(org, ent)) {
if (!ent->s.modelindex)
// don't send sounds if they will be attenuated away
if (Distance(org, ent->s.origin) > 400)
ent_visible = false;
ent_visible = false;
if (ent_visible && !beam_cull && !SV_EntityVisible(client, ent, clientpvs))
ent_visible = false;
}
} else if (!ent->s.modelindex) {
if (Distance(org, ent->s.origin) > 400)
ent_visible = false;
}
}

Expand Down