2020-04-12 06:09:38 +00:00
|
|
|
/*
|
|
|
|
** s_sound.cpp
|
|
|
|
** Main sound engine
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 1998-2016 Randy Heit
|
|
|
|
** Copyright 2002-2019 Christoph Oelckers
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "raze_sound.h"
|
|
|
|
#include "c_cvars.h"
|
|
|
|
#include "basics.h"
|
|
|
|
#include "stats.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
#include "filesystem.h"
|
|
|
|
#include "serializer.h"
|
|
|
|
#include "name.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "gamecontrol.h"
|
|
|
|
#include "build.h"
|
|
|
|
|
|
|
|
extern ReverbContainer* ForcedEnvironment;
|
|
|
|
static int LastReverb;
|
|
|
|
|
|
|
|
CUSTOM_CVAR(Bool, snd_reverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
{
|
|
|
|
FX_SetReverb(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is for testing reverb settings.
|
|
|
|
CUSTOM_CVAR(Int, snd_reverbtype, -1, 0)
|
|
|
|
{
|
|
|
|
FX_SetReverb(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FX_SetReverb(int strength)
|
|
|
|
{
|
|
|
|
if (strength == -1) strength = LastReverb;
|
|
|
|
if (snd_reverbtype > -1)
|
|
|
|
{
|
|
|
|
strength = snd_reverbtype;
|
|
|
|
ForcedEnvironment = S_FindEnvironment(strength);
|
|
|
|
}
|
|
|
|
else if (snd_reverb && strength > 0)
|
|
|
|
{
|
|
|
|
// todo: optimize environments. The original "reverb" was garbage and not usable as reference.
|
|
|
|
if (strength < 64) strength = 0x1400;
|
2020-08-09 11:26:12 +00:00
|
|
|
else if (strength < 180) strength = 0x1503;
|
|
|
|
else if (strength < 220) strength = 0x1502;
|
2020-04-12 06:09:38 +00:00
|
|
|
else strength = 0x1900;
|
|
|
|
LastReverb = strength;
|
|
|
|
ForcedEnvironment = S_FindEnvironment(strength);
|
|
|
|
}
|
|
|
|
else ForcedEnvironment = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_NoiseDebug
|
|
|
|
//
|
|
|
|
// [RH] Print sound debug info. Called by status bar.
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
FString NoiseDebug(SoundEngine *engine)
|
|
|
|
{
|
|
|
|
FVector3 listener;
|
|
|
|
FVector3 origin;
|
|
|
|
|
|
|
|
listener = engine->GetListener().position;
|
|
|
|
int ch = 0;
|
|
|
|
|
|
|
|
FString out;
|
|
|
|
|
|
|
|
out.Format("*** SOUND DEBUG INFO ***\nListener: %3.2f %2.3f %2.3f\n"
|
|
|
|
"x y z vol dist chan pri flags aud pos name\n", listener.X, listener.Y, listener.Z);
|
|
|
|
|
|
|
|
for (auto chan = engine->GetChannels(); chan; chan = chan->NextChan)
|
|
|
|
{
|
|
|
|
if (!(chan->ChanFlags & CHANF_IS3D))
|
|
|
|
{
|
|
|
|
out += "--- --- --- --- ";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
engine->CalcPosVel(chan, &origin, nullptr);
|
|
|
|
out.AppendFormat(TEXTCOLOR_GOLD "%5.0f | %5.0f | %5.0f | %5.0f ", origin.X, origin.Z, origin.Y, (origin - listener).Length());
|
|
|
|
}
|
|
|
|
out.AppendFormat("%-.2g %-4d %-4d %sF%s3%sZ%sU%sM%sN%sA%sL%sE%sV" TEXTCOLOR_GOLD " %-5.4f %-4u %d: %s %p\n", chan->Volume, chan->EntChannel, chan->Priority,
|
|
|
|
(chan->ChanFlags & CHANF_FORGETTABLE) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
(chan->ChanFlags & CHANF_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED,
|
|
|
|
GSnd->GetAudibility(chan), GSnd->GetPosition(chan), ((int)chan->OrgID)-1, engine->GetSounds()[chan->SoundID].name.GetChars(), chan->Source);
|
|
|
|
ch++;
|
|
|
|
}
|
|
|
|
out.AppendFormat("%d channels\n", ch);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ADD_STAT(sounddebug)
|
|
|
|
{
|
|
|
|
return NoiseDebug(soundEngine);
|
|
|
|
}
|
|
|
|
|
|
|
|
CVAR(Bool, snd_extendedlookup, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|
|
|
|
|
|
|
int S_LookupSound(const char* fn)
|
|
|
|
{
|
|
|
|
static const char * const sndformats[] = { "OGG", "FLAC", "WAV" };
|
|
|
|
if (snd_extendedlookup)
|
|
|
|
{
|
|
|
|
int lump = fileSystem.FindFileWithExtensions(StripExtension(fn), sndformats, countof(sndformats));
|
|
|
|
if (lump >= 0) return lump;
|
|
|
|
}
|
|
|
|
return fileSystem.FindFile(fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Although saving the sound system's state is supposed to be an engine
|
|
|
|
// feature, the specifics cannot be set up there, this needs to be on the client side.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static FSerializer& Serialize(FSerializer& arc, const char* key, FSoundChan& chan, FSoundChan* def)
|
|
|
|
{
|
|
|
|
if (arc.BeginObject(key))
|
|
|
|
{
|
|
|
|
arc("sourcetype", chan.SourceType)
|
|
|
|
("soundid", chan.SoundID)
|
|
|
|
("orgid", chan.OrgID)
|
|
|
|
("volume", chan.Volume)
|
|
|
|
("distancescale", chan.DistanceScale)
|
|
|
|
("pitch", chan.Pitch)
|
|
|
|
("chanflags", chan.ChanFlags)
|
|
|
|
("entchannel", chan.EntChannel)
|
|
|
|
("priority", chan.Priority)
|
|
|
|
("nearlimit", chan.NearLimit)
|
|
|
|
("starttime", chan.StartTime)
|
|
|
|
("rolloftype", chan.Rolloff.RolloffType)
|
|
|
|
("rolloffmin", chan.Rolloff.MinDistance)
|
|
|
|
("rolloffmax", chan.Rolloff.MaxDistance)
|
|
|
|
("limitrange", chan.LimitRange)
|
|
|
|
.Array("point", chan.Point, 3);
|
|
|
|
|
|
|
|
int SourceIndex = 0;
|
|
|
|
if (arc.isWriting())
|
|
|
|
{
|
|
|
|
if (chan.SourceType == SOURCE_Actor) SourceIndex = int((spritetype*)(chan.Source) - sprite);
|
|
|
|
else SourceIndex = soundEngine->SoundSourceIndex(&chan);
|
|
|
|
}
|
|
|
|
arc("Source", SourceIndex);
|
|
|
|
if (arc.isReading())
|
|
|
|
{
|
|
|
|
if (chan.SourceType == SOURCE_Actor) chan.Source = &sprite[SourceIndex];
|
|
|
|
else soundEngine->SetSource(&chan, SourceIndex);
|
|
|
|
}
|
|
|
|
arc.EndObject();
|
|
|
|
}
|
|
|
|
return arc;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// S_SerializeSounds
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void S_SerializeSounds(FSerializer& arc)
|
|
|
|
{
|
|
|
|
FSoundChan* chan;
|
|
|
|
|
|
|
|
GSnd->Sync(true);
|
|
|
|
|
|
|
|
if (arc.isWriting())
|
|
|
|
{
|
|
|
|
// Count channels and accumulate them so we can store them in
|
|
|
|
// reverse order. That way, they will be in the same order when
|
|
|
|
// reloaded later as they are now.
|
|
|
|
TArray<FSoundChan*> chans = soundEngine->AllActiveChannels();
|
|
|
|
|
|
|
|
if (chans.Size() > 0 && arc.BeginArray("sounds"))
|
|
|
|
{
|
|
|
|
for (unsigned int i = chans.Size(); i-- != 0; )
|
|
|
|
{
|
|
|
|
// Replace start time with sample position.
|
|
|
|
uint64_t start = chans[i]->StartTime;
|
|
|
|
chans[i]->StartTime = GSnd ? GSnd->GetPosition(chans[i]) : 0;
|
|
|
|
arc(nullptr, *chans[i]);
|
|
|
|
chans[i]->StartTime = start;
|
|
|
|
}
|
|
|
|
arc.EndArray();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned int count;
|
|
|
|
|
|
|
|
soundEngine->StopAllChannels();
|
|
|
|
if (arc.BeginArray("sounds"))
|
|
|
|
{
|
|
|
|
count = arc.ArraySize();
|
|
|
|
for (unsigned int i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
chan = (FSoundChan*)soundEngine->GetChannel(nullptr);
|
|
|
|
arc(nullptr, *chan);
|
|
|
|
// Sounds always start out evicted when restored from a save.
|
|
|
|
chan->ChanFlags |= CHANF_EVICTED | CHANF_ABSTIME;
|
|
|
|
}
|
|
|
|
arc.EndArray();
|
|
|
|
}
|
2020-08-25 23:19:21 +00:00
|
|
|
// Build runs at 120 fps, we need to allow a small delay here.
|
|
|
|
soundEngine->SetRestartTime(I_GetBuildTime() + 6);
|
2020-04-12 06:09:38 +00:00
|
|
|
}
|
|
|
|
GSnd->Sync(false);
|
|
|
|
GSnd->UpdateSounds();
|
|
|
|
}
|
|
|
|
|