From 69e018d0c4f1865937619250e80abae62e6314c4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 29 Feb 2020 12:33:35 +0100 Subject: [PATCH] - 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. --- source/common/savegamehelp.cpp | 2 + source/common/sound/s_sound.cpp | 106 +++++++++++++++++++++++++- source/common/sound/s_soundinternal.h | 6 ++ source/exhumed/src/sound.cpp | 10 +-- source/sw/src/game.cpp | 10 ++- source/sw/src/sounds.cpp | 17 +++++ 6 files changed, 141 insertions(+), 10 deletions(-) diff --git a/source/common/savegamehelp.cpp b/source/common/savegamehelp.cpp index bf4ca9141..ecff9b139 100644 --- a/source/common/savegamehelp.cpp +++ b/source/common/savegamehelp.cpp @@ -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); } //============================================================================= diff --git a/source/common/sound/s_sound.cpp b/source/common/sound/s_sound.cpp index 44b1791ee..95a997118 100644 --- a/source/common/sound/s_sound.cpp +++ b/source/common/sound/s_sound.cpp @@ -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 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 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(); +} + diff --git a/source/common/sound/s_soundinternal.h b/source/common/sound/s_soundinternal.h index b7df05e2f..f2dfd909c 100644 --- a/source/common/sound/s_soundinternal.h +++ b/source/common/sound/s_soundinternal.h @@ -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); diff --git a/source/exhumed/src/sound.cpp b/source/exhumed/src/sound.cpp index f732fae69..f70098acf 100644 --- a/source/exhumed/src/sound.cpp +++ b/source/exhumed/src/sound.cpp @@ -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; diff --git a/source/sw/src/game.cpp b/source/sw/src/game.cpp index 5cda617d8..19a29cc27 100644 --- a/source/sw/src/game.cpp +++ b/source/sw/src/game.cpp @@ -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(); diff --git a/source/sw/src/sounds.cpp b/source/sw/src/sounds.cpp index 0e7087ff5..c0640ca38 100644 --- a/source/sw/src/sounds.cpp +++ b/source/sw/src/sounds.cpp @@ -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; + } + }; //==========================================================================