- Fixed: Actor-less sounds that aren't played on CHAN_AUTO should still be

subject to channel overriding.
- Re-added priority selection based on sound usage.
- Reduced the number of virtual channels to match the number of real
  channels.
- Added customizable rolloff, including Doom's standard linear gain rolloff.
  SNDINFO commands are:
    $rolloff <sound> <min distance> <max distance>          -- linear gain (like Doom)
    $rolloff <sound> linear <min distance> <max distance>   -- linear volume
    $rolloff <sound> log <min distance> <rolloff factor>    -- logarithmic
    $rolloff <sound> custom <min distance> <max distance>   -- use SNDCURVE lump
  Anything closer than min distance is full volume and anything further than
  max distance is inaudible. Logarithmic rolloff does not have a maximum
  distance; it has a scalar that controls how quickly the volume drops off
  instead.


SVN r834 (trunk)
This commit is contained in:
Randy Heit 2008-03-22 03:33:41 +00:00
parent cab572c008
commit 84d125cf21
8 changed files with 328 additions and 139 deletions

View File

@ -1,3 +1,20 @@
March 21, 2008
- Fixed: Actor-less sounds that aren't played on CHAN_AUTO should still be
subject to channel overriding.
- Re-added priority selection based on sound usage.
- Reduced the number of virtual channels to match the number of real
channels.
- Added customizable rolloff, including Doom's standard linear gain rolloff.
SNDINFO commands are:
$rolloff <sound> <min distance> <max distance> -- linear gain (like Doom)
$rolloff <sound> linear <min distance> <max distance> -- linear volume
$rolloff <sound> log <min distance> <rolloff factor> -- logarithmic
$rolloff <sound> custom <min distance> <max distance> -- use SNDCURVE lump
Anything closer than min distance is full volume and anything further than
max distance is inaudible. Logarithmic rolloff does not have a maximum
distance; it has a scalar that controls how quickly the volume drops off
instead.
March 21, 2008 (Changes by Graf Zahl)
- Disabled scrolling of 3DMIDTEX textures. Due to the special needs this
cannot work properly.

View File

