- added serialization of playing sounds.

For ambient sounds that can be cleanly restarted a new 'transient' flag was added so that these do not get written out.
Tested on SW so far, other games yet to do.
This commit is contained in:
Christoph Oelckers 2020-02-29 12:33:35 +01:00
parent da6c7ddab5
commit 69e018d0c4
6 changed files with 141 additions and 10 deletions

View file

@ -50,6 +50,7 @@
#include "serializer.h"
#include "version.h"
#include "z_music.h"
#include "s_soundinternal.h"
static CompositeSavegameWriter savewriter;
static FResourceFile *savereader;
@ -70,6 +71,7 @@ static void SerializeSession(FSerializer& arc)
SECRET_Serialize(arc);
Mus_Serialize(arc);
quoteMgr.Serialize(arc);
S_SerializeSounds(arc);
}
//=============================================================================

View file

@ -44,6 +44,8 @@
#include "filesystem.h"
#include "cmdlib.h"
#include "gamecontrol.h"
#include "serializer.h"
#include "build.h"
enum
@ -280,7 +282,7 @@ TArray<FSoundChan*> SoundEngine::AllActiveChannels()
// If the sound is forgettable, this is as good a time as
// any to forget about it. And if it's a UI sound, it shouldn't
// be stored in the savegame.
if (!(chan->ChanFlags & (CHANF_FORGETTABLE | CHANF_UI)))
if (!(chan->ChanFlags & (CHANF_FORGETTABLE | CHANF_UI | CHANF_TRANSIENT)))
{
chans.Push(chan);
}
@ -1764,3 +1766,105 @@ int S_LookupSound(const char* fn)
}
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();
}
// totalclock runs on 120 fps, we need to allow a small delay here.
soundEngine->SetRestartTime((int)totalclock + 6);
}
GSnd->Sync(false);
GSnd->UpdateSounds();
}

View file

@ -275,6 +275,10 @@ public:
}
void EvictAllChannels();
// For handling special sound types. Source types None, Unattached and Actor never get here.
virtual int SoundSourceIndex(FSoundChan* chan) { return 0; }
virtual void SetSource(FSoundChan* chan, int index) {}
void StopChannel(FSoundChan* chan);
sfxinfo_t* LoadSound(sfxinfo_t* sfx);
@ -451,3 +455,5 @@ inline int S_FindSound(const char* name)
}
int S_LookupSound(const char* fn);
class FSerializer;
void S_SerializeSounds(FSerializer& arc);

View file

@ -258,7 +258,7 @@ void BendAmbientSound(void)
{
soundEngine->EnumerateChannels([](FSoundChan* chan)
{
if (chan->SourceType == SOURCE_Ambient)
if (chan->SourceType == SOURCE_Ambient && chan->Source == &amb)
{
soundEngine->SetPitch(chan, (nDronePitch + 11800) / 11025.f);
}
@ -346,7 +346,7 @@ void StartSwirly(int nActiveSound)
nVolume = 220;
soundEngine->StopSound(SOURCE_Swirly, &swirly, -1);
soundEngine->StartSound(SOURCE_Swirly, &swirly, nullptr, CHAN_BODY, 0, StaticSound[kSoundMana1]+1, nVolume / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f);
soundEngine->StartSound(SOURCE_Swirly, &swirly, nullptr, CHAN_BODY, CHANF_TRANSIENT, StaticSound[kSoundMana1]+1, nVolume / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f);
}
//==========================================================================
@ -396,7 +396,7 @@ void SoundBigEntrance(void)
short nPitch = i * 512 - 1200;
//pASound->snd_pitch = nPitch;
soundEngine->StopSound(SOURCE_EXBoss, &fakesources[i], -1);
soundEngine->StartSound(SOURCE_EXBoss, &fakesources[i], nullptr, CHAN_BODY, 0, StaticSound[kSoundTorchOn]+1, 200 / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f);
soundEngine->StartSound(SOURCE_EXBoss, &fakesources[i], nullptr, CHAN_BODY, CHANF_TRANSIENT, StaticSound[kSoundTorchOn]+1, 200 / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f);
}
}
@ -611,7 +611,7 @@ void CheckAmbience(short nSector)
{
vec3_t v = { pWall->x, pWall->y, sector[nSector2].floorz };
amb = GetSoundPos(&v);
soundEngine->StartSound(SOURCE_Ambient, &amb, nullptr, CHAN_BODY, CHANF_NONE, SectSound[nSector] + 1, 1.f, ATTN_NORM);
soundEngine->StartSound(SOURCE_Ambient, &amb, nullptr, CHAN_BODY, CHANF_TRANSIENT, SectSound[nSector] + 1, 1.f, ATTN_NORM);
return;
}
soundEngine->EnumerateChannels([=](FSoundChan* chan)
@ -685,7 +685,7 @@ void UpdateCreepySounds()
GetSpriteSoundPitch(&nVolume, &nPitch);
soundEngine->StopSound(SOURCE_Ambient, &creepy, CHAN_BODY);
soundEngine->StartSound(SOURCE_Ambient, &creepy, nullptr, CHAN_BODY, CHANF_NONE, vsi + 1, nVolume / 255.f, ATTN_NONE, nullptr, (11025 + nPitch) / 11025.f);
soundEngine->StartSound(SOURCE_Ambient, &creepy, nullptr, CHAN_BODY, CHANF_TRANSIENT, vsi + 1, nVolume / 255.f, ATTN_NONE, nullptr, (11025 + nPitch) / 11025.f);
}
}
nCreepyTimer = kCreepyCount;

View file

@ -1009,16 +1009,18 @@ void InitLevelGlobals2(void)
void
InitLevel(void)
{
if (LoadGameOutsideMoveLoop)
{
InitLevelGlobals();
return;
}
static int DemoNumber = 0;
Terminate3DSounds();
// A few IMPORTANT GLOBAL RESETS
InitLevelGlobals();
if (LoadGameOutsideMoveLoop)
{
return;
}
if (!DemoMode)
Mus_Stop();

View file

@ -431,6 +431,23 @@ public:
S_Rolloff.MinDistance = 0; // These are the default values, SW uses a few different rolloff settings.
S_Rolloff.MaxDistance = 1187;
}
int SoundSourceIndex(FSoundChan* chan) override
{
if (chan->SourceType == SOURCE_Player) return int(PLAYERp(chan->Source) - Player);
return 0;
}
void SetSource(FSoundChan* chan, int index) override
{
if (chan->SourceType == SOURCE_Player)
{
if (index < 0 || index >= MAX_SW_PLAYERS_REG) index = 0;
chan->Source = &Player[index];
}
else chan->Source = nullptr;
}
};
//==========================================================================