limit the number of identical sounds playing at once, based on priority ordering (#807)

* Revert "add parallel same-sound limit from DSDA-Doom (#796)"

This reverts commit d7849c8fa7ba4b2c92a1784b92127df267f4e8db.

* first shot at sorting sounds by priority

* fix build

* our S_AdjustSoundParams() already mangles volume into priority

* add/fix comments

* newer same-priority sounds are supposed to stop older ones

* use qsort() instead of undocumented (whoops\!) SDL_qsort()

* back to newer same-priority sounds let older ones finish

* keep the singularity concept alive

* get rid of the lowestpriority concept, we already ordered channels by priority

* keep the "snd_channels = 16" default

* Revert "keep the "snd_channels = 16" default"

This reverts commit 79fc9b3332304aa7cd9f7ac687c46089f1d40918.

* Update s_sound.c

* maintain a stable sort (newer sounds have precedence)

* fix typo/thinko

* fix sort order

* fix comparison function return value

* Update s_sound.c

* last nit-pick (hopefully!)
This commit is contained in:
Fabian Greffrath 2022-11-17 16:26:20 +01:00 committed by GitHub
parent 9bcf2be8a0
commit f73f189abb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 106 deletions

View File

@ -3710,6 +3710,7 @@ enum {
// [FG] uncapped rendering frame rate // [FG] uncapped rendering frame rate
general_uncapped, general_uncapped,
general_vsync, general_vsync,
general_stub1,
general_trans, general_trans,
general_transpct, general_transpct,
general_gamma, general_gamma,
@ -3717,7 +3718,6 @@ enum {
general_title2, general_title2,
general_sndchan, general_sndchan,
general_parallelsfx,
general_pitch, general_pitch,
// [FG] play sounds in full length // [FG] play sounds in full length
general_fullsnd, general_fullsnd,
@ -3789,6 +3789,8 @@ setup_menu_t gen_settings1[] = { // General Settings screen1
{"Vertical Sync", S_YESNO, m_null, M_X, {"Vertical Sync", S_YESNO, m_null, M_X,
M_Y+ general_vsync*M_SPC, {"use_vsync"}, 0, I_ResetScreen}, M_Y+ general_vsync*M_SPC, {"use_vsync"}, 0, I_ResetScreen},
{"", S_SKIP, m_null, M_X, M_Y + general_stub1*M_SPC},
{"Enable predefined translucency", S_YESNO, m_null, M_X, {"Enable predefined translucency", S_YESNO, m_null, M_X,
M_Y+ general_trans*M_SPC, {"translucency"}, 0, M_Trans}, M_Y+ general_trans*M_SPC, {"translucency"}, 0, M_Trans},
@ -3806,10 +3808,6 @@ setup_menu_t gen_settings1[] = { // General Settings screen1
{"Number of Sound Channels", S_NUM|S_PRGWARN, m_null, M_X, {"Number of Sound Channels", S_NUM|S_PRGWARN, m_null, M_X,
M_Y + general_sndchan*M_SPC, {"snd_channels"}}, M_Y + general_sndchan*M_SPC, {"snd_channels"}},
// [FG] parallel same-sound limit from DSDA-Doom
{"Parallel Same-Sound Limit", S_YESNO, m_null, M_X,
M_Y + general_parallelsfx*M_SPC, {"parallel_sfx"}},
{"Enable v1.1 Pitch Effects", S_YESNO, m_null, M_X, {"Enable v1.1 Pitch Effects", S_YESNO, m_null, M_X,
M_Y + general_pitch*M_SPC, {"pitched_sounds"}}, M_Y + general_pitch*M_SPC, {"pitched_sounds"}},

View File

@ -1493,7 +1493,7 @@ default_t defaults[] = {
{ // killough { // killough
"snd_channels", "snd_channels",
(config_t *) &default_numChannels, NULL, (config_t *) &default_numChannels, NULL,
{16}, {1, MAX_CHANNELS}, 0, ss_gen, wad_no, {MAX_CHANNELS}, {1, MAX_CHANNELS}, 0, ss_gen, wad_no,
"number of sound effects handled simultaneously" "number of sound effects handled simultaneously"
}, },
@ -2253,28 +2253,6 @@ default_t defaults[] = {
"1 to play sounds in full length" "1 to play sounds in full length"
}, },
// [FG] parallel same-sound limit
{
"parallel_sfx",
(config_t *) &parallel_sfx, NULL,
{0}, {0, 1}, number, ss_gen, wad_no,
"1 to enable parallel same-sound limit"
},
{
"parallel_sfx_limit",
(config_t *) &parallel_sfx_limit, NULL,
{2}, {0, 32}, number, ss_none, wad_no,
"parallel same-sound limit"
},
{
"parallel_sfx_window",
(config_t *) &parallel_sfx_window, NULL,
{4}, {1, 32}, number, ss_none, wad_no,
"parallel same-sound limit window"
},
// [FG] music backend // [FG] music backend
{ {
"midi_player", "midi_player",

View File

@ -29,6 +29,8 @@
// killough 3/7/98: modified to allow arbitrary listeners in spy mode // killough 3/7/98: modified to allow arbitrary listeners in spy mode
// killough 5/2/98: reindented, removed useless code, beautified // killough 5/2/98: reindented, removed useless code, beautified
#include <stdlib.h> // [FG] qsort()
#include "doomstat.h" #include "doomstat.h"
#include "s_sound.h" #include "s_sound.h"
#include "s_musinfo.h" // [crispy] struct musinfo #include "s_musinfo.h" // [crispy] struct musinfo
@ -228,6 +230,28 @@ static int S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source,
return *vol > 0; return *vol > 0;
} }
//
// S_CompareChannels
//
// A comparison function that determines which sound channel should
// take priority. Can be used with std::sort.
//
// Returns true if the first channel should precede the second.
//
static int S_CompareChannels(const void *arg_a, const void *arg_b)
{
const channel_t *a = (const channel_t *) arg_a;
const channel_t *b = (const channel_t *) arg_b;
// Note that a higher priority number means lower priority!
const int ret = a->priority - b->priority;
return ret ? ret : (b->idnum - a->idnum);
}
// How many instances of the same sfx can be playing concurrently
static const unsigned int max_instances = 3;
// //
// S_getChannel : // S_getChannel :
// //
@ -240,8 +264,10 @@ static int S_getChannel(const mobj_t *origin, sfxinfo_t *sfxinfo,
{ {
// channel number to use // channel number to use
int cnum; int cnum;
int lowestpriority = -1; // haleyjd int instances = 0;
int lpcnum = -1;
// Sort the sound channels by descending priority levels
qsort(channels, numChannels, sizeof(channel_t), S_CompareChannels);
// haleyjd 09/28/06: moved this here. If we kill a sound already // haleyjd 09/28/06: moved this here. If we kill a sound already
// being played, we can use that channel. There is no need to // being played, we can use that channel. There is no need to
@ -250,82 +276,65 @@ static int S_getChannel(const mobj_t *origin, sfxinfo_t *sfxinfo,
// kill old sound // kill old sound
// killough 12/98: replace is_pickup hack with singularity flag // killough 12/98: replace is_pickup hack with singularity flag
// haleyjd 06/12/08: only if subchannel matches // haleyjd 06/12/08: only if subchannel matches
for(cnum = 0; cnum < numChannels; ++cnum) for (cnum = 0; cnum < numChannels; ++cnum)
{ {
if(channels[cnum].sfxinfo && if (!channels[cnum].sfxinfo)
channels[cnum].singularity == singularity && continue;
channels[cnum].origin == origin)
{
// [FG] looping sounds don't interrupt each other
if (channels[cnum].sfxinfo == sfxinfo && channels[cnum].loop && loop)
return -1;
S_StopChannel(cnum); // [FG] looping sounds don't interrupt each other
break; if (channels[cnum].sfxinfo == sfxinfo &&
} channels[cnum].origin == origin &&
channels[cnum].loop && loop)
{
return -1;
}
if (channels[cnum].singularity == singularity &&
channels[cnum].origin == origin)
{
S_StopChannel(cnum);
return cnum;
}
// Limit the number of identical sounds playing at once
if (channels[cnum].sfxinfo == sfxinfo)
{
if (++instances >= max_instances)
{
if (priority < channels[cnum].priority)
{
S_StopChannel(cnum);
return cnum;
}
else
{
return -1;
}
}
}
} }
// Find an open channel // Find an open channel
if(cnum == numChannels) for (cnum = 0; cnum < numChannels; ++cnum)
{ {
// haleyjd 09/28/06: it isn't necessary to look for playing sounds in if (!channels[cnum].sfxinfo)
// the same singularity class again, as we just did that above. Here return cnum;
// we are looking for an open channel. We will also keep track of the
// channel found with the lowest sound priority while doing this.
for(cnum = 0; cnum < numChannels && channels[cnum].sfxinfo; ++cnum)
{
if(channels[cnum].priority > lowestpriority)
{
lowestpriority = channels[cnum].priority;
lpcnum = cnum;
}
}
} }
// None available? // None available?
if(cnum == numChannels) for (cnum = numChannels - 1; cnum >= 0; --cnum)
{ {
// Look for lower priority // Look for lower priority
// haleyjd: we have stored the channel found with the lowest priority if (priority < channels[cnum].priority)
// in the loop above {
if(priority > lowestpriority) S_StopChannel(cnum);
return -1; // No lower priority. Sorry, Charlie. return cnum;
else }
{
S_StopChannel(lpcnum); // Otherwise, kick out lowest priority.
cnum = lpcnum;
}
} }
#ifdef RANGECHECK return -1;
if(cnum >= numChannels)
I_Error("S_getChannel: handle %d out of range\n", cnum);
#endif
return cnum;
} }
// [FG] parallel same-sound limit from DSDA-Doom
boolean parallel_sfx;
int parallel_sfx_limit;
int parallel_sfx_window;
static boolean BlockSFX(sfxinfo_t *sfx)
{
if (!parallel_sfx)
return false;
if (gametic - sfx->parallel_tic >= parallel_sfx_window)
{
sfx->parallel_tic = gametic;
sfx->parallel_count = 0;
}
++sfx->parallel_count;
return sfx->parallel_count > parallel_sfx_limit;
}
static void S_StartSoundEx(const mobj_t *origin, int sfx_id, boolean loop) static void S_StartSoundEx(const mobj_t *origin, int sfx_id, boolean loop)
{ {
@ -392,10 +401,6 @@ static void S_StartSoundEx(const mobj_t *origin, int sfx_id, boolean loop)
sep = NORM_SEP; sep = NORM_SEP;
} }
// [FG] parallel same-sound limit from DSDA-Doom
if (BlockSFX(sfx))
return;
if(pitched_sounds) if(pitched_sounds)
{ {
// hacks to vary the sfx pitches // hacks to vary the sfx pitches

View File

@ -78,16 +78,8 @@ typedef struct sfxinfo_struct {
// haleyjd 04/23/08: additional caching data // haleyjd 04/23/08: additional caching data
unsigned int alen; // length of converted sound pointed to by data unsigned int alen; // length of converted sound pointed to by data
// [FG] parallel same-sound limit from DSDA-Doom
int parallel_tic;
int parallel_count;
} sfxinfo_t; } sfxinfo_t;
extern boolean parallel_sfx;
extern int parallel_sfx_limit;
extern int parallel_sfx_window;
// //
// MusicInfo struct. // MusicInfo struct.
// //