@ -3,7 +3,7 @@
** Routines for managing SNDINFO lumps and ambient sounds
**
**---------------------------------------------------------------------------
** Copyright 1998-2007 Randy Heit
** Copyright 1998-2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -145,7 +145,8 @@ enum SICommands
SI_IfDoom,
SI_IfHeretic,
SI_IfHexen,
SI_IfStrife
SI_IfStrife,
SI_Rolloff,
};
// Blood was a cool game. If Monolith ever releases the source for it,
@ -240,6 +241,7 @@ static const char *SICommandStrings[] =
"$ifheretic",
"$ifhexen",
"$ifstrife",
"$rolloff",
NULL
};
@ -450,7 +452,10 @@ int S_AddSoundLump (const char *logicalname, int lump)
newsfx.bUsed = false;
newsfx.bSingular = false;
newsfx.bTentative = false;
newsfx.RolloffType = ROLLOFF_Doom;
newsfx.link = sfxinfo_t::NO_LINK;
newsfx.MinDistance = 0;
newsfx.MaxDistance = 0;
return (int)S_sfx.Push (newsfx);
}
@ -1136,6 +1141,62 @@ static void S_AddSNDINFO (int lump)
CurrentPitchMask = (1 << clamp (sc.Number, 0, 7)) - 1;
break;
case SI_Rolloff: {
// $rolloff *|<logical name> [linear|log|custom] <min dist> <max dist/rolloff factor>
// Using * for the name makes it the default for sounds that don't specify otherwise.
float *min, *max;
int type;
int sfx;
sc.MustGetString();
if (sc.Compare("*"))
{
sfx = -1;
min = &S_MinDistance;
max = &S_MaxDistanceOrRolloffFactor;
}
else
{
sfx = S_FindSoundTentative(sc.String);
min = &S_sfx[sfx].MinDistance;
max = &S_sfx[sfx].MaxDistance;
}
type = ROLLOFF_Doom;
if (!sc.CheckFloat())
{
sc.MustGetString();
if (sc.Compare("linear"))
{
type = ROLLOFF_Linear;
}
else if (sc.Compare("log"))
{
type = ROLLOFF_Log;
}
else if (sc.Compare("custom"))
{
type = ROLLOFF_Custom;
}
else
{
sc.ScriptError("Unknown rolloff type '%s'", sc.String);
}
sc.MustGetFloat();
}
if (sfx < 0)
{
S_RolloffType = type;
}
else
{
S_sfx[sfx].RolloffType = type;
}
*min = sc.Float;
sc.MustGetFloat();
*max = sc.Float;
break;
}
case SI_Random: {
// $random <logical name> { <logical name> ... }
FRandomSoundList random;

View File

@ -74,17 +74,6 @@
#define S_PITCH_PERTURB 1
#define S_STEREO_SWING 0.75
/* Sound curve parameters for Doom */
// Maximum sound distance
const int S_CLIPPING_DIST = 1200;
// Sounds closer than this to the listener are maxed out.
// Originally: 200.
const int S_CLOSE_DIST = 160;
const float S_ATTENUATOR = S_CLIPPING_DIST - S_CLOSE_DIST;
// TYPES -------------------------------------------------------------------
struct MusPlayingInfo
@ -112,12 +101,10 @@ static void CalcPosVel (fixed_t *pt, AActor *mover, int constz, float pos[3],
// PRIVATE DATA DEFINITIONS ------------------------------------------------
int MAX_SND_DIST;
static bool SoundPaused; // whether sound effects are paused
static bool MusicPaused; // whether music is paused
static MusPlayingInfo mus_playing; // music currently being played
static FString LastSong; // last music that was played
static float *SoundCurve;
static FPlayList *PlayList;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -127,6 +114,12 @@ int sfx_empty;
FSoundChan *Channels;
FSoundChan *FreeChannels;
int S_RolloffType;
float S_MinDistance;
float S_MaxDistanceOrRolloffFactor;
BYTE *S_SoundCurve;
int S_SoundCurveSize;
CVAR (Bool, snd_surround, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // [RH] Use surround sounds?
FBoolCVar noisedebug ("noise", false, 0); // [RH] Print sound debugging info?
CVAR (Int, snd_channels, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number of channels available
@ -251,43 +244,23 @@ void S_AddLocalSndInfo(int lump);
void S_Init ()
{
int i;
int curvelump;
atterm (S_Shutdown);
// remove old data (S_Init can be called multiple times!)
if (SoundCurve)
if (S_SoundCurve != NULL)
{
delete[] SoundCurve;
delete[] S_SoundCurve;
}
// Heretic and Hexen have sound curve lookup tables. Doom does not.
curvelump = Wads.CheckNumForName ("SNDCURVE");
if (curvelump >= 0)
{
MAX_SND_DIST = Wads.LumpLength (curvelump);
SoundCurve = new float[MAX_SND_DIST];
FMemLump lump = Wads.ReadLump(curvelump);
BYTE *lumpmem = (BYTE *)lump.GetMem();
// The maximum value in a SNDCURVE lump is 127, so scale it to
// fit our sound system's volume levels. Also, FMOD's volumes
// are linear, whereas we want the "dumb" logarithmic volumes
// for our "2D" sounds.
for (i = 0; i < S_CLIPPING_DIST; ++i)
{
SoundCurve[i] = (powf(10.f, (lumpmem[i] / 127.f)) - 1.f) / 9.f;
}
}
else
{
MAX_SND_DIST = S_CLIPPING_DIST;
SoundCurve = new float[S_CLIPPING_DIST];
for (i = 0; i < S_CLIPPING_DIST; ++i)
{
SoundCurve[i] = (powf(10.f, MIN(1.f, (S_CLIPPING_DIST - i) / S_ATTENUATOR)) - 1.f) / 9.f;
}
S_SoundCurveSize = Wads.LumpLength (curvelump);
S_SoundCurve = new BYTE[S_SoundCurveSize];
Wads.ReadLump(curvelump, S_SoundCurve);
}
// Free all channels for use.
@ -348,10 +321,10 @@ void S_Shutdown ()
}
FreeChannels = NULL;
if (SoundCurve != NULL)
if (S_SoundCurve != NULL)
{
delete[] SoundCurve;
SoundCurve = NULL;
delete[] S_SoundCurve;
S_SoundCurve = NULL;
}
if (PlayList != NULL)
{
@ -734,36 +707,34 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel,
{
return;
}
// Select priority.
if (attenuation <= 0)
if (attenuation <= 0 || mover == players[consoleplayer].camera)
{
basepriority = 200;
basepriority = 40;
}
else
{
switch (channel)
{
case CHAN_WEAPON:
basepriority = 100;
basepriority = 20;
break;
case CHAN_VOICE:
basepriority = 75;
basepriority = 10;
break;
default:
case CHAN_BODY:
basepriority = 50;
basepriority = 0;
break;
case CHAN_ITEM:
basepriority = 25;
basepriority = -10;
break;
}
if (attenuation == 1)
basepriority += 50;
basepriority = int(basepriority * attenuation);
}
if (mover != NULL)
{
if (channel == CHAN_AUTO)
if (mover != NULL && channel == CHAN_AUTO)
{ // Select a channel that isn't already playing something.
BYTE mask = mover->SoundChans;
@ -788,9 +759,8 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel,
}
}
// If this actor is already playing something on the selected channel, stop it.
if (mover->SoundChans & (1 << channel))
if ((mover == NULL && channel != CHAN_AUTO) || (mover != NULL && mover->SoundChans & (1 << channel)))
{
for (chan = Channels; chan != NULL; chan = chan->NextChan)
{
@ -801,7 +771,6 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel,
}
}
}
}
// Vary the sfx pitches.
if (sfx->PitchMask != 0)
@ -830,7 +799,7 @@ static void S_StartSound (fixed_t *pt, AActor *mover, int channel,
pt2[2] = z;
CalcPosVel (pt2, mover, chanflags & CHAN_LISTENERZ, pos, vel);
}
chan = GSnd->StartSound3D (sfx, volume, pitch, looping, pos, vel, !(chanflags & CHAN_NOPAUSE));
chan = GSnd->StartSound3D (sfx, volume, attenuation, pitch, basepriority, looping, pos, vel, !(chanflags & CHAN_NOPAUSE));
if (chan != NULL)
{
chan->ConstZ = !!(chanflags & CHAN_LISTENERZ);
@ -1047,6 +1016,7 @@ void S_StopAllChannels ()
{
GSnd->StopSound(Channels);
}
GSnd->UpdateSounds();
}
//==========================================================================

View File

@ -54,14 +54,34 @@ struct sfxinfo_t
WORD bUsed:1;
WORD bSingular:1;
WORD bTentative:1;
WORD RolloffType:2;
WORD link;
enum { NO_LINK = 0xffff };
float MinDistance;
union { float MaxDistance, RolloffFactor; };
};
// Rolloff types
enum
{
ROLLOFF_Doom, // Linear rolloff with a logarithmic volume scale
ROLLOFF_Linear, // Linear rolloff with a linear volume scale
ROLLOFF_Log, // Logarithmic rolloff (standard hardware type)
ROLLOFF_Custom // Lookup volume from SNDCURVE
};
// the complete set of sound effects
extern TArray<sfxinfo_t> S_sfx;
// Default rolloff information.
extern int S_RolloffType;
extern float S_MinDistance;
extern float S_MaxDistanceOrRolloffFactor;
extern BYTE *S_SoundCurve;
extern int S_SoundCurveSize;
// Information about one playing sound.
struct FSoundChan
{
@ -75,6 +95,7 @@ struct FSoundChan
int SoundID; // Sound ID of playing sound
int OrgID; // Sound ID of sound used to start this channel
float Volume;
float DistanceScale;
BYTE EntChannel; // Actor's sound channel.
bool Loop;
bool Is3D;

View File

@ -94,8 +94,6 @@ static const char *Enum_NameForNum(const FEnumList *list, int num);
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern int MAX_SND_DIST;
EXTERN_CVAR (String, snd_output)
EXTERN_CVAR (Float, snd_musicvolume)
EXTERN_CVAR (Int, snd_buffersize)
@ -116,10 +114,14 @@ CVAR (Bool, snd_dspnet, false, 0)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static const int S_CLIPPING_DIST = 1200;
static const int S_CLOSE_DIST = 160;
static const ReverbContainer *PrevEnvironment;
// The rolloff callback is called during FMOD::Sound::play, so we need this
// global variable to contain the sound info during that time for the
// callback.
static sfxinfo_t *GSfxInfo;
static float GDistScale;
// In the below lists, duplicate entries are for user selection. When
// queried, only the first one for the particular value is shown.
static const FEnumList OutputNames[] =
@ -551,13 +553,13 @@ bool FMODSoundRenderer::Init()
{
initflags |= FMOD_INIT_ENABLE_DSPNET;
}
result = Sys->init(MAX_CHANNELS, initflags, 0);
result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0);
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
{ // The speaker mode selected isn't supported by this soundcard. Switch it back to stereo.
result = Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
ERRCHECK(result);
result = Sys->init(MAX_CHANNELS, initflags, 0);
result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0);
ERRCHECK(result);
}
if (result != FMOD_OK)
@ -580,21 +582,8 @@ bool FMODSoundRenderer::Init()
result = SPC_CreateCodec(Sys);
float rolloff_factor;
if (gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Strife)
{
rolloff_factor = 1.7f;
}
else if (gameinfo.gametype == GAME_Heretic)
{
rolloff_factor = 1.24f;
}
else
{
rolloff_factor = 0.96f;
}
Sys->set3DSettings(1.f, 100.f, rolloff_factor);
Sys->set3DSettings(0.5f, 96.f, 1.f);
Sys->set3DRolloffCallback(RolloffCallback);
snd_sfxvolume.Callback ();
return true;
}
@ -933,6 +922,7 @@ FSoundChan *FMODSoundRenderer::StartSound(sfxinfo_t *sfx, float vol, int pitch,
freq = PITCH(sfx->frequency, pitch);
GSfxInfo = sfx;
result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx->data, true, &chan);
if (FMOD_OK == result)
{
@ -967,18 +957,40 @@ FSoundChan *FMODSoundRenderer::StartSound(sfxinfo_t *sfx, float vol, int pitch,
//
//==========================================================================
FSoundChan *FMODSoundRenderer::StartSound3D(sfxinfo_t *sfx, float vol, int pitch,
bool looping, float pos[3], float vel[3], bool pausable)
FSoundChan *FMODSoundRenderer::StartSound3D(sfxinfo_t *sfx, float vol, float distscale,
int pitch, int priority, bool looping, float pos[3], float vel[3], bool pausable)
{
int id = int(sfx - &S_sfx[0]);
FMOD_RESULT result;
FMOD_MODE mode;
FMOD::Channel *chan;
float freq;
float def_freq, def_vol, def_pan;
int def_priority;
freq = PITCH(sfx->frequency, pitch);
// Change the sound's default priority before playing it.
if (FMOD_OK == ((FMOD::Sound *)sfx->data)->getDefaults(&def_freq, &def_vol, &def_pan, &def_priority))
{
((FMOD::Sound *)sfx->data)->setDefaults(def_freq, def_vol, def_pan, clamp(def_priority - priority, 1, 256));
}
else
{
def_priority = -1;
}
// Play it.
GSfxInfo = sfx;
GDistScale = distscale;
result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx->data, true, &chan);
// Then set the priority back.
if (def_priority >= 0)
{
((FMOD::Sound *)sfx->data)->setDefaults(def_freq, def_vol, def_pan, def_priority);
}
if (FMOD_OK == result)
{
result = chan->getMode(&mode);
@ -1013,7 +1025,9 @@ FSoundChan *FMODSoundRenderer::StartSound3D(sfxinfo_t *sfx, float vol, int pitch
chan->setVolume(vol);
chan->set3DAttributes((FMOD_VECTOR *)pos, (FMOD_VECTOR *)vel);
chan->setPaused(false);
return CommonChannelSetup(chan, true);
FSoundChan *schan = CommonChannelSetup(chan, true);
schan->DistanceScale = distscale;
return schan;
}
DPrintf ("Sound %s failed to play: %d\n", sfx->name.GetChars(), result);
@ -1035,6 +1049,7 @@ FSoundChan *FMODSoundRenderer::CommonChannelSetup(FMOD::Channel *chan, bool is3d
chan->setUserData(schan);
chan->setCallback(FMOD_CHANNEL_CALLBACKTYPE_END, ChannelEndCallback, 0);
schan->Is3D = is3d;
GSfxInfo = NULL;
return schan;
}
@ -1051,33 +1066,8 @@ void FMODSoundRenderer::StopSound(FSoundChan *chan)
if (chan->SysChannel != NULL)
{
((FMOD::Channel *)chan->SysChannel)->setCallback(FMOD_CHANNEL_CALLBACKTYPE_END, 0, 0);
((FMOD::Channel *)chan->SysChannel)->stop();
}
S_ReturnChannel(chan);
}
//==========================================================================
//
// FMODSoundRenderer :: ChannelEndCallback static
//
// Called when the channel finishes playing.
//
//==========================================================================
FMOD_RESULT F_CALLBACK FMODSoundRenderer::ChannelEndCallback
(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type,
int cmd, unsigned int data1, unsigned int data2)
{
assert(type == FMOD_CHANNEL_CALLBACKTYPE_END);
FMOD::Channel *chan = (FMOD::Channel *)channel;
FSoundChan *schan;
if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL)
{
S_ReturnChannel(schan);
}
return FMOD_OK;
}
//==========================================================================
@ -1305,8 +1295,24 @@ void FMODSoundRenderer::DoLoad(void **slot, sfxinfo_t *sfx)
FMOD_MODE samplemode;
FMOD_CREATESOUNDEXINFO exinfo = { sizeof(exinfo), };
FMOD::Sound *sample;
int rolloff;
float mindist, maxdist;
samplemode = FMOD_3D | FMOD_OPENMEMORY | FMOD_SOFTWARE;
if (sfx->MaxDistance == 0)
{
mindist = S_MinDistance;
maxdist = S_MaxDistanceOrRolloffFactor;
rolloff = S_RolloffType;
}
else
{
mindist = sfx->MinDistance;
maxdist = sfx->MaxDistance;
rolloff = sfx->RolloffType;
}
sfxdata = NULL;
errcount = 0;
@ -1401,11 +1407,14 @@ void FMODSoundRenderer::DoLoad(void **slot, sfxinfo_t *sfx)
break;
}
if (sfx->data)
if (sample != NULL)
{
// Match s_sound.cpp min distance.
// Max distance is irrelevant.
sample->set3DMinMaxDistance(float(S_CLOSE_DIST), 10000.f);
if (rolloff == ROLLOFF_Log)
{
maxdist = 10000.f;
}
sample->set3DMinMaxDistance(mindist, maxdist);
sample->setUserData(sfx);
}
if (sfxdata != NULL)
@ -1445,3 +1454,105 @@ void FMODSoundRenderer::getsfx(sfxinfo_t *sfx)
}
DoLoad(&sfx->data, sfx);
}
//==========================================================================
//
// FMODSoundRenderer :: ChannelEndCallback static
//
// Called when the channel finishes playing.
//
//==========================================================================
FMOD_RESULT F_CALLBACK FMODSoundRenderer::ChannelEndCallback
(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type,
int cmd, unsigned int data1, unsigned int data2)
{
assert(type == FMOD_CHANNEL_CALLBACKTYPE_END);
FMOD::Channel *chan = (FMOD::Channel *)channel;
FSoundChan *schan;
if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL)
{
S_ReturnChannel(schan);
}
return FMOD_OK;
}
//==========================================================================
//
// FMODSoundRenderer :: RolloffCallback static
//
// Calculates a volume for the sound based on distance.
//
//==========================================================================
float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNEL *channel, float distance)
{
FMOD::Channel *chan = (FMOD::Channel *)channel;
FSoundChan *schan;
// Defaults for Doom.
int type = ROLLOFF_Doom;
sfxinfo_t *sfx;
float min;
float max;
float factor;
float volume;
type = S_RolloffType;
factor = S_MaxDistanceOrRolloffFactor;
min = S_MinDistance;
max = S_MaxDistanceOrRolloffFactor;
if (GSfxInfo != NULL)
{
sfx = GSfxInfo;
distance *= GDistScale;
}
else if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL)
{
sfx = schan->SfxInfo;
distance *= schan->DistanceScale;
}
else
{
return 0;
}
if (sfx == NULL)
{
return 0;
}
if (sfx->MaxDistance == 0)
{
type = sfx->RolloffType;
factor = sfx->RolloffFactor;
}
chan->get3DMinMaxDistance(&min, &max);
if (distance <= min)
{
return 1;
}
if (type == ROLLOFF_Log)
{ // Logarithmic rolloff has no max distance where it goes silent.
return min / (min + factor * (distance - min));
}
if (distance >= max)
{
return 0;
}
volume = (max - distance) / (max - min);
if (type == ROLLOFF_Custom && S_SoundCurve != NULL)
{
volume = S_SoundCurve[int(S_SoundCurveSize * (1 - volume))] / 127.f;
}
if (type == ROLLOFF_Linear)
{
return volume;
}
else
{
return (powf(10.f, volume) - 1.f) / 9.f;
}
}

