in doom_py/src/vizdoom/src/s_advsound.cpp [973:1415]
static void S_AddSNDINFO (int lump)
{
bool skipToEndIf;
TArray<WORD> list;
FScanner sc(lump);
skipToEndIf = false;
while (sc.GetString ())
{
if (skipToEndIf)
{
if (sc.Compare ("$endif"))
{
skipToEndIf = false;
}
continue;
}
if (sc.String[0] == '$')
{ // Got a command
switch (sc.MatchString (SICommandStrings))
{
case SI_Ambient: {
// $ambient <num> <logical name> [point [atten] | surround | [world]]
// <continuous | random <minsecs> <maxsecs> | periodic <secs>>
// <volume>
FAmbientSound *ambient;
sc.MustGetNumber ();
ambient = &Ambients[sc.Number];
ambient->type = 0;
ambient->periodmin = 0;
ambient->periodmax = 0;
ambient->volume = 0;
ambient->attenuation = 0;
ambient->sound = 0;
sc.MustGetString ();
ambient->sound = FSoundID(S_FindSoundTentative(sc.String));
ambient->attenuation = 0;
sc.MustGetString ();
if (sc.Compare ("point"))
{
float attenuation;
ambient->type = POSITIONAL;
sc.MustGetString ();
if (IsFloat (sc.String))
{
attenuation = (float)atof (sc.String);
sc.MustGetString ();
if (attenuation > 0)
{
ambient->attenuation = attenuation;
}
else
{
ambient->attenuation = 1;
}
}
else
{
ambient->attenuation = 1;
}
}
else if (sc.Compare ("surround"))
{
ambient->type = SURROUND;
sc.MustGetString ();
ambient->attenuation = -1;
}
else
{ // World is an optional keyword
if (sc.Compare ("world"))
{
sc.MustGetString ();
}
}
if (sc.Compare ("continuous"))
{
ambient->type |= CONTINUOUS;
}
else if (sc.Compare ("random"))
{
ambient->type |= RANDOM;
sc.MustGetFloat ();
ambient->periodmin = (int)(sc.Float * TICRATE);
sc.MustGetFloat ();
ambient->periodmax = (int)(sc.Float * TICRATE);
}
else if (sc.Compare ("periodic"))
{
ambient->type |= PERIODIC;
sc.MustGetFloat ();
ambient->periodmin = (int)(sc.Float * TICRATE);
}
else
{
Printf ("Unknown ambient type (%s)\n", sc.String);
}
sc.MustGetFloat ();
ambient->volume = (float)sc.Float;
if (ambient->volume > 1)
ambient->volume = 1;
else if (ambient->volume < 0)
ambient->volume = 0;
}
break;
case SI_Map: {
// Hexen-style $MAP command
int mapnum;
sc.MustGetNumber();
mapnum = sc.Number;
sc.MustGetString();
if (mapnum != 0)
{
HexenMusic[mapnum] = sc.String;
}
}
break;
case SI_Registered:
// I don't think Hexen even pays attention to the $registered command.
case SI_EDFOverride:
break;
case SI_ArchivePath:
sc.MustGetString (); // Unused for now
break;
case SI_PlayerSound: {
// $playersound <player class> <gender> <logical name> <lump name>
FString pclass;
int gender, refid, sfxnum;
S_ParsePlayerSoundCommon (sc, pclass, gender, refid);
sfxnum = S_AddPlayerSound (pclass, gender, refid, sc.String);
if (0 == stricmp(sc.String, "dsempty"))
{
S_sfx[sfxnum].bPlayerSilent = true;
}
}
break;
case SI_PlayerSoundDup: {
// $playersounddup <player class> <gender> <logical name> <target sound name>
FString pclass;
int gender, refid, targid;
S_ParsePlayerSoundCommon (sc, pclass, gender, refid);
targid = S_FindSoundNoHash (sc.String);
if (!S_sfx[targid].bPlayerReserve)
{
sc.ScriptError ("%s is not a player sound", sc.String);
}
S_DupPlayerSound (pclass, gender, refid, targid);
}
break;
case SI_PlayerCompat: {
// $playercompat <player class> <gender> <logical name> <compat sound name>
FString pclass;
int gender, refid;
int sfxfrom, aliasto;
S_ParsePlayerSoundCommon (sc, pclass, gender, refid);
sfxfrom = S_AddSound (sc.String, -1, &sc);
aliasto = S_LookupPlayerSound (pclass, gender, refid);
S_sfx[sfxfrom].link = aliasto;
S_sfx[sfxfrom].bPlayerCompat = true;
}
break;
case SI_PlayerAlias: {
// $playeralias <player class> <gender> <logical name> <logical name of existing sound>
FString pclass;
int gender, refid;
int soundnum;
S_ParsePlayerSoundCommon (sc, pclass, gender, refid);
soundnum = S_FindSoundTentative (sc.String);
S_AddPlayerSoundExisting (pclass, gender, refid, soundnum);
}
break;
case SI_Alias: {
// $alias <name of alias> <name of real sound>
int sfxfrom;
sc.MustGetString ();
sfxfrom = S_AddSound (sc.String, -1, &sc);
sc.MustGetString ();
if (S_sfx[sfxfrom].bPlayerCompat)
{
sfxfrom = S_sfx[sfxfrom].link;
}
S_sfx[sfxfrom].link = S_FindSoundTentative (sc.String);
S_sfx[sfxfrom].NearLimit = -1; // Aliases must use the original sound's limit.
}
break;
case SI_Limit: {
// $limit <logical name> <max channels> [<distance>]
int sfx;
sc.MustGetString ();
sfx = S_FindSoundTentative (sc.String);
sc.MustGetNumber ();
S_sfx[sfx].NearLimit = MIN(MAX(sc.Number, 0), 255);
if (sc.CheckFloat())
{
S_sfx[sfx].LimitRange = float(sc.Float * sc.Float);
}
}
break;
case SI_Singular: {
// $singular <logical name>
int sfx;
sc.MustGetString ();
sfx = S_FindSoundTentative (sc.String);
S_sfx[sfx].bSingular = true;
}
break;
case SI_PitchShift: {
// $pitchshift <logical name> <pitch shift amount>
int sfx;
sc.MustGetString ();
sfx = S_FindSoundTentative (sc.String);
sc.MustGetNumber ();
S_sfx[sfx].PitchMask = (1 << clamp (sc.Number, 0, 7)) - 1;
}
break;
case SI_PitchShiftRange:
// $pitchshiftrange <pitch shift amount>
sc.MustGetNumber ();
CurrentPitchMask = (1 << clamp (sc.Number, 0, 7)) - 1;
break;
case SI_Volume: {
// $volume <logical name> <volume>
int sfx;
sc.MustGetString();
sfx = S_FindSoundTentative(sc.String);
sc.MustGetFloat();
S_sfx[sfx].Volume = (float)sc.Float;
}
break;
case SI_Attenuation: {
// $attenuation <logical name> <attenuation>
int sfx;
sc.MustGetString();
sfx = S_FindSoundTentative(sc.String);
sc.MustGetFloat();
S_sfx[sfx].Attenuation = (float)sc.Float;
}
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.
FRolloffInfo *rolloff;
int type;
int sfx;
sc.MustGetString();
if (sc.Compare("*"))
{
sfx = -1;
rolloff = &S_Rolloff;
}
else
{
sfx = S_FindSoundTentative(sc.String);
rolloff = &S_sfx[sfx].Rolloff;
}
type = ROLLOFF_Doom;
if (!sc.CheckFloat())
{
sc.MustGetString();
if (sc.Compare("linear"))
{
rolloff->RolloffType = ROLLOFF_Linear;
}
else if (sc.Compare("log"))
{
rolloff->RolloffType = ROLLOFF_Log;
}
else if (sc.Compare("custom"))
{
rolloff->RolloffType = ROLLOFF_Custom;
}
else
{
sc.ScriptError("Unknown rolloff type '%s'", sc.String);
}
sc.MustGetFloat();
}
rolloff->MinDistance = (float)sc.Float;
sc.MustGetFloat();
rolloff->MaxDistance = (float)sc.Float;
break;
}
case SI_Random: {
// $random <logical name> { <logical name> ... }
FRandomSoundList random;
list.Clear ();
sc.MustGetString ();
random.SfxHead = S_AddSound (sc.String, -1, &sc);
sc.MustGetStringName ("{");
while (sc.GetString () && !sc.Compare ("}"))
{
WORD sfxto = S_FindSoundTentative (sc.String);
if (sfxto == random.SfxHead)
{
Printf("Definition of random sound '%s' refers to itself recursively.", sc.String);
continue;
}
list.Push (sfxto);
}
if (list.Size() == 1)
{ // Only one sound: treat as $alias
S_sfx[random.SfxHead].link = list[0];
S_sfx[random.SfxHead].NearLimit = -1;
}
else if (list.Size() > 1)
{ // Only add non-empty random lists
random.NumSounds = (WORD)list.Size();
S_sfx[random.SfxHead].link = (WORD)S_rnd.Push (random);
S_sfx[random.SfxHead].bRandomHeader = true;
S_rnd[S_sfx[random.SfxHead].link].Sounds = new WORD[random.NumSounds];
memcpy (S_rnd[S_sfx[random.SfxHead].link].Sounds, &list[0], sizeof(WORD)*random.NumSounds);
S_sfx[random.SfxHead].NearLimit = -1;
}
}
break;
case SI_MusicVolume: {
sc.MustGetString();
FString musname (sc.String);
sc.MustGetFloat();
FMusicVolume *mv = (FMusicVolume *)M_Malloc (sizeof(*mv) + musname.Len());
mv->Volume = (float)sc.Float;
strcpy (mv->MusicName, musname);
mv->Next = MusicVolumes;
MusicVolumes = mv;
}
break;
case SI_MusicAlias: {
sc.MustGetString();
int lump = Wads.CheckNumForName(sc.String, ns_music);
if (lump >= 0)
{
// do not set the alias if a later WAD defines its own music of this name
int file = Wads.GetLumpFile(lump);
int sndifile = Wads.GetLumpFile(sc.LumpNum);
if (file > sndifile)
{
sc.MustGetString();
continue;
}
}
FName alias = sc.String;
sc.MustGetString();
FName mapped = sc.String;
// only set the alias if the lump it maps to exists.
if (mapped == NAME_None || Wads.CheckNumForName(sc.String, ns_music) >= 0)
{
MusicAliases[alias] = mapped;
}
}
break;
case SI_MidiDevice: {
sc.MustGetString();
FName nm = sc.String;
FScanner::SavedPos save = sc.SavePos();
sc.SetCMode(true);
sc.MustGetString();
MidiDeviceSetting devset;
if (sc.Compare("timidity")) devset.device = MDEV_TIMIDITY;
else if (sc.Compare("fmod") || sc.Compare("sndsys")) devset.device = MDEV_SNDSYS;
else if (sc.Compare("standard")) devset.device = MDEV_MMAPI;
else if (sc.Compare("opl")) devset.device = MDEV_OPL;
else if (sc.Compare("default")) devset.device = MDEV_DEFAULT;
else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH;
else if (sc.Compare("gus")) devset.device = MDEV_GUS;
else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI;
else sc.ScriptError("Unknown MIDI device %s\n", sc.String);
if (sc.CheckString(","))
{
sc.SetCMode(false);
sc.MustGetString();
devset.args = sc.String;
}
else
{
// This does not really do what one might expect, because the next token has already been parsed and can be a '$'.
// So in order to continue parsing without C-Mode, we need to reset and parse the last token again.
sc.SetCMode(false);
sc.RestorePos(save);
sc.MustGetString();
}
MidiDevices[nm] = devset;
}
break;
case SI_IfDoom: //also Chex
case SI_IfStrife:
case SI_IfHeretic:
case SI_IfHexen:
skipToEndIf = !CheckGame(sc.String+3, true);
break;
}
}
else
{ // Got a logical sound mapping
FString name (sc.String);
sc.MustGetString ();
S_AddSound (name, sc.String, &sc);
}
}
}