diff --git a/inc/client/client.h b/inc/client/client.h index 62d49b474..e4e0233d7 100644 --- a/inc/client/client.h +++ b/inc/client/client.h @@ -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 diff --git a/inc/shared/shared.h b/inc/shared/shared.h index 018f0d875..babf24342 100644 --- a/inc/shared/shared.h +++ b/inc/shared/shared.h @@ -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 diff --git a/src/client/sound/al.c b/src/client/sound/al.c index 60696bd7f..8933c5d92 100644 --- a/src/client/sound/al.c +++ b/src/client/sound/al.c @@ -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); diff --git a/src/client/sound/dma.c b/src/client/sound/dma.c index b18ec1c0d..bbd2b0e98 100644 --- a/src/client/sound/dma.c +++ b/src/client/sound/dma.c @@ -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 { @@ -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; @@ -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; @@ -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; } @@ -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; diff --git a/src/client/sound/main.c b/src/client/sound/main.c index 96b99abf2..c5ddf219d 100644 --- a/src/client/sound/main.c +++ b/src/client/sound/main.c @@ -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 diff --git a/src/client/sound/mem.c b/src/client/sound/mem.c index 42c78192b..890c49d6c 100644 --- a/src/client/sound/mem.c +++ b/src/client/sound/mem.c @@ -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; @@ -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 @@ -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); diff --git a/src/client/sound/sound.h b/src/client/sound/sound.h index efa7667a6..dce2abac8 100644 --- a/src/client/sound/sound.h +++ b/src/client/sound/sound.h @@ -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; @@ -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); diff --git a/src/common/msg.c b/src/common/msg.c index b2f9547f6..2e72c6a5e 100644 --- a/src/common/msg.c +++ b/src/common/msg.c @@ -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; } } @@ -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(); } diff --git a/src/server/entities.c b/src/server/entities.c index 1c64fa711..2c296aa06 100644 --- a/src/server/entities.c +++ b/src/server/entities.c @@ -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 @@ -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; @@ -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 @@ -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; } }