View File

@ -25,7 +25,7 @@ public:
// Starts a sound.
FSoundChan *StartSound (sfxinfo_t *sfx, float vol, int pitch, bool looping, bool pauseable);
FSoundChan *StartSound3D (sfxinfo_t *sfx, float vol, int pitch, bool looping, float pos[3], float vel[3], bool pauseable);
FSoundChan *StartSound3D (sfxinfo_t *sfx, float vol, float distscale, int pitch, int priority, bool looping, float pos[3], float vel[3], bool pauseable);
// Stops a sound channel.
void StopSound (FSoundChan *chan);
@ -56,6 +56,7 @@ private:
static FMOD_RESULT F_CALLBACK ChannelEndCallback
(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, int cmd, unsigned int data1, unsigned int data2);
static float F_CALLBACK RolloffCallback(FMOD_CHANNEL *channel, float distance);
FSoundChan *CommonChannelSetup(FMOD::Channel *chan, bool is3d);
void DoLoad (void **slot, sfxinfo_t *sfx);

View File

@ -79,7 +79,7 @@ public:
// Starts a sound.
virtual FSoundChan *StartSound (sfxinfo_t *sfx, float vol, int pitch, bool looping, bool pauseable) = 0;
virtual FSoundChan *StartSound3D (sfxinfo_t *sfx, float vol, int pitch, bool looping, float pos[3], float vel[3], bool pauseable) = 0;
virtual FSoundChan *StartSound3D (sfxinfo_t *sfx, float vol, float distscale, int pitch, int priority, bool looping, float pos[3], float vel[3], bool pauseable) = 0;
// Stops a sound channel.
virtual void StopSound (FSoundChan *chan) = 0;

View File

@ -46,6 +46,9 @@ world/quake dsquake
misc/freeze icedth1
misc/icebreak icebrk1a
// The released source code used a min distance of 160. Why?
// Retail Doom and Strife use 200.
$rolloff * 200 1200
/***************************************************************************/
/* */
@ -55,6 +58,7 @@ misc/icebreak icebrk1a
$ifdoom
// BOOM has pitch shifting equivalent to a range of 4. I never got to hear
// Doom when it used pitch shifting, so I don't know if this is correct or not.
$pitchshiftrange 4
@ -483,6 +487,8 @@ $endif // ifdoom
$ifheretic
$rolloff * custom 0 1600
$pitchshiftrange 2
$playersound player male *wimpydeath plrwdth
@ -731,6 +737,8 @@ $endif // ifheretic
$ifhexen
$rolloff * custom 0 2025
$pitchshiftrange 3
$random PlayerFighterExtremeDeathPicker { PlayerFighterExtreme1Death