From b9582cc98e083416b39f388124d32f9cbaa3a6e2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 8 Dec 2019 21:22:53 +0100 Subject: [PATCH] - continued refactoring on sound code. The game independent part of the code has been mostly isolated. --- src/console/c_dispatch.h | 4 +- src/playsim/fragglescript/t_func.cpp | 2 +- src/playsim/p_acs.cpp | 22 +- src/r_data/sprites.cpp | 4 +- src/scripting/backend/codegen.cpp | 2 +- src/scripting/types.cpp | 2 +- src/scripting/vm/jit_move.cpp | 2 +- src/scripting/vm/vmexec.h | 2 +- src/serializer.cpp | 2 +- src/sound/backend/i_sound.cpp | 7 +- src/sound/backend/i_sound.h | 6 +- src/sound/backend/i_soundinternal.h | 1 + src/sound/backend/oalsound.cpp | 41 +- src/sound/s_advsound.cpp | 356 +------ src/sound/s_doomsound.cpp | 877 ++++++++++++++++-- src/sound/s_doomsound.h | 44 +- src/sound/s_sndseq.cpp | 4 +- src/sound/s_sound.cpp | 1223 +++++++++---------------- src/sound/s_sound.h | 25 +- src/sound/s_soundinternal.h | 243 +++-- src/utility/superfasthash.h | 5 + wadsrc/static/zscript/actors/actor.zs | 2 + 22 files changed, 1545 insertions(+), 1331 deletions(-) create mode 100644 src/utility/superfasthash.h diff --git a/src/console/c_dispatch.h b/src/console/c_dispatch.h index e21e977d1a..f9fbd31d53 100644 --- a/src/console/c_dispatch.h +++ b/src/console/c_dispatch.h @@ -208,9 +208,7 @@ extern bool ParsingKeyConf, UnsafeExecutionContext; void ResetButtonTriggers (); // Call ResetTriggers for all buttons void ResetButtonStates (); // Same as above, but also clear bDown -extern unsigned int MakeKey (const char *s); -extern unsigned int MakeKey (const char *s, size_t len); -extern unsigned int SuperFastHash (const char *data, size_t len); +#include "superfasthash.h" void execLogfile(const char *fn, bool append = false); diff --git a/src/playsim/fragglescript/t_func.cpp b/src/playsim/fragglescript/t_func.cpp index ee41d0d9c6..5d3917b31e 100644 --- a/src/playsim/fragglescript/t_func.cpp +++ b/src/playsim/fragglescript/t_func.cpp @@ -364,7 +364,7 @@ static FSoundID T_FindSound(const char * name) } int id = S_AddSound(name, buffer); - S_HashSounds(); + soundEngine->HashSounds(); return FSoundID(id); } diff --git a/src/playsim/p_acs.cpp b/src/playsim/p_acs.cpp index e28e8d72be..cbcfb0fc63 100644 --- a/src/playsim/p_acs.cpp +++ b/src/playsim/p_acs.cpp @@ -4387,11 +4387,11 @@ int DLevelScript::GetActorProperty (int tid, int property) return 0; } - case APROP_SeeSound: return GlobalACSStrings.AddString(actor->SeeSound); - case APROP_AttackSound: return GlobalACSStrings.AddString(actor->AttackSound); - case APROP_PainSound: return GlobalACSStrings.AddString(actor->PainSound); - case APROP_DeathSound: return GlobalACSStrings.AddString(actor->DeathSound); - case APROP_ActiveSound: return GlobalACSStrings.AddString(actor->ActiveSound); + case APROP_SeeSound: return GlobalACSStrings.AddString(S_GetSoundName(actor->SeeSound)); + case APROP_AttackSound: return GlobalACSStrings.AddString(S_GetSoundName(actor->AttackSound)); + case APROP_PainSound: return GlobalACSStrings.AddString(S_GetSoundName(actor->PainSound)); + case APROP_DeathSound: return GlobalACSStrings.AddString(S_GetSoundName(actor->DeathSound)); + case APROP_ActiveSound: return GlobalACSStrings.AddString(S_GetSoundName(actor->ActiveSound)); case APROP_Species: return GlobalACSStrings.AddString(actor->GetSpecies()); case APROP_NameTag: return GlobalACSStrings.AddString(actor->GetTag()); case APROP_StencilColor:return actor->fillcolor; @@ -4464,11 +4464,11 @@ int DLevelScript::CheckActorProperty (int tid, int property, int value) // Strings are covered by GetActorProperty, but they're fairly // heavy-duty, so make the check here. - case APROP_SeeSound: string = actor->SeeSound; break; - case APROP_AttackSound: string = actor->AttackSound; break; - case APROP_PainSound: string = actor->PainSound; break; - case APROP_DeathSound: string = actor->DeathSound; break; - case APROP_ActiveSound: string = actor->ActiveSound; break; + case APROP_SeeSound: string = S_GetSoundName(actor->SeeSound); break; + case APROP_AttackSound: string = S_GetSoundName(actor->AttackSound); break; + case APROP_PainSound: string = S_GetSoundName(actor->PainSound); break; + case APROP_DeathSound: string = S_GetSoundName(actor->DeathSound); break; + case APROP_ActiveSound: string = S_GetSoundName(actor->ActiveSound); break; case APROP_Species: string = actor->GetSpecies(); break; case APROP_NameTag: string = actor->GetTag(); break; case APROP_DamageType: string = actor->DamageType; break; @@ -5265,7 +5265,7 @@ int DLevelScript::SwapActorTeleFog(AActor *activator, int tid) } else if (rettype == TypeSound) { - retval = GlobalACSStrings.AddString(FSoundID(retval)); + retval = GlobalACSStrings.AddString(S_GetSoundName(FSoundID(retval))); } } else if (rettype == TypeFloat64) diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index eecb27dd20..2bab762776 100644 --- a/src/r_data/sprites.cpp +++ b/src/r_data/sprites.cpp @@ -731,7 +731,7 @@ void R_InitSkins (void) } else { - int sndref = S_FindSoundNoHash (key); + int sndref = soundEngine->FindSoundNoHash (key); if (sndref != 0) { S_AddPlayerSound (Skins[i].Name, Skins[i].gender, sndref, lump, true); @@ -911,7 +911,7 @@ void R_InitSkins (void) if (Skins.Size() > PlayerClasses.Size ()) { // The sound table may have changed, so rehash it. - S_HashSounds (); + soundEngine->HashSounds (); S_ShrinkPlayerSoundLists (); } } diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index ac5f681631..f5f178cfa8 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -1243,7 +1243,7 @@ FxExpression *FxStringCast::Resolve(FCompileContext &ctx) if (basex->isConstant()) { ExpVal constval = static_cast(basex)->GetValue(); - FxExpression *x = new FxConstant(S_sfx[constval.GetInt()].name, ScriptPosition); + FxExpression *x = new FxConstant(S_GetSoundName(constval.GetInt()), ScriptPosition); delete this; return x; } diff --git a/src/scripting/types.cpp b/src/scripting/types.cpp index 269de5cc96..aee0b3b0aa 100644 --- a/src/scripting/types.cpp +++ b/src/scripting/types.cpp @@ -1184,7 +1184,7 @@ PSound::PSound() void PSound::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - const char *cptr = *(const FSoundID *)addr; + const char *cptr = S_GetSoundName(*(const FSoundID *)addr); ar.StringPtr(key, cptr); } diff --git a/src/scripting/vm/jit_move.cpp b/src/scripting/vm/jit_move.cpp index ca584a326a..f9383eabab 100644 --- a/src/scripting/vm/jit_move.cpp +++ b/src/scripting/vm/jit_move.cpp @@ -51,7 +51,7 @@ static void CastN2S(FString *a, int b) { FName name = FName(ENamedName(b)); *a = static int CastS2Co(FString *b) { return V_GetColor(nullptr, *b); } static void CastCo2S(FString *a, int b) { PalEntry c(b); a->Format("%02x %02x %02x", c.r, c.g, c.b); } static int CastS2So(FString *b) { return FSoundID(*b); } -static void CastSo2S(FString *a, int b) { *a = S_sfx[b].name; } +static void CastSo2S(FString* a, int b) { *a = S_GetSoundName(b); } static void CastSID2S(FString *a, unsigned int b) { *a = (b >= sprites.Size()) ? "TNT1" : sprites[b].name; } static void CastTID2S(FString *a, int b) { auto tex = TexMan.GetTexture(*(FTextureID*)&b); *a = (tex == nullptr) ? "(null)" : tex->GetName().GetChars(); } diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index 1d3e6411f9..08a398f9e9 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -1837,7 +1837,7 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c case CAST_So2S: ASSERTS(a); ASSERTD(b); - reg.s[a] = S_sfx[reg.d[b]].name; + reg.s[a] = S_GetSoundName(reg.d[b]); break; case CAST_SID2S: diff --git a/src/serializer.cpp b/src/serializer.cpp index f6829d3de4..57ce752cbc 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -1663,7 +1663,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI if (!arc.w->inObject() || def == nullptr || sid != *def) { arc.WriteKey(key); - const char *sn = (const char*)sid; + const char *sn = S_GetSoundName(sid); if (sn != nullptr) arc.w->String(sn); else arc.w->Null(); } diff --git a/src/sound/backend/i_sound.cpp b/src/sound/backend/i_sound.cpp index 94b1a860e3..616120b98f 100644 --- a/src/sound/backend/i_sound.cpp +++ b/src/sound/backend/i_sound.cpp @@ -35,8 +35,6 @@ #include #include -#include "doomtype.h" - #include "oalsound.h" #include "i_module.h" @@ -52,6 +50,7 @@ #include "s_music.h" #include "zmusic/zmusic.h" + EXTERN_CVAR (Float, snd_sfxvolume) EXTERN_CVAR (Float, snd_musicvolume) CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -300,8 +299,8 @@ void I_InitSound () void I_CloseSound () { - // Free all loaded samples - S_UnloadAllSounds(); + // Free all loaded samples. Beware that the sound engine may already have been deleted. + if (soundEngine) soundEngine->UnloadAllSounds(); delete GSnd; GSnd = NULL; diff --git a/src/sound/backend/i_sound.h b/src/sound/backend/i_sound.h index eedcb18b22..83f5fc2c61 100644 --- a/src/sound/backend/i_sound.h +++ b/src/sound/backend/i_sound.h @@ -174,11 +174,7 @@ extern bool nosfx; extern bool nosound; void I_InitSound (); - -void S_ChannelEnded(FISoundChannel *schan); -void S_ChannelVirtualChanged(FISoundChannel *schan, bool is_virtual); -float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic); -FISoundChannel *S_GetChannel(void *syschan); +void I_CloseSound(); extern ReverbContainer *DefaultEnvironments[26]; diff --git a/src/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index ef9e24db3e..311ed22b0b 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -122,4 +122,5 @@ class SoundStream; + #endif diff --git a/src/sound/backend/oalsound.cpp b/src/sound/backend/oalsound.cpp index 55df4b9820..a16d42b41b 100644 --- a/src/sound/backend/oalsound.cpp +++ b/src/sound/backend/oalsound.cpp @@ -544,22 +544,7 @@ static size_t GetChannelCount(ChannelConfig chans) static float GetRolloff(const FRolloffInfo *rolloff, float distance) { - if(distance <= rolloff->MinDistance) - return 1.f; - // Logarithmic rolloff has no max distance where it goes silent. - if(rolloff->RolloffType == ROLLOFF_Log) - return rolloff->MinDistance / - (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); - if(distance >= rolloff->MaxDistance) - return 0.f; - - float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); - if(rolloff->RolloffType == ROLLOFF_Linear) - return volume; - - if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve.Size() > 0) - return S_SoundCurve[int(S_SoundCurve.Size() * (1.f - volume))] / 127.f; - return (powf(10.f, volume) - 1.f) / 9.f; + return soundEngine->GetRolloff(rolloff, distance); } ALCdevice *OpenALSoundRenderer::InitDevice() @@ -983,7 +968,7 @@ void OpenALSoundRenderer::SetSfxVolume(float volume) { SfxVolume = volume; - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel != NULL) @@ -1359,7 +1344,7 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) return; ALuint buffer = GET_PTRID(sfx.data); - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel) @@ -1495,7 +1480,7 @@ FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int FreeSfx.Pop(); FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + if(!chan) chan = soundEngine->GetChannel(MAKE_PTRID(source)); else chan->SysChannel = MAKE_PTRID(source); chan->Rolloff.RolloffType = ROLLOFF_Log; @@ -1706,7 +1691,7 @@ FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener FreeSfx.Pop(); FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + if(!chan) chan = soundEngine->GetChannel(MAKE_PTRID(source)); else chan->SysChannel = MAKE_PTRID(source); chan->Rolloff = *rolloff; @@ -1765,7 +1750,7 @@ void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) ALuint source = GET_PTRID(chan->SysChannel); // Release first, so it can be properly marked as evicted if it's being killed - S_ChannelEnded(chan); + soundEngine->ChannelEnded(chan); ALint state = AL_INITIAL; alGetSourcei(source, AL_SOURCE_STATE, &state); @@ -1789,7 +1774,7 @@ void OpenALSoundRenderer::ForceStopChannel(FISoundChannel *chan) ALuint source = GET_PTRID(chan->SysChannel); if(!source) return; - S_ChannelEnded(chan); + soundEngine->ChannelEnded(chan); FreeSource(source); } @@ -2009,7 +1994,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); // Apply the updated filters on the sources - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2022,7 +2007,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) } } - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2047,7 +2032,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2060,7 +2045,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) } } - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2236,7 +2221,7 @@ void OpenALSoundRenderer::PurgeStoppedSources() if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) continue; - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) @@ -2358,7 +2343,7 @@ void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) FSoundChan *OpenALSoundRenderer::FindLowestChannel() { - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); FSoundChan *lowest = NULL; while(schan) { diff --git a/src/sound/s_advsound.cpp b/src/sound/s_advsound.cpp index 19e55ef131..113cb36d73 100644 --- a/src/sound/s_advsound.cpp +++ b/src/sound/s_advsound.cpp @@ -61,12 +61,6 @@ // TYPES ------------------------------------------------------------------- -struct FRandomSoundList -{ - TArray Choices; - uint32_t Owner = 0; -}; - struct FPlayerClassLookup { FString Name; @@ -200,11 +194,9 @@ static int S_AddSound (const char *logicalname, int lumpnum, FScanner *sc=NULL); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- -extern int sfx_empty; - // PUBLIC DATA DEFINITIONS ------------------------------------------------- -TArray S_sfx (128); +//TArray S_sfx (128); TMap HexenMusic; // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -239,7 +231,6 @@ static const char *SICommandStrings[] = NULL }; -static TArray S_rnd; static FMusicVolume *MusicVolumes; static TArray SavedPlayerSounds; @@ -255,8 +246,6 @@ static int DefPlayerClass; static uint8_t CurrentPitchMask; -static FRandom pr_randsound ("RandSound"); - // CODE -------------------------------------------------------------------- //========================================================================== @@ -281,36 +270,6 @@ float S_GetMusicVolume (const char *music) return 1.f; } -//========================================================================== - -// -// S_HashSounds -// -// Fills in the next and index fields of S_sfx to form a working hash table. -//========================================================================== - -void S_HashSounds () -{ - unsigned int i; - unsigned int j; - unsigned int size; - - S_sfx.ShrinkToFit (); - size = S_sfx.Size (); - - // Mark all buckets as empty - for (i = 0; i < size; i++) - S_sfx[i].index = 0; - - // Now set up the chains - for (i = 1; i < size; i++) - { - j = MakeKey (S_sfx[i].name) % size; - S_sfx[i].next = S_sfx[j].index; - S_sfx[j].index = i; - } -} - //========================================================================== // // S_CheckIntegrity @@ -320,6 +279,7 @@ void S_HashSounds () static bool S_CheckSound(sfxinfo_t *startsfx, sfxinfo_t *sfx, TArray &chain) { + auto &S_sfx = soundEngine->GetSounds(); sfxinfo_t *me = sfx; bool success = true; unsigned siz = chain.Size(); @@ -339,7 +299,7 @@ static bool S_CheckSound(sfxinfo_t *startsfx, sfxinfo_t *sfx, TArraybRandomHeader) { - const FRandomSoundList *list = &S_rnd[me->link]; + const FRandomSoundList* list = soundEngine->ResolveRandomSound(me); for (unsigned i = 0; i < list->Choices.Size(); ++i) { auto rsfx = &S_sfx[list->Choices[i]]; @@ -385,6 +345,7 @@ void S_CheckIntegrity() TArray chain; TArray broken; + auto &S_sfx = soundEngine->GetSounds(); broken.Resize(S_sfx.Size()); memset(&broken[0], 0, sizeof(bool)*S_sfx.Size()); for (unsigned i = 0; i < S_sfx.Size(); i++) @@ -404,34 +365,18 @@ void S_CheckIntegrity() } } -//========================================================================== -// -// S_PickReplacement -// -// Picks a replacement sound from the associated random list. If this sound -// is not the head of a random list, then the sound passed is returned. -//========================================================================== - -int S_PickReplacement(int refid) -{ - while (S_sfx[refid].bRandomHeader) - { - const FRandomSoundList *list = &S_rnd[S_sfx[refid].link]; - refid = list->Choices[pr_randsound(list->Choices.Size())]; - } - return refid; -} - //========================================================================== // // S_GetSoundMSLength // // Returns duration of sound +// This cannot be made a sound engine function due to the player sounds. // //========================================================================== unsigned int S_GetMSLength(FSoundID sound) { + auto &S_sfx = soundEngine->GetSounds(); if ((unsigned int)sound >= S_sfx.Size()) { return 0; @@ -453,7 +398,7 @@ unsigned int S_GetMSLength(FSoundID sound) // I think the longest one makes more sense. int length = 0; - const FRandomSoundList *list = &S_rnd[sfx->link]; + const FRandomSoundList* list = soundEngine->ResolveRandomSound(sfx); for (auto &me : list->Choices) { @@ -469,7 +414,7 @@ unsigned int S_GetMSLength(FSoundID sound) } } - sfx = S_LoadSound(sfx); + sfx = soundEngine->LoadSound(sfx, nullptr); if (sfx != NULL) return GSnd->GetMSLength(sfx->data); else return 0; } @@ -481,157 +426,6 @@ DEFINE_ACTION_FUNCTION(DObject,S_GetLength) ACTION_RETURN_FLOAT(S_GetMSLength(sound_id)/1000.0); } -//========================================================================== -// -// S_CacheRandomSound -// -// Loads all sounds a random sound might play. -// -//========================================================================== - -void S_CacheRandomSound (sfxinfo_t *sfx) -{ - if (sfx->bRandomHeader) - { - const FRandomSoundList *list = &S_rnd[sfx->link]; - for (unsigned i = 0; i < list->Choices.Size(); ++i) - { - sfx = &S_sfx[list->Choices[i]]; - sfx->bUsed = true; - S_CacheSound (&S_sfx[list->Choices[i]]); - } - } -} - -//========================================================================== -// -// S_FindSound -// -// Given a logical name, find the sound's index in S_sfx. -//========================================================================== - -int S_FindSound (const char *logicalname) -{ - int i; - - if (logicalname != NULL) - { - i = S_sfx[MakeKey (logicalname) % S_sfx.Size ()].index; - - while ((i != 0) && stricmp (S_sfx[i].name, logicalname)) - i = S_sfx[i].next; - - return i; - } - else - { - return 0; - } -} - -//========================================================================== -// -// S_FindSoundNoHash -// -// Given a logical name, find the sound's index in S_sfx without -// using the hash table. -//========================================================================== - -int S_FindSoundNoHash (const char *logicalname) -{ - unsigned int i; - - for (i = 1; i < S_sfx.Size (); i++) - { - if (stricmp (S_sfx[i].name, logicalname) == 0) - { - return i; - } - } - return 0; -} - -//========================================================================== -// -// S_FindSoundByLump -// -// Given a sound lump, find the sound's index in S_sfx. -//========================================================================== - -int S_FindSoundByLump (int lump) -{ - if (lump != -1) - { - unsigned int i; - - for (i = 1; i < S_sfx.Size (); i++) - if (S_sfx[i].lumpnum == lump) - return i; - } - return 0; -} - -//========================================================================== -// -// S_AddSoundLump -// -// Adds a new sound mapping to S_sfx. -//========================================================================== - -int S_AddSoundLump (const char *logicalname, int lump) -{ - sfxinfo_t newsfx; - - newsfx.data.Clear(); - newsfx.data3d.Clear(); - newsfx.name = logicalname; - newsfx.lumpnum = lump; - newsfx.next = 0; - newsfx.index = 0; - newsfx.Volume = 1; - newsfx.Attenuation = 1; - newsfx.PitchMask = CurrentPitchMask; - newsfx.NearLimit = 2; - newsfx.LimitRange = 256*256; - newsfx.bRandomHeader = false; - newsfx.bPlayerReserve = false; - newsfx.bLoadRAW = false; - newsfx.bPlayerCompat = false; - newsfx.b16bit = false; - newsfx.bUsed = false; - newsfx.bSingular = false; - newsfx.bTentative = false; - newsfx.bPlayerSilent = false; - newsfx.RawRate = 0; - newsfx.link = sfxinfo_t::NO_LINK; - newsfx.Rolloff.RolloffType = ROLLOFF_Doom; - newsfx.Rolloff.MinDistance = 0; - newsfx.Rolloff.MaxDistance = 0; - newsfx.LoopStart = -1; - - return (int)S_sfx.Push (newsfx); -} - -//========================================================================== -// -// S_FindSoundTentative -// -// Given a logical name, find the sound's index in S_sfx without -// using the hash table. If it does not exist, a new sound without -// an associated lump is created. -//========================================================================== - -int S_FindSoundTentative (const char *name) -{ - int id = S_FindSoundNoHash (name); - if (id == 0) - { - id = S_AddSoundLump (name, -1); - S_sfx[id].bTentative = true; - } - return id; -} - //========================================================================== // // S_AddSound @@ -648,9 +442,10 @@ int S_AddSound (const char *logicalname, const char *lumpname, FScanner *sc) static int S_AddSound (const char *logicalname, int lumpnum, FScanner *sc) { + auto &S_sfx = soundEngine->GetSounds(); int sfxid; - sfxid = S_FindSoundNoHash (logicalname); + sfxid = soundEngine->FindSoundNoHash (logicalname); if (sfxid > 0) { // If the sound has already been defined, change the old definition @@ -674,7 +469,7 @@ static int S_AddSound (const char *logicalname, int lumpnum, FScanner *sc) } if (sfx->bRandomHeader) { - FRandomSoundList *rnd = &S_rnd[sfx->link]; + FRandomSoundList* rnd = soundEngine->ResolveRandomSound(sfx); rnd->Choices.Reset(); rnd->Owner = 0; } @@ -691,7 +486,7 @@ static int S_AddSound (const char *logicalname, int lumpnum, FScanner *sc) } else { // Otherwise, create a new definition. - sfxid = S_AddSoundLump (logicalname, lumpnum); + sfxid = soundEngine->AddSoundLump (logicalname, lumpnum, CurrentPitchMask); } return sfxid; @@ -719,6 +514,7 @@ int S_AddPlayerSound (const char *pclass, int gender, int refid, int S_AddPlayerSound (const char *pclass, int gender, int refid, int lumpnum, bool fromskin) { + auto &S_sfx = soundEngine->GetSounds(); FString fakename; int id; @@ -728,7 +524,7 @@ int S_AddPlayerSound (const char *pclass, int gender, int refid, int lumpnum, bo fakename += '"'; fakename += S_sfx[refid].name; - id = S_AddSoundLump (fakename, lumpnum); + id = soundEngine->AddSoundLump (fakename, lumpnum, CurrentPitchMask); int classnum = S_AddPlayerClass (pclass); int soundlist = S_AddPlayerGender (classnum, gender); @@ -752,6 +548,7 @@ int S_AddPlayerSoundExisting (const char *pclass, int gender, int refid, int classnum = S_AddPlayerClass (pclass); int soundlist = S_AddPlayerGender (classnum, gender); + auto &S_sfx = soundEngine->GetSounds(); PlayerSounds[soundlist].AddSound (S_sfx[refid].link, aliasto); if (fromskin) S_SavePlayerSound(pclass, gender, refid, aliasto, true); @@ -928,7 +725,7 @@ void FPlayerSoundHashTable::MarkUsed() { for (Entry *probe = Buckets[i]; probe != NULL; probe = probe->Next) { - S_sfx[probe->SfxID].bUsed = true; + soundEngine->MarkUsed(probe->SfxID); } } } @@ -944,8 +741,9 @@ void FPlayerSoundHashTable::MarkUsed() void S_ClearSoundData() { - S_StopAllChannels(); - S_UnloadAllSounds(); + soundEngine->StopAllChannels(); + soundEngine->UnloadAllSounds(); + auto &S_sfx = soundEngine->GetSounds(); S_sfx.Clear(); Ambients.Clear(); while (MusicVolumes != NULL) @@ -954,7 +752,7 @@ void S_ClearSoundData() MusicVolumes = me->Next; M_Free(me); } - S_rnd.Clear(); + soundEngine->ClearRandoms(); NumPlayerReserves = 0; PlayerClassesIsSorted = false; @@ -977,6 +775,7 @@ void S_ClearSoundData() void S_ParseSndInfo (bool redefine) { + auto &S_sfx = soundEngine->GetSounds(); int lump; if (!redefine) SavedPlayerSounds.Clear(); // clear skin sounds only for initial parsing. @@ -1005,13 +804,7 @@ void S_ParseSndInfo (bool redefine) } } S_RestorePlayerSounds(); - S_HashSounds (); - S_sfx.ShrinkToFit (); - - if (S_rnd.Size() > 0) - { - S_rnd.ShrinkToFit (); - } + soundEngine->HashSounds (); S_ShrinkPlayerSoundLists (); @@ -1028,13 +821,7 @@ void S_ParseSndInfo (bool redefine) void S_AddLocalSndInfo(int lump) { S_AddSNDINFO(lump); - S_HashSounds (); - S_sfx.ShrinkToFit (); - - if (S_rnd.Size() > 0) - { - S_rnd.ShrinkToFit (); - } + soundEngine->HashSounds (); S_ShrinkPlayerSoundLists (); S_CheckIntegrity(); @@ -1050,6 +837,7 @@ void S_AddLocalSndInfo(int lump) static void S_AddSNDINFO (int lump) { + auto &S_sfx = soundEngine->GetSounds(); bool skipToEndIf; TArray list; @@ -1087,7 +875,7 @@ static void S_AddSNDINFO (int lump) ambient->sound = 0; sc.MustGetString (); - ambient->sound = FSoundID(S_FindSoundTentative(sc.String)); + ambient->sound = FSoundID(soundEngine->FindSoundTentative(sc.String)); ambient->attenuation = 0; sc.MustGetString (); @@ -1205,7 +993,7 @@ static void S_AddSNDINFO (int lump) int gender, refid, targid; S_ParsePlayerSoundCommon (sc, pclass, gender, refid); - targid = S_FindSoundNoHash (sc.String); + targid = soundEngine->FindSoundNoHash (sc.String); if (!S_sfx[targid].bPlayerReserve) { sc.ScriptError ("%s is not a player sound", sc.String); @@ -1235,7 +1023,7 @@ static void S_AddSNDINFO (int lump) int soundnum; S_ParsePlayerSoundCommon (sc, pclass, gender, refid); - soundnum = S_FindSoundTentative (sc.String); + soundnum = soundEngine->FindSoundTentative (sc.String); S_AddPlayerSoundExisting (pclass, gender, refid, soundnum); } break; @@ -1251,7 +1039,7 @@ static void S_AddSNDINFO (int lump) { sfxfrom = S_sfx[sfxfrom].link; } - S_sfx[sfxfrom].link = S_FindSoundTentative (sc.String); + S_sfx[sfxfrom].link = soundEngine->FindSoundTentative (sc.String); S_sfx[sfxfrom].NearLimit = -1; // Aliases must use the original sound's limit. } break; @@ -1261,7 +1049,7 @@ static void S_AddSNDINFO (int lump) int sfx; sc.MustGetString (); - sfx = S_FindSoundTentative (sc.String); + sfx = soundEngine->FindSoundTentative (sc.String); sc.MustGetNumber (); S_sfx[sfx].NearLimit = MIN(MAX(sc.Number, 0), 255); if (sc.CheckFloat()) @@ -1276,7 +1064,7 @@ static void S_AddSNDINFO (int lump) int sfx; sc.MustGetString (); - sfx = S_FindSoundTentative (sc.String); + sfx = soundEngine->FindSoundTentative (sc.String); S_sfx[sfx].bSingular = true; } break; @@ -1286,7 +1074,7 @@ static void S_AddSNDINFO (int lump) int sfx; sc.MustGetString (); - sfx = S_FindSoundTentative (sc.String); + sfx = soundEngine->FindSoundTentative (sc.String); sc.MustGetNumber (); S_sfx[sfx].PitchMask = (1 << clamp (sc.Number, 0, 7)) - 1; } @@ -1303,7 +1091,7 @@ static void S_AddSNDINFO (int lump) int sfx; sc.MustGetString(); - sfx = S_FindSoundTentative(sc.String); + sfx = soundEngine->FindSoundTentative(sc.String); sc.MustGetFloat(); S_sfx[sfx].Volume = (float)sc.Float; } @@ -1314,7 +1102,7 @@ static void S_AddSNDINFO (int lump) int sfx; sc.MustGetString(); - sfx = S_FindSoundTentative(sc.String); + sfx = soundEngine->FindSoundTentative(sc.String); sc.MustGetFloat(); S_sfx[sfx].Attenuation = (float)sc.Float; } @@ -1331,11 +1119,11 @@ static void S_AddSNDINFO (int lump) if (sc.Compare("*")) { sfx = -1; - rolloff = &S_Rolloff; + rolloff = &soundEngine->GlobalRolloff(); } else { - sfx = S_FindSoundTentative(sc.String); + sfx = soundEngine->FindSoundTentative(sc.String); rolloff = &S_sfx[sfx].Rolloff; } type = ROLLOFF_Doom; @@ -1376,7 +1164,7 @@ static void S_AddSNDINFO (int lump) sc.MustGetStringName ("{"); while (sc.GetString () && !sc.Compare ("}")) { - uint32_t sfxto = S_FindSoundTentative (sc.String); + uint32_t sfxto = soundEngine->FindSoundTentative (sc.String); if (sfxto == random.Owner) { Printf("Definition of random sound '%s' refers to itself recursively.\n", sc.String); @@ -1391,13 +1179,7 @@ static void S_AddSNDINFO (int lump) } else if (list.Size() > 1) { // Only add non-empty random lists - auto index = S_rnd.Reserve(1); - auto &random = S_rnd.Last(); - random.Choices = std::move(list); - random.Owner = Owner; - S_sfx[Owner].link = index; - S_sfx[Owner].bRandomHeader = true; - S_sfx[Owner].NearLimit = -1; + soundEngine->AddRandomSound(Owner, list); } } break; @@ -1513,6 +1295,7 @@ static void S_AddBloodSFX (int lumpnum) if (rawlump != -1) { + auto &S_sfx = soundEngine->GetSounds(); const char *name = Wads.GetLumpFullName(lumpnum); sfxnum = S_AddSound(name, rawlump); if (sfx->Format < 5 || sfx->Format > 12) @@ -1572,7 +1355,8 @@ static void S_ParsePlayerSoundCommon (FScanner &sc, FString &pclass, int &gender sc.MustGetString (); gender = D_GenderToInt (sc.String); sc.MustGetString (); - refid = S_FindSoundNoHash (sc.String); + refid = soundEngine->FindSoundNoHash (sc.String); + auto &S_sfx = soundEngine->GetSounds(); if (refid != 0 && !S_sfx[refid].bPlayerReserve && !S_sfx[refid].bTentative) { sc.ScriptError ("%s has already been used for a non-player sound.", sc.String); @@ -1734,6 +1518,7 @@ int S_LookupPlayerSound (const char *pclass, int gender, const char *name) int S_LookupPlayerSound (const char *pclass, int gender, FSoundID refid) { + auto &S_sfx = soundEngine->GetSounds(); if (!S_sfx[refid].bPlayerReserve) { // Not a player sound, so just return this sound return refid; @@ -1744,6 +1529,7 @@ int S_LookupPlayerSound (const char *pclass, int gender, FSoundID refid) static int S_LookupPlayerSound (int classidx, int gender, FSoundID refid) { + auto &S_sfx = soundEngine->GetSounds(); int ingender = gender; if (classidx == -1) @@ -1847,6 +1633,7 @@ bool S_AreSoundsEquivalent (AActor *actor, int id1, int id2) { sfxinfo_t *sfx; + auto &S_sfx = soundEngine->GetSounds(); if (id1 == id2) { return true; @@ -1998,52 +1785,6 @@ void S_MarkPlayerSounds (AActor *player) } } -//========================================================================== -// -// CCMD soundlist -// -//========================================================================== - -CCMD (soundlist) -{ - char lumpname[9]; - unsigned int i; - - lumpname[8] = 0; - for (i = 0; i < S_sfx.Size (); i++) - { - const sfxinfo_t *sfx = &S_sfx[i]; - if (sfx->bRandomHeader) - { - Printf ("%3d. %s -> #%d {", i, sfx->name.GetChars(), sfx->link); - const FRandomSoundList *list = &S_rnd[sfx->link]; - for (auto &me : list->Choices) - { - Printf (" %s ", S_sfx[me].name.GetChars()); - } - Printf ("}\n"); - } - else if (sfx->bPlayerReserve) - { - Printf ("%3d. %s <>\n", i, sfx->name.GetChars(), sfx->link); - } - else if (S_sfx[i].lumpnum != -1) - { - Wads.GetLumpName (lumpname, sfx->lumpnum); - Printf ("%3d. %s (%s)\n", i, sfx->name.GetChars(), lumpname); - } - else if (S_sfx[i].link != sfxinfo_t::NO_LINK) - { - Printf ("%3d. %s -> %s\n", i, sfx->name.GetChars(), S_sfx[sfx->link].name.GetChars()); - } - else - { - Printf ("%3d. %s **not present**\n", i, sfx->name.GetChars()); - } - Printf(" PitchMask = %d\n", sfx->PitchMask); - } -} - //========================================================================== // // CCMD soundlinks @@ -2052,6 +1793,7 @@ CCMD (soundlist) CCMD (soundlinks) { + auto &S_sfx = soundEngine->GetSounds(); unsigned int i; for (i = 0; i < S_sfx.Size (); i++) @@ -2075,6 +1817,7 @@ CCMD (soundlinks) CCMD (playersounds) { + auto &S_sfx = soundEngine->GetSounds(); const char *reserveNames[256]; unsigned int i; int j, k, l; @@ -2119,7 +1862,7 @@ DEFINE_ACTION_FUNCTION(AAmbientSound, MarkAmbientSounds) FAmbientSound *ambient = Ambients.CheckKey(self->args[0]); if (ambient != NULL) { - ambient->sound.MarkUsed(); + soundEngine->MarkUsed(ambient->sound); } return 0; } @@ -2251,13 +1994,12 @@ DEFINE_ACTION_FUNCTION(AAmbientSound, Activate) { if ((amb->type & 3) == 0 && amb->periodmin == 0) { - int sndnum = S_FindSound(amb->sound); - if (sndnum == 0) + if (amb->sound == 0) { self->Destroy (); return 0; } - amb->periodmin = ::Scale(S_GetMSLength(sndnum), TICRATE, 1000); + amb->periodmin = ::Scale(S_GetMSLength(amb->sound), TICRATE, 1000); } self->special1 = 0; @@ -2343,7 +2085,7 @@ DEFINE_ACTION_FUNCTION(DObject, MarkSound) { PARAM_PROLOGUE; PARAM_SOUND(sound_id); - sound_id.MarkUsed(); + soundEngine->MarkUsed(sound_id); return 0; } diff --git a/src/sound/s_doomsound.cpp b/src/sound/s_doomsound.cpp index 865ac15bc0..a9a5eb54cd 100644 --- a/src/sound/s_doomsound.cpp +++ b/src/sound/s_doomsound.cpp @@ -66,11 +66,209 @@ #include "g_game.h" #include "s_music.h" +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + + +FBoolCVar noisedebug("noise", false, 0); // [RH] Print sound debugging info? +CUSTOM_CVAR(Int, snd_channels, 128, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // number of channels available +{ + if (self < 64) self = 64; +} +CVAR(Bool, snd_waterreverb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + + +static FString LastLocalSndInfo; +static FString LastLocalSndSeq; +void S_AddLocalSndInfo(int lump); + +class DoomSoundEngine : public SoundEngine +{ + // client specific parts of the sound engine go in this class. + void CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FVector3* pos, FVector3* vel) override; + bool ValidatePosVel(int sourcetype, const void* source, const FVector3& pos, const FVector3& vel); + TArray ReadSound(int lumpnum); + int PickReplacement(int refid); + +public: + DoomSoundEngine() = default; + void NoiseDebug(void); + void PrintSoundList(); +}; + +//========================================================================== +// +// S_Init +// +// Initializes sound stuff, including volume. Sets channels, SFX and +// music volume, allocates channel buffer, and sets S_sfx lookup. +//========================================================================== + +void S_Init() +{ + // Must be up before I_InitSound. + if (!soundEngine) + { + soundEngine = new DoomSoundEngine; + } + + I_InitSound(); + + // Heretic and Hexen have sound curve lookup tables. Doom does not. + int curvelump = Wads.CheckNumForName("SNDCURVE"); + TArray curve; + if (curvelump >= 0) + { + curve = Wads.ReadLumpIntoArray(curvelump); + } + soundEngine->Init(curve); +} + +//========================================================================== +// +// S_Shutdown +// +//========================================================================== + +void S_Shutdown() +{ + S_StopMusic(true); + + for (auto Level : AllLevels()) + { + SN_StopAllSequences(Level); + } + + if (soundEngine) + { + soundEngine->Shutdown(); + delete soundEngine; + soundEngine = nullptr; + } + + mus_playing.LastSong = ""; // If this isn't reset here, the song would attempt to resume at the most inpopportune time... + + if (GSnd != nullptr) + { + I_CloseSound(); + } +} + + +//========================================================================== +// +// S_Start +// +// Per level startup code. Kills playing sounds at start of level +// and starts new music. +//========================================================================== + +void S_Start() +{ + if (GSnd && soundEngine) + { + // kill all playing sounds at start of level (trust me - a good idea) + soundEngine->StopAllChannels(); + + // Check for local sound definitions. Only reload if they differ + // from the previous ones. + FString LocalSndInfo; + FString LocalSndSeq; + + // To be certain better check whether level is valid! + if (primaryLevel->info) + { + LocalSndInfo = primaryLevel->info->SoundInfo; + LocalSndSeq = primaryLevel->info->SndSeq; + } + + bool parse_ss = false; + + // This level uses a different local SNDINFO + if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !primaryLevel->info) + { + soundEngine->UnloadAllSounds(); + + // Parse the global SNDINFO + S_ParseSndInfo(true); + + if (LocalSndInfo.IsNotEmpty()) + { + // Now parse the local SNDINFO + int j = Wads.CheckNumForFullName(LocalSndInfo, true); + if (j >= 0) S_AddLocalSndInfo(j); + } + + // Also reload the SNDSEQ if the SNDINFO was replaced! + parse_ss = true; + } + else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0) + { + parse_ss = true; + } + + if (parse_ss) + { + S_ParseSndSeq(LocalSndSeq.IsNotEmpty() ? Wads.CheckNumForFullName(LocalSndSeq, true) : -1); + } + + LastLocalSndInfo = LocalSndInfo; + LastLocalSndSeq = LocalSndSeq; + } +} + +//========================================================================== +// +// S_PrecacheLevel +// +// Like R_PrecacheLevel, but for sounds. +// +//========================================================================== + +void S_PrecacheLevel(FLevelLocals* Level) +{ + if (GSnd && Level == primaryLevel) + { + soundEngine->MarkAllUnused(); + + AActor* actor; + auto iterator = Level->GetThinkerIterator(); + + // Precache all sounds known to be used by the currently spawned actors. + while ((actor = iterator.Next()) != nullptr) + { + IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds) + { + VMValue params[1] = { actor }; + VMCall(func, params, 1, nullptr, 0); + } + } + for (auto snd : gameinfo.PrecachedSounds) + { + soundEngine->MarkUsed(snd); + } + // Precache all extra sounds requested by this map. + for (auto snd : primaryLevel->info->PrecacheSounds) + { + soundEngine->MarkUsed(snd); + } + soundEngine->CacheMarkedSounds(); + } +} + + +//========================================================================== +// +// S_InitData +// +//========================================================================== + +void S_InitData() +{ + LastLocalSndInfo = LastLocalSndSeq = ""; + S_ParseSndInfo(false); + S_ParseSndSeq(-1); +} -extern SoundListener listener; -extern int RestartEvictionsAt; // do not restart evicted channels before this time -extern bool SoundPaused; // whether sound is paused -void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); //========================================================================== // @@ -80,12 +278,12 @@ void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float pitch) { - S_StartSound(SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); + soundEngine->StartSound(SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); } void S_Sound(int channel, FSoundID sound_id, float volume, float attenuation) { - S_StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); + soundEngine->StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); } DEFINE_ACTION_FUNCTION(DObject, S_Sound) @@ -100,23 +298,49 @@ DEFINE_ACTION_FUNCTION(DObject, S_Sound) return 0; } +//========================================================================== +// +// Common checking code for the actor sound functions +// +//========================================================================== + +static bool VerifyActorSound(AActor* ent, FSoundID& sound_id, int& channel) +{ + if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) + return false; + + if ((channel & CHAN_MAYBE_LOCAL) && (compatflags & COMPATF_SILENTPICKUP)) + { + if (soundEngine->isListener(ent)) + { + return false; + } + } + + if (soundEngine->isPlayerReserve(sound_id)) + { + sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); + if (sound_id <= 0) return false; + } + + if (compatflags & COMPATF_MAGICSILENCE) + { // For people who just can't play without a silent BFG. + channel = CHAN_WEAPON; + } + return true; +} + + //========================================================================== // // S_Sound - An actor is source // //========================================================================== + void S_SoundPitchActor(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation, float pitch) { - if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) - return; - - if (S_sfx[sound_id].bPlayerReserve) - { - sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); - if (sound_id <= 0) return; - } - - S_StartSound (SOURCE_Actor, ent, nullptr, channel, sound_id, volume, attenuation, 0, pitch); + if (VerifyActorSound(ent, sound_id, channel)) + soundEngine->StartSound (SOURCE_Actor, ent, nullptr, channel, sound_id, volume, attenuation, 0, pitch); } void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation) @@ -134,22 +358,15 @@ void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float at void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume, float mindist, float maxdist) { - if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) - return; - - if (S_sfx[sound_id].bPlayerReserve) + if (VerifyActorSound(ent, sound_id, channel)) { - sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); - if (sound_id <= 0) return; + FRolloffInfo rolloff; + + rolloff.RolloffType = ROLLOFF_Linear; + rolloff.MinDistance = mindist; + rolloff.MaxDistance = maxdist; + soundEngine->StartSound(SOURCE_Actor, ent, nullptr, channel, sound_id, volume, 1, &rolloff); } - - - FRolloffInfo rolloff; - - rolloff.RolloffType = ROLLOFF_Linear; - rolloff.MinDistance = mindist; - rolloff.MaxDistance = maxdist; - S_StartSound(SOURCE_Actor, ent, nullptr, channel, sound_id, volume, 1, &rolloff); } //========================================================================== @@ -161,7 +378,7 @@ void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation) { if (poly->Level != primaryLevel) return; - S_StartSound (SOURCE_Polyobj, poly, nullptr, channel, sound_id, volume, attenuation); + soundEngine->StartSound (SOURCE_Polyobj, poly, nullptr, channel, sound_id, volume, attenuation); } //========================================================================== @@ -175,7 +392,7 @@ void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sou if (Level != primaryLevel) return; // The sound system switches Y and Z around. FVector3 p((float)pos.X, (float)pos.Z, (float)pos.Y); - S_StartSound (SOURCE_Unattached, nullptr, &p, channel, sound_id, volume, attenuation); + soundEngine->StartSound (SOURCE_Unattached, nullptr, &p, channel, sound_id, volume, attenuation); } //========================================================================== @@ -187,7 +404,7 @@ void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sou void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation) { if (sec->Level != primaryLevel) return; - S_StartSound (SOURCE_Sector, sec, nullptr, channel, sfxid, volume, attenuation); + soundEngine->StartSound (SOURCE_Sector, sec, nullptr, channel, sfxid, volume, attenuation); } //========================================================================== @@ -250,7 +467,7 @@ void A_PlaySound(AActor *self, int soundid, int channel, double volume, int loop void S_StopSound (AActor *actor, int channel) { - S_StopSound(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); + soundEngine->StopSound(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); } @@ -264,7 +481,7 @@ void S_StopSound (AActor *actor, int channel) void S_StopSound (const sector_t *sec, int channel) { - S_StopSound(SOURCE_Sector, sec, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); + soundEngine->StopSound(SOURCE_Sector, sec, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); } //========================================================================== @@ -277,7 +494,7 @@ void S_StopSound (const sector_t *sec, int channel) void S_StopSound (const FPolyObj *poly, int channel) { - S_StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); + soundEngine->StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); } //========================================================================== @@ -285,14 +502,14 @@ void S_StopSound (const FPolyObj *poly, int channel) // S_RelinkSound // // Moves all the sounds from one thing to another. If the destination is -// NULL, then the sound becomes a positioned sound. +// nullptr, then the sound becomes a positioned sound. //========================================================================== void S_RelinkSound (AActor *from, AActor *to) { FVector3 p = from->SoundPos(); - S_RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); + soundEngine->RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); } //========================================================================== @@ -303,7 +520,7 @@ void S_RelinkSound (AActor *from, AActor *to) void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) { - S_ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); + soundEngine->ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); } //========================================================================== @@ -314,17 +531,7 @@ void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) { - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (chan->SourceType == SOURCE_Actor && - chan->Source == actor && - chan->EntChannel == channel) - { - S_SetPitch(chan, (float)pitch); - return; - } - } - return; + soundEngine->ChangeSoundPitch(SOURCE_Actor, actor, channel, pitch); } //========================================================================== @@ -336,17 +543,17 @@ void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) { - return S_GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); + return soundEngine->GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); } bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) { - return S_GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); + return soundEngine->GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); } bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) { - return S_GetSoundPlayingInfo(SOURCE_Polyobj, poly, sound_id); + return soundEngine->GetSoundPlayingInfo(SOURCE_Polyobj, poly, sound_id); } //========================================================================== @@ -359,9 +566,9 @@ bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) { if (compatflags & COMPATF_MAGICSILENCE) { - channel = CHAN_AUTO; + channel = CHAN_AUTO; // checks all channels } - return S_IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); + return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); } //========================================================================== @@ -372,7 +579,8 @@ bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) static void S_SetListener(AActor *listenactor) { - if (listenactor != NULL) + SoundListener listener; + if (listenactor != nullptr) { listener.angle = (float)listenactor->Angles.Yaw.Radians(); /* @@ -393,10 +601,11 @@ static void S_SetListener(AActor *listenactor) listener.position.Zero(); listener.velocity.Zero(); listener.underwater = false; - listener.Environment = NULL; + listener.Environment = nullptr; listener.valid = false; } listener.ListenerObject = listenactor; + soundEngine->SetListener(listener); } //========================================================================== @@ -416,7 +625,7 @@ void S_UpdateSounds (AActor *listenactor) SN_UpdateActiveSequences(Level); } - S_UpdateSounds(primaryLevel->time); + soundEngine->UpdateSounds(primaryLevel->time); } //========================================================================== @@ -474,21 +683,11 @@ void S_SerializeSounds(FSerializer &arc) if (arc.isWriting()) { - TArray chans; - // 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. - for (chan = Channels; chan != NULL; chan = chan->NextChan) - { - // 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 & (CHAN_FORGETTABLE | CHAN_UI))) - { - chans.Push(chan); - } - } + TArray chans = soundEngine->AllActiveChannels(); + if (chans.Size() > 0 && arc.BeginArray("sounds")) { for (unsigned int i = chans.Size(); i-- != 0; ) @@ -506,13 +705,13 @@ void S_SerializeSounds(FSerializer &arc) { unsigned int count; - S_StopAllChannels(); + soundEngine->StopAllChannels(); if (arc.BeginArray("sounds")) { count = arc.ArraySize(); for (unsigned int i = 0; i < count; ++i) { - chan = (FSoundChan*)S_GetChannel(NULL); + chan = (FSoundChan*)soundEngine->GetChannel(nullptr); arc(nullptr, *chan); // Sounds always start out evicted when restored from a save. chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; @@ -526,12 +725,515 @@ void S_SerializeSounds(FSerializer &arc) // playing before the wipe, and depending on the synchronization // between the main thread and the mixer thread at the time, the // sounds might be heard briefly before pausing for the wipe. - RestartEvictionsAt = primaryLevel->time + 2; + soundEngine->SetRestartTime(primaryLevel->time + 2); } GSnd->Sync(false); GSnd->UpdateSounds(); } +//========================================================================== +// +// S_SetSoundPaused +// +// Called with state non-zero when the app is active, zero when it isn't. +// +//========================================================================== + +void S_SetSoundPaused(int state) +{ + if (state) + { + if (paused == 0) + { + S_ResumeSound(true); + if (GSnd != nullptr) + { + GSnd->SetInactive(SoundRenderer::INACTIVE_Active); + } + } + } + else + { + if (paused == 0) + { + S_PauseSound(false, true); + if (GSnd != nullptr) + { + GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ? + SoundRenderer::INACTIVE_Complete : + SoundRenderer::INACTIVE_Mute); + } + } + } + if (!netgame +#ifdef _DEBUG + && !demoplayback +#endif + ) + { + pauseext = !state; + } +} + + +//========================================================================== +// +// CalcSectorSoundOrg +// +// Returns the perceived sound origin for a sector. If the listener is +// inside the sector, then the origin is their location. Otherwise, the +// origin is from the nearest wall on the sector. +// +//========================================================================== + +static void CalcSectorSoundOrg(const DVector3& listenpos, const sector_t* sec, int channum, FVector3& pos) +{ + if (!(sec->Level->i_compatflags & COMPATF_SECTORSOUNDS)) + { + // Are we inside the sector? If yes, the closest point is the one we're on. + if (primaryLevel->PointInSector(listenpos.X, listenpos.Y) == sec) + { + pos.X = (float)listenpos.X; + pos.Z = (float)listenpos.Y; + } + else + { + // Find the closest point on the sector's boundary lines and use + // that as the perceived origin of the sound. + DVector2 xy; + sec->ClosestPoint(listenpos, xy); + pos.X = (float)xy.X; + pos.Z = (float)xy.Y; + } + } + else + { + pos.X = float(sec->centerspot.X); + pos.Z = float(sec->centerspot.Y); + } + + // Set sound vertical position based on channel. + if (channum == CHAN_FLOOR) + { + pos.Y = (float)MIN(sec->floorplane.ZatPoint(listenpos), listenpos.Z); + } + else if (channum == CHAN_CEILING) + { + pos.Y = (float)MAX(sec->ceilingplane.ZatPoint(listenpos), listenpos.Z); + } + else if (channum == CHAN_INTERIOR) + { + pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); + } +} + +//========================================================================== +// +// CalcPolySoundOrg +// +// Returns the perceived sound origin for a polyobject. This is similar to +// CalcSectorSoundOrg, except there is no special case for being "inside" +// a polyobject, so the sound literally comes from the polyobject's walls. +// Vertical position of the sound always comes from the visible wall. +// +//========================================================================== + +static void CalcPolyobjSoundOrg(const DVector3& listenpos, const FPolyObj* poly, FVector3& pos) +{ + side_t* side; + sector_t* sec; + + DVector2 ppos; + poly->ClosestPoint(listenpos, ppos, &side); + pos.X = (float)ppos.X; + pos.Z = (float)ppos.Y; + sec = side->sector; + pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); +} + +//========================================================================= +// +// CalcPosVel +// +// The game specific part of the sound updater. +// +//========================================================================= + +void DoomSoundEngine::CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FVector3* pos, FVector3* vel) +{ + if (pos != nullptr) + { + DVector3 listenpos; + int pgroup; + AActor* listener = players[consoleplayer].camera; + + if (listener != nullptr) + { + listenpos = listener->Pos(); + *pos = listener->SoundPos(); + pgroup = listener->Sector->PortalGroup; + } + else + { + listenpos.Zero(); + pos->Zero(); + pgroup = 0; + } + if (vel) vel->Zero(); + + // [BL] Moved this case out of the switch statement to make code easier + // on static analysis. + if (type == SOURCE_Unattached) + { + sector_t* sec = primaryLevel->PointInSector(pt[0], pt[2]); + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sec->PortalGroup); + pos->X = pt[0] - (float)disp.X; + pos->Y = !(chanflags & CHAN_LISTENERZ) ? pt[1] : (float)listenpos.Z; + pos->Z = pt[2] - (float)disp.Y; + } + else + { + switch (type) + { + case SOURCE_None: + default: + break; + + case SOURCE_Actor: + { + auto actor = (AActor*)source; + //assert(actor != nullptr); + if (actor != nullptr) + { + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, actor->Sector->PortalGroup); + DVector3 posi = actor->Pos() - disp; + *pos = { (float)posi.X, (float)posi.Z, (float)posi.Y }; + if (vel) + { + vel->X = float(actor->Vel.X * TICRATE); + vel->Y = float(actor->Vel.Z * TICRATE); + vel->Z = float(actor->Vel.Y * TICRATE); + } + } + break; + } + + case SOURCE_Sector: + { + auto sector = (sector_t*)source; + assert(sector != nullptr); + if (sector != nullptr) + { + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sector->PortalGroup); + if (chanflags & CHAN_AREA) + { + // listener must be reversely offset to calculate the proper sound origin. + CalcSectorSoundOrg(listenpos + disp, sector, channum, *pos); + pos->X -= (float)disp.X; + pos->Z -= (float)disp.Y; + } + else + { + + pos->X = (float)(sector->centerspot.X - disp.X); + pos->Z = (float)(sector->centerspot.Y - disp.Y); + chanflags |= CHAN_LISTENERZ; + } + } + break; + } + + case SOURCE_Polyobj: + { + auto poly = (FPolyObj*)source; + assert(poly != nullptr); + if (poly != nullptr) + { + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup); + CalcPolyobjSoundOrg(listenpos + disp, poly, *pos); + pos->X -= (float)disp.X; + pos->Z -= (float)disp.Y; + } + break; + } + } + + if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != nullptr) + { + pos->Y = (float)listenpos.Z; + } + } + } +} + +//========================================================================== +// +// ValidatePosVel +// +//========================================================================== + +inline bool Validate(const float value, const float limit) +{ + return value >= -limit && value <= limit; +} + +static bool Validate(const FVector3& value, const float limit, const char* const name, const AActor* const actor) +{ + const bool valid = + Validate(value.X, limit) + && Validate(value.Y, limit) + && Validate(value.Z, limit); + + if (!valid) + { + // Sound position and velocity have Y and Z axes swapped comparing to map coordinate system + Printf(TEXTCOLOR_RED "Invalid sound %s " TEXTCOLOR_WHITE "(%f, %f, %f)", name, value.X, value.Z, value.Y); + + if (actor == nullptr) + { + Printf("\n"); + } + else + { + Printf(TEXTCOLOR_RED " for actor of class " TEXTCOLOR_WHITE "%s\n", actor->GetClass()->TypeName.GetChars()); + } + } + + return valid; +} + +bool DoomSoundEngine::ValidatePosVel(int sourcetype, const void* source, const FVector3& pos, const FVector3& vel) +{ + if (sourcetype != SOURCE_Actor) return true; + // The actual limit for map coordinates + auto actor = (AActor*)source; + static const float POSITION_LIMIT = 1024.f * 1024.f; + const bool valid = Validate(pos, POSITION_LIMIT, "position", actor); + + // The maximum velocity is enough to travel through entire map in one tic + static const float VELOCITY_LIMIT = 2 * POSITION_LIMIT * TICRATE; + return Validate(vel, VELOCITY_LIMIT, "velocity", actor) && valid; +} + +//========================================================================== +// +// This is to avoid hardscoding the dependency on Wads into the sound engine +// +//========================================================================== + +TArray DoomSoundEngine::ReadSound(int lumpnum) +{ + auto wlump = Wads.OpenLumpReader(lumpnum); + return wlump.Read(); +} + +//========================================================================== +// +// S_PickReplacement +// +// This is overridden to use a synchronized RNG. +// +//========================================================================== +static FRandom pr_randsound("RandSound"); + +int DoomSoundEngine::PickReplacement(int refid) +{ + while (S_sfx[refid].bRandomHeader) + { + const FRandomSoundList* list = &S_rnd[S_sfx[refid].link]; + refid = list->Choices[pr_randsound(list->Choices.Size())]; + } + return refid; +} + +//========================================================================== +// +// S_NoiseDebug +// +// [RH] Print sound debug info. Called by status bar. +//========================================================================== + +void DoomSoundEngine::NoiseDebug() +{ + FSoundChan* chan; + FVector3 listener; + FVector3 origin; + int y, color; + + y = 32 * CleanYfac; + screen->DrawText(NewConsoleFont, CR_YELLOW, 0, y, "*** SOUND DEBUG INFO ***", TAG_DONE); + y += NewConsoleFont->GetHeight(); + + screen->DrawText(NewConsoleFont, CR_GOLD, 0, y, "name", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 70, y, "x", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 120, y, "y", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 170, y, "z", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 220, y, "vol", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 260, y, "dist", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 300, y, "chan", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 340, y, "pri", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 380, y, "flags", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 460, y, "aud", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 520, y, "pos", TAG_DONE); + y += NewConsoleFont->GetHeight(); + + if (Channels == nullptr) + { + return; + } + + + listener = players[consoleplayer].camera->SoundPos(); + + // Display the oldest channel first. + for (chan = Channels; chan->NextChan != nullptr; chan = chan->NextChan) + { + } + while (y < SCREENHEIGHT - 16) + { + char temp[32]; + + SoundEngine::CalcPosVel(chan, &origin, nullptr); + color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY; + + // Name + Wads.GetLumpName(temp, S_sfx[chan->SoundID].lumpnum); + temp[8] = 0; + screen->DrawText(NewConsoleFont, color, 0, y, temp, TAG_DONE); + + if (!(chan->ChanFlags & CHAN_IS3D)) + { + screen->DrawText(NewConsoleFont, color, 70, y, "---", TAG_DONE); // X + screen->DrawText(NewConsoleFont, color, 120, y, "---", TAG_DONE); // Y + screen->DrawText(NewConsoleFont, color, 170, y, "---", TAG_DONE); // Z + screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); // Distance + } + else + { + // X coordinate + mysnprintf(temp, countof(temp), "%.0f", origin.X); + screen->DrawText(NewConsoleFont, color, 70, y, temp, TAG_DONE); + + // Y coordinate + mysnprintf(temp, countof(temp), "%.0f", origin.Z); + screen->DrawText(NewConsoleFont, color, 120, y, temp, TAG_DONE); + + // Z coordinate + mysnprintf(temp, countof(temp), "%.0f", origin.Y); + screen->DrawText(NewConsoleFont, color, 170, y, temp, TAG_DONE); + + // Distance + if (chan->DistanceScale > 0) + { + mysnprintf(temp, countof(temp), "%.0f", (origin - listener).Length()); + screen->DrawText(NewConsoleFont, color, 260, y, temp, TAG_DONE); + } + else + { + screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); + } + } + + // Volume + mysnprintf(temp, countof(temp), "%.2g", chan->Volume); + screen->DrawText(NewConsoleFont, color, 220, y, temp, TAG_DONE); + + // Channel + mysnprintf(temp, countof(temp), "%d", chan->EntChannel); + screen->DrawText(NewConsoleFont, color, 300, y, temp, TAG_DONE); + + // Priority + mysnprintf(temp, countof(temp), "%d", chan->Priority); + screen->DrawText(NewConsoleFont, color, 340, y, temp, TAG_DONE); + + // Flags + mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE%sV", + (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); + screen->DrawText(NewConsoleFont, color, 380, y, temp, TAG_DONE); + + // Audibility + mysnprintf(temp, countof(temp), "%.4f", GSnd->GetAudibility(chan)); + screen->DrawText(NewConsoleFont, color, 460, y, temp, TAG_DONE); + + // Position + mysnprintf(temp, countof(temp), "%u", GSnd->GetPosition(chan)); + screen->DrawText(NewConsoleFont, color, 520, y, temp, TAG_DONE); + + + y += NewConsoleFont->GetHeight(); + if (chan->PrevChan == &Channels) + { + break; + } + chan = (FSoundChan*)((size_t)chan->PrevChan - myoffsetof(FSoundChan, NextChan)); + } +} + +void S_NoiseDebug(void) +{ + static_cast(soundEngine)->NoiseDebug(); +} + + +//========================================================================== +// +// CCMD soundlist +// +//========================================================================== + +void DoomSoundEngine::PrintSoundList() +{ + auto &S_sfx = soundEngine->GetSounds(); + char lumpname[9]; + unsigned int i; + + lumpname[8] = 0; + for (i = 0; i < S_sfx.Size(); i++) + { + const sfxinfo_t* sfx = &S_sfx[i]; + if (sfx->bRandomHeader) + { + Printf("%3d. %s -> #%d {", i, sfx->name.GetChars(), sfx->link); + const FRandomSoundList* list = &S_rnd[sfx->link]; + for (auto& me : list->Choices) + { + Printf(" %s ", S_sfx[me].name.GetChars()); + } + Printf("}\n"); + } + else if (sfx->bPlayerReserve) + { + Printf("%3d. %s <>\n", i, sfx->name.GetChars(), sfx->link); + } + else if (S_sfx[i].lumpnum != -1) + { + Wads.GetLumpName(lumpname, sfx->lumpnum); + Printf("%3d. %s (%s)\n", i, sfx->name.GetChars(), lumpname); + } + else if (S_sfx[i].link != sfxinfo_t::NO_LINK) + { + Printf("%3d. %s -> %s\n", i, sfx->name.GetChars(), S_sfx[sfx->link].name.GetChars()); + } + else + { + Printf("%3d. %s **not present**\n", i, sfx->name.GetChars()); + } + Printf(" PitchMask = %d\n", sfx->PitchMask); + } +} + +CCMD(soundlist) +{ + static_cast(soundEngine)->PrintSoundList(); +} //========================================================================== // @@ -563,7 +1265,7 @@ CCMD (playsound) CCMD (loopsound) { - if (players[consoleplayer].mo != NULL && !netgame && argv.argc() > 1) + if (players[consoleplayer].mo != nullptr && !netgame && argv.argc() > 1) { FSoundID id = argv[1]; if (id == 0) @@ -573,7 +1275,7 @@ CCMD (loopsound) else { AActor *icon = Spawn(primaryLevel, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE); - if (icon != NULL) + if (icon != nullptr) { S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE); } @@ -599,7 +1301,7 @@ CCMD (cachesound) FSoundID sfxnum = argv[i]; if (sfxnum != FSoundID(0)) { - S_CacheSound (&S_sfx[sfxnum]); + soundEngine->CacheSound(sfxnum); } } } @@ -607,21 +1309,7 @@ CCMD (cachesound) CCMD(listsoundchannels) { - FSoundChan *chan; - int count = 0; - for (chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (!(chan->ChanFlags & CHAN_EVICTED)) - { - FVector3 chanorigin; - - CalcPosVel(chan, &chanorigin, NULL); - - Printf("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)chan->SoundID, chanorigin.X, chanorigin.Y, chanorigin.Z); - count++; - } - } - Printf("%d sounds playing\n", count); + Printf("%s", soundEngine->ListSoundChannels().GetChars()); } // intentionally moved here to keep the s_music include out of the rest of the file. @@ -642,7 +1330,7 @@ void S_PauseSound (bool notmusic, bool notsfx) } if (!notsfx) { - SoundPaused = true; + soundEngine->SetPaused(true); GSnd->SetSfxPaused (true, 0); } } @@ -668,7 +1356,7 @@ void S_ResumeSound (bool notsfx) S_ResumeMusic(); if (!notsfx) { - SoundPaused = false; + soundEngine->SetPaused(false); GSnd->SetSfxPaused (false, 0); } } @@ -682,6 +1370,7 @@ DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound) } + CCMD (snd_status) { GSnd->PrintStatus (); @@ -695,7 +1384,7 @@ CCMD (snd_reset) void S_SoundReset() { S_StopMusic(true); - S_Reset(); + soundEngine->Reset(); S_RestartMusic(); } diff --git a/src/sound/s_doomsound.h b/src/sound/s_doomsound.h index 49f002a8fb..e36cf6030c 100644 --- a/src/sound/s_doomsound.h +++ b/src/sound/s_doomsound.h @@ -3,8 +3,22 @@ // Information about one playing sound. struct sector_t; struct FPolyObj; +struct FLevelLocals; + +void S_Init(); +void S_InitData(); +void S_Start(); +void S_Shutdown(); void S_UpdateSounds(AActor* listenactor); +void S_SetSoundPaused(int state); + +void S_PrecacheLevel(FLevelLocals* l); + +// Start sound for thing at +void S_Sound(int channel, FSoundID sfxid, float volume, float attenuation); +void S_SoundPitch(int channel, FSoundID sfxid, float volume, float attenuation, float pitch); + void S_Sound (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation); void S_SoundMinMaxDist (AActor *ent, int channel, FSoundID sfxid, float volume, float mindist, float maxdist); @@ -44,4 +58,32 @@ void S_SerializeSounds(FSerializer &arc); void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch); static void S_SetListener(AActor *listenactor); -void S_SoundReset(); \ No newline at end of file +void S_SoundReset(); +void S_ResumeSound(bool state); +void S_PauseSound(bool state1, bool state); +void S_NoiseDebug(); + +inline void S_StopSound(int chan) +{ + soundEngine->StopSound(chan); +} + +inline void S_StopAllChannels() +{ + soundEngine->StopAllChannels(); +} + +inline const char* S_GetSoundName(FSoundID id) +{ + return soundEngine->GetSoundName(id); +} + +inline int S_FindSound(const char* logicalname) +{ + return soundEngine->FindSound(logicalname); +} + +inline int S_FindSoundByResID(int rid) +{ + return soundEngine->FindSoundByResID(rid); +} diff --git a/src/sound/s_sndseq.cpp b/src/sound/s_sndseq.cpp index da2b3d6003..9bdbfb9239 100644 --- a/src/sound/s_sndseq.cpp +++ b/src/sound/s_sndseq.cpp @@ -1433,13 +1433,13 @@ void SN_MarkPrecacheSounds(int sequence, seqtype_t type) { FSoundSequence *seq = Sequences[sequence]; - seq->StopSound.MarkUsed(); + soundEngine->MarkUsed(seq->StopSound); for (int i = 0; GetCommand(seq->Script[i]) != SS_CMD_END; ++i) { int cmd = GetCommand(seq->Script[i]); if (cmd == SS_CMD_PLAY || cmd == SS_CMD_PLAYREPEAT || cmd == SS_CMD_PLAYLOOP) { - FSoundID(GetData(seq->Script[i])).MarkUsed(); + soundEngine->MarkUsed(GetData(seq->Script[i])); } } } diff --git a/src/sound/s_sound.cpp b/src/sound/s_sound.cpp index a06be742c3..7b6492c2a8 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -41,262 +41,33 @@ #include "i_system.h" #include "i_sound.h" -#include "i_music.h" #include "s_sound.h" -#include "s_sndseq.h" -#include "s_playlist.h" -#include "c_dispatch.h" -#include "m_random.h" -#include "w_wad.h" -#include "p_local.h" -#include "doomstat.h" -#include "cmdlib.h" -#include "v_video.h" -#include "v_text.h" -#include "a_sharedglobal.h" -#include "gstrings.h" -#include "gi.h" -#include "po_man.h" -#include "serializer.h" -#include "d_player.h" -#include "g_levellocals.h" -#include "vm.h" -#include "g_game.h" -#include "s_music.h" - -// MACROS ------------------------------------------------------------------ - -#define NORM_PITCH 128 -#define NORM_PRIORITY 64 -#define NORM_SEP 0 - -#define S_PITCH_PERTURB 1 -#define S_STEREO_SWING 0.75 - -// TYPES ------------------------------------------------------------------- - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -extern float S_GetMusicVolume (const char *music); - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -void I_CloseSound(); +#include "m_swap.h" +#include "superfasthash.h" -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer); -static bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, int sourcetype, const void *actor, int channel); -static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen); -static void S_ActivatePlayList(bool goBack); -void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); -static void CalcPosVel(int type, const void *source, - const float pt[3], int channel, int chanflags, FVector3 *pos, FVector3 *vel); -static void CalcSectorSoundOrg(const DVector3 &listenpos, const sector_t *sec, int channum, FVector3 &res); -static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, FVector3 &res); - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -bool SoundPaused; // whether sound is paused -int RestartEvictionsAt; // do not restart evicted channels before this time -SoundListener listener; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -int sfx_empty; - -FSoundChan *Channels; -FSoundChan *FreeChannels; - -FRolloffInfo S_Rolloff; -TArray S_SoundCurve; - -FBoolCVar noisedebug ("noise", false, 0); // [RH] Print sound debugging info? -CUSTOM_CVAR (Int, snd_channels, 128, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number of channels available +enum { - if (self < 64) self = 64; -} -CVAR(Bool, snd_waterreverb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + DEFAULT_PITCH = 128, +}; -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// S_NoiseDebug -// -// [RH] Print sound debug info. Called by status bar. -//========================================================================== - -void S_NoiseDebug (void) -{ - FSoundChan *chan; - FVector3 listener; - FVector3 origin; - int y, color; - - y = 32 * CleanYfac; - screen->DrawText (NewConsoleFont, CR_YELLOW, 0, y, "*** SOUND DEBUG INFO ***", TAG_DONE); - y += NewConsoleFont->GetHeight(); - - screen->DrawText (NewConsoleFont, CR_GOLD, 0, y, "name", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 70, y, "x", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 120, y, "y", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 170, y, "z", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 220, y, "vol", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 260, y, "dist", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 300, y, "chan", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 340, y, "pri", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 380, y, "flags", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 460, y, "aud", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 520, y, "pos", TAG_DONE); - y += NewConsoleFont->GetHeight(); - - if (Channels == NULL) - { - return; - } - - - listener = players[consoleplayer].camera->SoundPos(); - - // Display the oldest channel first. - for (chan = Channels; chan->NextChan != NULL; chan = chan->NextChan) - { } - while (y < SCREENHEIGHT - 16) - { - char temp[32]; - - CalcPosVel(chan, &origin, NULL); - color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY; - - // Name - Wads.GetLumpName (temp, S_sfx[chan->SoundID].lumpnum); - temp[8] = 0; - screen->DrawText (NewConsoleFont, color, 0, y, temp, TAG_DONE); - - if (!(chan->ChanFlags & CHAN_IS3D)) - { - screen->DrawText(NewConsoleFont, color, 70, y, "---", TAG_DONE); // X - screen->DrawText(NewConsoleFont, color, 120, y, "---", TAG_DONE); // Y - screen->DrawText(NewConsoleFont, color, 170, y, "---", TAG_DONE); // Z - screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); // Distance - } - else - { - // X coordinate - mysnprintf(temp, countof(temp), "%.0f", origin.X); - screen->DrawText(NewConsoleFont, color, 70, y, temp, TAG_DONE); - - // Y coordinate - mysnprintf(temp, countof(temp), "%.0f", origin.Z); - screen->DrawText(NewConsoleFont, color, 120, y, temp, TAG_DONE); - - // Z coordinate - mysnprintf(temp, countof(temp), "%.0f", origin.Y); - screen->DrawText(NewConsoleFont, color, 170, y, temp, TAG_DONE); - - // Distance - if (chan->DistanceScale > 0) - { - mysnprintf(temp, countof(temp), "%.0f", (origin - listener).Length()); - screen->DrawText(NewConsoleFont, color, 260, y, temp, TAG_DONE); - } - else - { - screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); - } - } - - // Volume - mysnprintf(temp, countof(temp), "%.2g", chan->Volume); - screen->DrawText(NewConsoleFont, color, 220, y, temp, TAG_DONE); - - // Channel - mysnprintf(temp, countof(temp), "%d", chan->EntChannel); - screen->DrawText(NewConsoleFont, color, 300, y, temp, TAG_DONE); - - // Priority - mysnprintf(temp, countof(temp), "%d", chan->Priority); - screen->DrawText(NewConsoleFont, color, 340, y, temp, TAG_DONE); - - // Flags - mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE%sV", - (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); - screen->DrawText(NewConsoleFont, color, 380, y, temp, TAG_DONE); - - // Audibility - mysnprintf(temp, countof(temp), "%.4f", GSnd->GetAudibility(chan)); - screen->DrawText(NewConsoleFont, color, 460, y, temp, TAG_DONE); - - // Position - mysnprintf(temp, countof(temp), "%u", GSnd->GetPosition(chan)); - screen->DrawText(NewConsoleFont, color, 520, y, temp, TAG_DONE); - - - y += NewConsoleFont->GetHeight(); - if (chan->PrevChan == &Channels) - { - break; - } - chan = (FSoundChan *)((size_t)chan->PrevChan - myoffsetof(FSoundChan, NextChan)); - } -} - -static FString LastLocalSndInfo; -static FString LastLocalSndSeq; -void S_AddLocalSndInfo(int lump); +SoundEngine* soundEngine; +int sfx_empty = -1; //========================================================================== // // S_Init // -// Initializes sound stuff, including volume. Sets channels, SFX and -// music volume, allocates channel buffer, and sets S_sfx lookup. //========================================================================== -void S_Init () +void SoundEngine::Init(TArray &curve) { - int curvelump; - - // Heretic and Hexen have sound curve lookup tables. Doom does not. - I_InitSound(); - curvelump = Wads.CheckNumForName ("SNDCURVE"); - if (curvelump >= 0) - { - S_SoundCurve.Resize(Wads.LumpLength (curvelump)); - Wads.ReadLump(curvelump, S_SoundCurve.Data()); - } - else - { - S_SoundCurve.Clear(); - } - // Free all channels for use. while (Channels != NULL) { - S_ReturnChannel(Channels); + ReturnChannel(Channels); } -} - -//========================================================================== -// -// S_InitData -// -//========================================================================== - -void S_InitData () -{ - LastLocalSndInfo = LastLocalSndSeq = ""; - S_ParseSndInfo (false); - S_ParseSndSeq (-1); + S_SoundCurve = std::move(curve); } //========================================================================== @@ -305,13 +76,11 @@ void S_InitData () // //========================================================================== -void S_Shutdown () +void SoundEngine::Shutdown () { FSoundChan *chan, *next; - S_StopMusic(true); - mus_playing.LastSong = ""; // If this isn't reset here, the song would attempt resume at the most inpopportune time... - S_StopAllChannels(); + StopAllChannels(); if (GSnd) GSnd->UpdateSounds(); @@ -322,139 +91,48 @@ void S_Shutdown () delete chan; } FreeChannels = NULL; - - if (GSnd != NULL) - { - I_CloseSound(); - } } - //========================================================================== // -// S_Start +// MarkUsed // -// Per level startup code. Kills playing sounds at start of level -// and starts new music. //========================================================================== -void S_Start () +void SoundEngine::MarkUsed(int id) { - if (GSnd) + if ((unsigned)id < S_sfx.Size()) { - // kill all playing sounds at start of level (trust me - a good idea) - S_StopAllChannels(); - - // Check for local sound definitions. Only reload if they differ - // from the previous ones. - FString LocalSndInfo; - FString LocalSndSeq; - - // To be certain better check whether level is valid! - if (primaryLevel->info) - { - LocalSndInfo = primaryLevel->info->SoundInfo; - LocalSndSeq = primaryLevel->info->SndSeq; - } - - bool parse_ss = false; - - // This level uses a different local SNDINFO - if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !primaryLevel->info) - { - // First delete the old sound list - for(unsigned i = 1; i < S_sfx.Size(); i++) - { - S_UnloadSound(&S_sfx[i]); - } - - // Parse the global SNDINFO - S_ParseSndInfo(true); - - if (LocalSndInfo.IsNotEmpty()) - { - // Now parse the local SNDINFO - int j = Wads.CheckNumForFullName(LocalSndInfo, true); - if (j>=0) S_AddLocalSndInfo(j); - } - - // Also reload the SNDSEQ if the SNDINFO was replaced! - parse_ss = true; - } - else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0) - { - parse_ss = true; - } - - if (parse_ss) - { - S_ParseSndSeq(LocalSndSeq.IsNotEmpty()? Wads.CheckNumForFullName(LocalSndSeq, true) : -1); - } - - LastLocalSndInfo = LocalSndInfo; - LastLocalSndSeq = LocalSndSeq; + S_sfx[id].bUsed = true; } } //========================================================================== // -// S_PrecacheLevel -// -// Like R_PrecacheLevel, but for sounds. +// Cache all marked sounds // //========================================================================== -void S_PrecacheLevel (FLevelLocals *Level) +void SoundEngine::CacheMarkedSounds() { - unsigned int i; - - if (GSnd && Level == primaryLevel) + // Don't unload sounds that are playing right now. + for (FSoundChan* chan = Channels; chan != nullptr; chan = chan->NextChan) { - for (i = 0; i < S_sfx.Size(); ++i) - { - S_sfx[i].bUsed = false; - } + MarkUsed(chan->SoundID); + } - AActor *actor; - auto iterator = Level->GetThinkerIterator(); - - // Precache all sounds known to be used by the currently spawned actors. - while ( (actor = iterator.Next()) != NULL ) + for (unsigned i = 1; i < S_sfx.Size(); ++i) + { + if (S_sfx[i].bUsed) { - IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds) - { - VMValue params[1] = { actor }; - VMCall(func, params, 1, nullptr, 0); - } + CacheSound(&S_sfx[i]); } - for (auto snd : gameinfo.PrecachedSounds) + } + for (unsigned i = 1; i < S_sfx.Size(); ++i) + { + if (!S_sfx[i].bUsed && S_sfx[i].link == sfxinfo_t::NO_LINK) { - FSoundID(snd).MarkUsed(); - } - // Precache all extra sounds requested by this map. - for (auto snd : primaryLevel->info->PrecacheSounds) - { - FSoundID(snd).MarkUsed(); - } - // Don't unload sounds that are playing right now. - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - chan->SoundID.MarkUsed(); - } - - for (i = 1; i < S_sfx.Size(); ++i) - { - if (S_sfx[i].bUsed) - { - S_CacheSound (&S_sfx[i]); - } - } - for (i = 1; i < S_sfx.Size(); ++i) - { - if (!S_sfx[i].bUsed && S_sfx[i].link == sfxinfo_t::NO_LINK) - { - S_UnloadSound (&S_sfx[i]); - } + UnloadSound(&S_sfx[i]); } } } @@ -465,7 +143,7 @@ void S_PrecacheLevel (FLevelLocals *Level) // //========================================================================== -void S_CacheSound (sfxinfo_t *sfx) +void SoundEngine::CacheSound (sfxinfo_t *sfx) { if (GSnd) { @@ -480,14 +158,14 @@ void S_CacheSound (sfxinfo_t *sfx) } if (sfx->bRandomHeader) { - S_CacheRandomSound(sfx); + CacheRandomSound(sfx); } else { // Since we do not know in what format the sound will be used, we have to cache both. FSoundLoadBuffer SoundBuffer; - S_LoadSound(sfx, &SoundBuffer); - S_LoadSound3D(sfx, &SoundBuffer); + LoadSound(sfx, &SoundBuffer); + LoadSound3D(sfx, &SoundBuffer); sfx->bUsed = true; } } @@ -499,14 +177,12 @@ void S_CacheSound (sfxinfo_t *sfx) // //========================================================================== -void S_UnloadSound (sfxinfo_t *sfx) +void SoundEngine::UnloadSound (sfxinfo_t *sfx) { if (sfx->data3d.isValid() && sfx->data != sfx->data3d) GSnd->UnloadSound(sfx->data3d); if (sfx->data.isValid()) GSnd->UnloadSound(sfx->data); - if (sfx->data.isValid() || sfx->data3d.isValid()) - DPrintf(DMSG_NOTIFY, "Unloaded sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); sfx->data.Clear(); sfx->data3d.Clear(); } @@ -519,21 +195,21 @@ void S_UnloadSound (sfxinfo_t *sfx) // //========================================================================== -FISoundChannel *S_GetChannel(void *syschan) +FSoundChan *SoundEngine::GetChannel(void *syschan) { FSoundChan *chan; if (FreeChannels != NULL) { chan = FreeChannels; - S_UnlinkChannel(chan); + UnlinkChannel(chan); } else { chan = new FSoundChan; memset(chan, 0, sizeof(*chan)); } - S_LinkChannel(chan, &Channels); + LinkChannel(chan, &Channels); chan->SysChannel = syschan; return chan; } @@ -546,11 +222,11 @@ FISoundChannel *S_GetChannel(void *syschan) // //========================================================================== -void S_ReturnChannel(FSoundChan *chan) +void SoundEngine::ReturnChannel(FSoundChan *chan) { - S_UnlinkChannel(chan); + UnlinkChannel(chan); memset(chan, 0, sizeof(*chan)); - S_LinkChannel(chan, &FreeChannels); + LinkChannel(chan, &FreeChannels); } //========================================================================== @@ -559,7 +235,7 @@ void S_ReturnChannel(FSoundChan *chan) // //========================================================================== -void S_UnlinkChannel(FSoundChan *chan) +void SoundEngine::UnlinkChannel(FSoundChan *chan) { *(chan->PrevChan) = chan->NextChan; if (chan->NextChan != NULL) @@ -574,7 +250,7 @@ void S_UnlinkChannel(FSoundChan *chan) // //========================================================================== -void S_LinkChannel(FSoundChan *chan, FSoundChan **head) +void SoundEngine::LinkChannel(FSoundChan *chan, FSoundChan **head) { chan->NextChan = *head; if (chan->NextChan != NULL) @@ -585,6 +261,56 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) chan->PrevChan = head; } +//========================================================================== +// +// +// +//========================================================================== + +TArray SoundEngine::AllActiveChannels() +{ + TArray chans; + + for (auto chan = Channels; chan != nullptr; chan = chan->NextChan) + { + // 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 & (CHAN_FORGETTABLE | CHAN_UI))) + { + chans.Push(chan); + } + } + return chans; +} + +//========================================================================== +// +// +// +//========================================================================== + +FString SoundEngine::ListSoundChannels() +{ + FString output; + FSoundChan* chan; + int count = 0; + for (chan = Channels; chan != nullptr; chan = chan->NextChan) + { + if (!(chan->ChanFlags & CHAN_EVICTED)) + { + FVector3 chanorigin; + + CalcPosVel(chan, &chanorigin, nullptr); + + output.AppendFormat("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)S_sfx[chan->SoundID].name.GetChars(), chanorigin.X, chanorigin.Y, chanorigin.Z); + count++; + } + } + output.AppendFormat("%d sounds playing\n", count); + return output; +} + // [RH] Split S_StartSoundAtVolume into multiple parts so that sounds can // be specified both by id and by name. Also borrowed some stuff from // Hexen and parameters from Quake. @@ -598,253 +324,15 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) // //========================================================================= -void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) +void SoundEngine::CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) { CalcPosVel(chan->SourceType, chan->Source, chan->Point, chan->EntChannel, chan->ChanFlags, pos, vel); } -//========================================================================= -// -// CalcPosVel -// -// This version is for sounds that haven't started yet so have no channel. -// -//========================================================================= - -static void CalcPosVel(int type, const void *source, - const float pt[3], int channum, int chanflags, FVector3 *pos, FVector3 *vel) +bool SoundEngine::ValidatePosVel(const FSoundChan* const chan, const FVector3& pos, const FVector3& vel) { - if (pos != NULL) - { - DVector3 listenpos; - int pgroup; - AActor *listener = players[consoleplayer].camera; - - if (listener != NULL) - { - listenpos = listener->Pos(); - *pos = listener->SoundPos(); - pgroup = listener->Sector->PortalGroup; - } - else - { - listenpos.Zero(); - pos->Zero(); - pgroup = 0; - } - if (vel) vel->Zero(); - - // [BL] Moved this case out of the switch statement to make code easier - // on static analysis. - if(type == SOURCE_Unattached) - { - sector_t *sec = primaryLevel->PointInSector(pt[0], pt[2]); - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sec->PortalGroup); - pos->X = pt[0] - (float)disp.X; - pos->Y = !(chanflags & CHAN_LISTENERZ) ? pt[1] : (float)listenpos.Z; - pos->Z = pt[2] - (float)disp.Y; - } - else - { - switch (type) - { - case SOURCE_None: - default: - break; - - case SOURCE_Actor: - { - auto actor = (AActor*)source; - //assert(actor != NULL); - if (actor != NULL) - { - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, actor->Sector->PortalGroup); - DVector3 posi = actor->Pos() - disp; - *pos = { (float)posi.X, (float)posi.Z, (float)posi.Y }; - if (vel) - { - vel->X = float(actor->Vel.X * TICRATE); - vel->Y = float(actor->Vel.Z * TICRATE); - vel->Z = float(actor->Vel.Y * TICRATE); - } - } - break; - } - - case SOURCE_Sector: - { - auto sector = (sector_t*)source; - assert(sector != NULL); - if (sector != NULL) - { - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sector->PortalGroup); - if (chanflags & CHAN_AREA) - { - // listener must be reversely offset to calculate the proper sound origin. - CalcSectorSoundOrg(listenpos + disp, sector, channum, *pos); - pos->X -= (float)disp.X; - pos->Z -= (float)disp.Y; - } - else - { - - pos->X = (float)(sector->centerspot.X - disp.X); - pos->Z = (float)(sector->centerspot.Y - disp.Y); - chanflags |= CHAN_LISTENERZ; - } - } - break; - } - - case SOURCE_Polyobj: - { - auto poly = (FPolyObj*)source; - assert(poly != NULL); - if (poly != NULL) - { - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup); - CalcPolyobjSoundOrg(listenpos + disp, poly, *pos); - pos->X -= (float)disp.X; - pos->Z -= (float)disp.Y; - } - break; - } - } - - if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != NULL) - { - pos->Y = (float)listenpos.Z; - } - } - } -} - -//========================================================================== -// -// ValidatePosVel -// -//========================================================================== - -inline bool Validate(const float value, const float limit) -{ - return value >= -limit && value <= limit; -} - -static bool Validate(const FVector3 &value, const float limit, const char *const name, const AActor *const actor) -{ - const bool valid = - Validate(value.X, limit) - && Validate(value.Y, limit) - && Validate(value.Z, limit); - - if (!valid) - { - // Sound position and velocity have Y and Z axes swapped comparing to map coordinate system - Printf(TEXTCOLOR_RED "Invalid sound %s " TEXTCOLOR_WHITE "(%f, %f, %f)", name, value.X, value.Z, value.Y); - - if (actor == nullptr) - { - Printf("\n"); - } - else - { - Printf(TEXTCOLOR_RED " for actor of class " TEXTCOLOR_WHITE "%s\n", actor->GetClass()->TypeName.GetChars()); - } - } - - return valid; -} - -static bool ValidatePosVel(const AActor *actor, const FVector3 &pos, const FVector3 &vel) -{ - // The actual limit for map coordinates - static const float POSITION_LIMIT = 1024.f * 1024.f; - const bool valid = Validate(pos, POSITION_LIMIT, "position", actor); - - // The maximum velocity is enough to travel through entire map in one tic - static const float VELOCITY_LIMIT = 2 * POSITION_LIMIT * TICRATE; - return Validate(vel, VELOCITY_LIMIT, "velocity", actor) && valid; -} - -static bool ValidatePosVel(const FSoundChan *const chan, const FVector3 &pos, const FVector3 &vel) -{ - return ValidatePosVel(chan->SourceType == SOURCE_Actor ? (AActor*)chan->Source : nullptr, pos, vel); -} - -//========================================================================== -// -// CalcSectorSoundOrg -// -// Returns the perceived sound origin for a sector. If the listener is -// inside the sector, then the origin is their location. Otherwise, the -// origin is from the nearest wall on the sector. -// -//========================================================================== - -static void CalcSectorSoundOrg(const DVector3 &listenpos, const sector_t *sec, int channum, FVector3 &pos) -{ - if (!(sec->Level->i_compatflags & COMPATF_SECTORSOUNDS)) - { - // Are we inside the sector? If yes, the closest point is the one we're on. - if (primaryLevel->PointInSector(listenpos.X, listenpos.Y) == sec) - { - pos.X = (float)listenpos.X; - pos.Z = (float)listenpos.Y; - } - else - { - // Find the closest point on the sector's boundary lines and use - // that as the perceived origin of the sound. - DVector2 xy; - sec->ClosestPoint(listenpos, xy); - pos.X = (float)xy.X; - pos.Z = (float)xy.Y; - } - } - else - { - pos.X = float(sec->centerspot.X); - pos.Z = float(sec->centerspot.Y); - } - - // Set sound vertical position based on channel. - if (channum == CHAN_FLOOR) - { - pos.Y = (float)MIN(sec->floorplane.ZatPoint(listenpos), listenpos.Z); - } - else if (channum == CHAN_CEILING) - { - pos.Y = (float)MAX(sec->ceilingplane.ZatPoint(listenpos), listenpos.Z); - } - else if (channum == CHAN_INTERIOR) - { - pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); - } -} - -//========================================================================== -// -// CalcPolySoundOrg -// -// Returns the perceived sound origin for a polyobject. This is similar to -// CalcSectorSoundOrg, except there is no special case for being "inside" -// a polyobject, so the sound literally comes from the polyobject's walls. -// Vertical position of the sound always comes from the visible wall. -// -//========================================================================== - -static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, FVector3 &pos) -{ - side_t *side; - sector_t *sec; - - DVector2 ppos; - poly->ClosestPoint(listenpos, ppos, &side); - pos.X = (float)ppos.X; - pos.Z = (float)ppos.Y; - sec = side->sector; - pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); + return ValidatePosVel(chan->SourceType, chan->Source, pos, vel); } //========================================================================== @@ -857,7 +345,7 @@ static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, // //========================================================================== -FSoundChan *S_StartSound(int type, const void *source, +FSoundChan *SoundEngine::StartSound(int type, const void *source, const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo *forcedrolloff, float spitch) { @@ -874,6 +362,7 @@ FSoundChan *S_StartSound(int type, const void *source, if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) return NULL; + // prevent crashes. if (type == SOURCE_Unattached && pt == nullptr) type = SOURCE_None; org_id = sound_id; @@ -882,27 +371,15 @@ FSoundChan *S_StartSound(int type, const void *source, CalcPosVel(type, source, &pt->X, channel, chanflags, &pos, &vel); - if (!ValidatePosVel(type == SOURCE_Actor ? (AActor*)source : nullptr, pos, vel)) + if (!ValidatePosVel(type, source, pos, vel)) { return nullptr; } - if (compatflags & COMPATF_MAGICSILENCE) - { // For people who just can't play without a silent BFG. - channel = CHAN_WEAPON; - } - else if ((chanflags & CHAN_MAYBE_LOCAL) && (compatflags & COMPATF_SILENTPICKUP)) - { - if (source != nullptr && source == listener.ListenerObject) - { - return nullptr; - } - } - sfx = &S_sfx[sound_id]; // Scale volume according to SNDINFO data. - volume = MIN(volume * sfx->Volume, 1.f); + volume = std::min(volume * sfx->Volume, 1.f); if (volume <= 0) return NULL; @@ -920,7 +397,7 @@ FSoundChan *S_StartSound(int type, const void *source, { // Random sounds attenuate based on the original (random) sound as well as the chosen one. attenuation *= sfx->Attenuation; - sound_id = FSoundID(S_PickReplacement (sound_id)); + sound_id = FSoundID(PickReplacement (sound_id)); if (near_limit < 0) { near_limit = S_sfx[sound_id].NearLimit; @@ -963,7 +440,7 @@ FSoundChan *S_StartSound(int type, const void *source, } // If this is a singular sound, don't play it if it's already playing. - if (sfx->bSingular && S_CheckSingular(sound_id)) + if (sfx->bSingular && CheckSingular(sound_id)) { chanflags |= CHAN_EVICTED; } @@ -976,8 +453,8 @@ FSoundChan *S_StartSound(int type, const void *source, } // If this sound doesn't like playing near itself, don't play it if - // that's what would happen. - if (near_limit > 0 && S_CheckSoundLimit(sfx, pos, near_limit, limit_range, type, type == SOURCE_Actor? source : nullptr, channel)) + // that's what would happen. (Does this really need the SOURCE_Actor restriction?) + if (near_limit > 0 && CheckSoundLimit(sfx, pos, near_limit, limit_range, type, type == SOURCE_Actor? source : nullptr, channel)) { chanflags |= CHAN_EVICTED; } @@ -991,7 +468,7 @@ FSoundChan *S_StartSound(int type, const void *source, } // Make sure the sound is loaded. - sfx = S_LoadSound(sfx, &SoundBuffer); + sfx = LoadSound(sfx, &SoundBuffer); // The empty sound never plays. if (sfx->lumpnum == sfx_empty) @@ -1014,7 +491,7 @@ FSoundChan *S_StartSound(int type, const void *source, { // Select a channel that isn't already playing something. // Try channel 0 first, then travel from channel 7 down. - if (!S_IsChannelUsed(type, source, 0, &seen)) + if (!IsChannelUsed(type, source, 0, &seen)) { channel = 0; } @@ -1022,7 +499,7 @@ FSoundChan *S_StartSound(int type, const void *source, { for (channel = 7; channel > 0; --channel) { - if (!S_IsChannelUsed(type, source, channel, &seen)) + if (!IsChannelUsed(type, source, channel, &seen)) { break; } @@ -1035,7 +512,7 @@ FSoundChan *S_StartSound(int type, const void *source, } // If this actor is already playing something on the selected channel, stop it. - if (type != SOURCE_None && ((source == NULL && channel != CHAN_AUTO) || (source != NULL && S_IsChannelUsed(type, source, channel, &seen)))) + if (type != SOURCE_None && ((source == NULL && channel != CHAN_AUTO) || (source != NULL && IsChannelUsed(type, source, channel, &seen)))) { for (chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1060,11 +537,11 @@ FSoundChan *S_StartSound(int type, const void *source, // Vary the sfx pitches. if (pitchmask != 0) { - pitch = NORM_PITCH - (M_Random() & pitchmask) + (M_Random() & pitchmask); + pitch = DEFAULT_PITCH - (rand() & pitchmask) + (rand() & pitchmask); } else { - pitch = NORM_PITCH; + pitch = DEFAULT_PITCH; } if (chanflags & CHAN_EVICTED) @@ -1081,7 +558,7 @@ FSoundChan *S_StartSound(int type, const void *source, if (attenuation > 0) { - S_LoadSound3D(sfx, &SoundBuffer); + LoadSound3D(sfx, &SoundBuffer); chan = (FSoundChan*)GSnd->StartSound3D (sfx->data3d, &listener, float(volume), rolloff, float(attenuation), pitch, basepriority, pos, vel, channel, startflags, NULL); } else @@ -1091,7 +568,7 @@ FSoundChan *S_StartSound(int type, const void *source, } if (chan == NULL && (chanflags & CHAN_LOOP)) { - chan = (FSoundChan*)S_GetChannel(NULL); + chan = (FSoundChan*)GetChannel(NULL); GSnd->MarkStartTime(chan); chanflags |= CHAN_EVICTED; } @@ -1126,7 +603,7 @@ FSoundChan *S_StartSound(int type, const void *source, } if (spitch > 0.0) - S_SetPitch(chan, spitch); + SetPitch(chan, spitch); } return chan; @@ -1140,7 +617,7 @@ FSoundChan *S_StartSound(int type, const void *source, // //========================================================================== -void S_RestartSound(FSoundChan *chan) +void SoundEngine::RestartChannel(FSoundChan *chan) { assert(chan->ChanFlags & CHAN_EVICTED); @@ -1149,10 +626,10 @@ void S_RestartSound(FSoundChan *chan) FSoundLoadBuffer SoundBuffer; // If this is a singular sound, don't play it if it's already playing. - if (sfx->bSingular && S_CheckSingular(chan->SoundID)) + if (sfx->bSingular && CheckSingular(chan->SoundID)) return; - sfx = S_LoadSound(sfx, &SoundBuffer); + sfx = LoadSound(sfx, &SoundBuffer); // The empty sound never plays. if (sfx->lumpnum == sfx_empty) @@ -1181,12 +658,12 @@ void S_RestartSound(FSoundChan *chan) // If this sound doesn't like playing near itself, don't play it if // that's what would happen. - if (chan->NearLimit > 0 && S_CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0)) + if (chan->NearLimit > 0 && CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0)) { return; } - S_LoadSound3D(sfx, &SoundBuffer); + LoadSound3D(sfx, &SoundBuffer); chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME); ochan = (FSoundChan*)GSnd->StartSound3D(sfx->data3d, &listener, chan->Volume, &chan->Rolloff, chan->DistanceScale, chan->Pitch, chan->Priority, pos, vel, chan->EntChannel, startflags, chan); @@ -1211,7 +688,7 @@ void S_RestartSound(FSoundChan *chan) // //========================================================================== -sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) +sfxinfo_t *SoundEngine::LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (GSnd->IsNull()) return sfx; @@ -1231,7 +708,7 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (S_sfx[i].data.isValid() && S_sfx[i].link == sfxinfo_t::NO_LINK && S_sfx[i].lumpnum == sfx->lumpnum) { - DPrintf (DMSG_NOTIFY, "Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); + //DPrintf (DMSG_NOTIFY, "Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); sfx->link = i; // This is necessary to avoid using the rolloff settings of the linked sound if its // settings are different. @@ -1240,13 +717,12 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) } } - DPrintf(DMSG_NOTIFY, "Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + //DPrintf(DMSG_NOTIFY, "Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); - int size = Wads.LumpLength(sfx->lumpnum); + auto sfxdata = ReadSound(sfx->lumpnum); + int size = sfxdata.Size(); if (size > 8) { - auto wlump = Wads.OpenLumpReader(sfx->lumpnum); - auto sfxdata = wlump.Read(size); int32_t dmxlen = LittleLong(((int32_t *)sfxdata.Data())[1]); std::pair snd; @@ -1291,14 +767,14 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) return sfx; } -static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) +void SoundEngine::LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (GSnd->IsNull()) return; if(sfx->data3d.isValid()) return; - DPrintf(DMSG_NOTIFY, "Loading monoized sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + //DPrintf(DMSG_NOTIFY, "Loading monoized sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); std::pair snd; @@ -1308,11 +784,9 @@ static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) } else { - int size = Wads.LumpLength(sfx->lumpnum); + auto sfxdata = ReadSound(sfx->lumpnum); + int size = sfxdata.Size(); if (size <= 8) return; - - auto wlump = Wads.OpenLumpReader(sfx->lumpnum); - auto sfxdata = wlump.Read(size); int32_t dmxlen = LittleLong(((int32_t *)sfxdata.Data())[1]); // If the sound is voc, use the custom loader. @@ -1350,7 +824,7 @@ static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) // //========================================================================== -bool S_CheckSingular(int sound_id) +bool SoundEngine::CheckSingular(int sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1379,7 +853,7 @@ bool S_CheckSingular(int sound_id) // //========================================================================== -bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, +bool SoundEngine::CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, int sourcetype, const void *actor, int channel) { FSoundChan *chan; @@ -1415,7 +889,7 @@ bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, floa // //========================================================================== -void S_StopSound (int channel) +void SoundEngine::StopSound (int channel) { FSoundChan *chan = Channels; while (chan != NULL) @@ -1423,7 +897,7 @@ void S_StopSound (int channel) FSoundChan *next = chan->NextChan; if (chan->SourceType == SOURCE_None) { - S_StopChannel(chan); + StopChannel(chan); } chan = next; } @@ -1437,7 +911,7 @@ void S_StopSound (int channel) // //========================================================================== -void S_StopSound(int sourcetype, const void* actor, int channel) +void SoundEngine::StopSound(int sourcetype, const void* actor, int channel) { FSoundChan* chan = Channels; while (chan != NULL) @@ -1447,7 +921,7 @@ void S_StopSound(int sourcetype, const void* actor, int channel) chan->Source == actor && (chan->EntChannel == channel || channel < 0)) { - S_StopChannel(chan); + StopChannel(chan); } chan = next; } @@ -1459,18 +933,13 @@ void S_StopSound(int sourcetype, const void* actor, int channel) // //========================================================================== -void S_StopAllChannels () +void SoundEngine::StopAllChannels () { - for (auto Level : AllLevels()) - { - SN_StopAllSequences(Level); - } - FSoundChan *chan = Channels; while (chan != NULL) { FSoundChan *next = chan->NextChan; - S_StopChannel(chan); + StopChannel(chan); chan = next; } @@ -1486,7 +955,7 @@ void S_StopAllChannels () // NULL, then the sound becomes a positioned sound. //========================================================================== -void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) +void SoundEngine::RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) { if (from == NULL) return; @@ -1511,7 +980,7 @@ void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVec } else { - S_StopChannel(chan); + StopChannel(chan); } } chan = next; @@ -1525,7 +994,7 @@ void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVec // //========================================================================== -void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume) +void SoundEngine::ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume) { float volume = float(dvolume); // don't let volume get out of bounds @@ -1554,7 +1023,7 @@ void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double // //========================================================================== -void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch) +void SoundEngine::ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1562,18 +1031,18 @@ void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double chan->Source == source && chan->EntChannel == channel) { - S_SetPitch(chan, (float)pitch); + SetPitch(chan, (float)pitch); return; } } return; } -void S_SetPitch(FSoundChan *chan, float pitch) +void SoundEngine::SetPitch(FSoundChan *chan, float pitch) { assert(chan != nullptr); - GSnd->ChannelPitch(chan, MAX(0.0001f, pitch)); - chan->Pitch = MAX(1, int(float(NORM_PITCH) * pitch)); + GSnd->ChannelPitch(chan, std::max(0.0001f, pitch)); + chan->Pitch = std::max(1, int(float(DEFAULT_PITCH) * pitch)); } //========================================================================== @@ -1583,7 +1052,7 @@ void S_SetPitch(FSoundChan *chan, float pitch) // Is a sound being played by a specific emitter? //========================================================================== -bool S_GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) +bool SoundEngine::GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) { if (sound_id > 0) { @@ -1610,7 +1079,7 @@ bool S_GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) // //========================================================================== -static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) +bool SoundEngine::IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) { if (*seen & (1 << channel)) { @@ -1636,7 +1105,7 @@ static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int // //========================================================================== -bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) +bool SoundEngine::IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1651,50 +1120,6 @@ bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, return false; } -//========================================================================== -// -// S_SetSoundPaused -// -// Called with state non-zero when the app is active, zero when it isn't. -// -//========================================================================== - -void S_SetSoundPaused (int state) -{ - if (state) - { - if (paused == 0) - { - S_ResumeSound(true); - if (GSnd != NULL) - { - GSnd->SetInactive(SoundRenderer::INACTIVE_Active); - } - } - } - else - { - if (paused == 0) - { - S_PauseSound(false, true); - if (GSnd != NULL) - { - GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ? - SoundRenderer::INACTIVE_Complete : - SoundRenderer::INACTIVE_Mute); - } - } - } - if (!netgame -#ifdef _DEBUG - && !demoplayback -#endif - ) - { - pauseext = !state; - } -} - //========================================================================== // // S_EvictAllChannels @@ -1704,7 +1129,7 @@ void S_SetSoundPaused (int state) // //========================================================================== -void S_EvictAllChannels() +void SoundEngine::EvictAllChannels() { FSoundChan *chan, *next; @@ -1722,7 +1147,7 @@ void S_EvictAllChannels() chan->StartTime = GSnd ? GSnd->GetPosition(chan) : 0; chan->ChanFlags |= CHAN_ABSTIME; } - S_StopChannel(chan); + StopChannel(chan); } // assert(chan->NextChan == next); } @@ -1737,21 +1162,21 @@ void S_EvictAllChannels() // //========================================================================== -void S_RestoreEvictedChannel(FSoundChan *chan) +void SoundEngine::RestoreEvictedChannel(FSoundChan *chan) { if (chan == NULL) { return; } - S_RestoreEvictedChannel(chan->NextChan); + RestoreEvictedChannel(chan->NextChan); if (chan->ChanFlags & CHAN_EVICTED) { - S_RestartSound(chan); + RestartChannel(chan); if (!(chan->ChanFlags & CHAN_LOOP)) { if (chan->ChanFlags & CHAN_EVICTED) { // Still evicted and not looping? Forget about it. - S_ReturnChannel(chan); + ReturnChannel(chan); } else if (!(chan->ChanFlags & CHAN_JUSTSTARTED)) { // Should this sound become evicted again, it's okay to forget about it. @@ -1761,7 +1186,7 @@ void S_RestoreEvictedChannel(FSoundChan *chan) } else if (chan->SysChannel == NULL && (chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_LOOP)) == CHAN_FORGETTABLE) { - S_ReturnChannel(chan); + ReturnChannel(chan); } } @@ -1774,10 +1199,10 @@ void S_RestoreEvictedChannel(FSoundChan *chan) // //========================================================================== -void S_RestoreEvictedChannels() +void SoundEngine::RestoreEvictedChannels() { // Restart channels in the same order they were originally played. - S_RestoreEvictedChannel(Channels); + RestoreEvictedChannel(Channels); } //========================================================================== @@ -1787,7 +1212,7 @@ void S_RestoreEvictedChannels() // Updates music & sounds //========================================================================== -void S_UpdateSounds(int time) +void SoundEngine::UpdateSounds(int time) { FVector3 pos, vel; @@ -1811,7 +1236,7 @@ void S_UpdateSounds(int time) if (time >= RestartEvictionsAt) { RestartEvictionsAt = 0; - S_RestoreEvictedChannels(); + RestoreEvictedChannels(); } } @@ -1821,63 +1246,46 @@ void S_UpdateSounds(int time) // //========================================================================== -float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic) +float SoundEngine::GetRolloff(const FRolloffInfo* rolloff, float distance) { if (rolloff == NULL) { return 0; } - if (distance <= rolloff->MinDistance) { - return 1; + return 1.f; } + // Logarithmic rolloff has no max distance where it goes silent. if (rolloff->RolloffType == ROLLOFF_Log) - { // Logarithmic rolloff has no max distance where it goes silent. + { return rolloff->MinDistance / (rolloff->MinDistance + rolloff->RolloffFactor * (distance - rolloff->MinDistance)); } if (distance >= rolloff->MaxDistance) { - return 0; + return 0.f; } float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if (rolloff->RolloffType == ROLLOFF_Linear) + { + return volume; + } + if (rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve.Size() > 0) { - volume = S_SoundCurve[int(S_SoundCurve.Size() * (1 - volume))] / 127.f; - } - if (logarithmic) - { - if (rolloff->RolloffType == ROLLOFF_Linear) - { - return volume; - } - else - { - return float((powf(10.f, volume) - 1.) / 9.); - } - } - else - { - if (rolloff->RolloffType == ROLLOFF_Linear) - { - return float(log10(9. * volume + 1.)); - } - else - { - return volume; - } + return S_SoundCurve[int(S_SoundCurve.Size() * (1.f - volume))] / 127.f; } + return (powf(10.f, volume) - 1.f) / 9.f; } - //========================================================================== // // S_ChannelEnded (callback for sound interface code) // //========================================================================== -void S_ChannelEnded(FISoundChannel *ichan) +void SoundEngine::ChannelEnded(FISoundChannel *ichan) { FSoundChan *schan = static_cast(ichan); bool evicted; @@ -1911,7 +1319,7 @@ void S_ChannelEnded(FISoundChannel *ichan) } if (!evicted) { - S_ReturnChannel(schan); + ReturnChannel(schan); } else { @@ -1927,7 +1335,7 @@ void S_ChannelEnded(FISoundChannel *ichan) // //========================================================================== -void S_ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) +void SoundEngine::ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) { FSoundChan *schan = static_cast(ichan); if (is_virtual) @@ -1942,11 +1350,11 @@ void S_ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) //========================================================================== // -// S_StopChannel +// StopChannel // //========================================================================== -void S_StopChannel(FSoundChan *chan) +void SoundEngine::StopChannel(FSoundChan *chan) { if (chan == NULL) return; @@ -1967,23 +1375,292 @@ void S_StopChannel(FSoundChan *chan) } else { - S_ReturnChannel(chan); + ReturnChannel(chan); } } -void S_UnloadAllSounds() +void SoundEngine::UnloadAllSounds() { for (unsigned i = 0; i < S_sfx.Size(); i++) { - S_UnloadSound(&S_sfx[i]); + UnloadSound(&S_sfx[i]); } } -void S_Reset() +void SoundEngine::Reset() { - S_EvictAllChannels(); + EvictAllChannels(); I_CloseSound(); I_InitSound(); - S_RestoreEvictedChannels(); + RestoreEvictedChannels(); } + +//========================================================================== +// +// S_FindSound +// +// Given a logical name, find the sound's index in S_sfx. +//========================================================================== + +int SoundEngine::FindSound(const char* logicalname) +{ + int i; + + if (logicalname != NULL) + { + i = S_sfx[MakeKey(logicalname) % S_sfx.Size()].index; + + while ((i != 0) && stricmp(S_sfx[i].name, logicalname)) + i = S_sfx[i].next; + + return i; + } + else + { + return 0; + } +} + +int SoundEngine::FindSoundByResID(int resid) +{ + auto p = ResIdMap.CheckKey(resid); + return p ? *p : 0; +} + +//========================================================================== +// +// S_FindSoundNoHash +// +// Given a logical name, find the sound's index in S_sfx without +// using the hash table. +//========================================================================== + +int SoundEngine::FindSoundNoHash(const char* logicalname) +{ + unsigned int i; + + for (i = 1; i < S_sfx.Size(); i++) + { + if (stricmp(S_sfx[i].name, logicalname) == 0) + { + return i; + } + } + return 0; +} + +//========================================================================== +// +// S_FindSoundByLump +// +// Given a sound lump, find the sound's index in S_sfx. +//========================================================================== + +int SoundEngine::FindSoundByLump(int lump) +{ + if (lump != -1) + { + unsigned int i; + + for (i = 1; i < S_sfx.Size(); i++) + if (S_sfx[i].lumpnum == lump) + return i; + } + return 0; +} + +//========================================================================== +// +// S_AddSoundLump +// +// Adds a new sound mapping to S_sfx. +//========================================================================== + +int SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid) +{ + S_sfx.Reserve(1); + sfxinfo_t &newsfx = S_sfx.Last(); + + newsfx.data.Clear(); + newsfx.data3d.Clear(); + newsfx.name = logicalname; + newsfx.lumpnum = lump; + newsfx.next = 0; + newsfx.index = 0; + newsfx.Volume = 1; + newsfx.Attenuation = 1; + newsfx.PitchMask = CurrentPitchMask; + newsfx.NearLimit = 2; + newsfx.LimitRange = 256 * 256; + newsfx.bRandomHeader = false; + newsfx.bPlayerReserve = false; + newsfx.bLoadRAW = false; + newsfx.bPlayerCompat = false; + newsfx.b16bit = false; + newsfx.bUsed = false; + newsfx.bSingular = false; + newsfx.bTentative = false; + newsfx.bPlayerSilent = false; + newsfx.ResourceId = resid; + newsfx.RawRate = 0; + newsfx.link = sfxinfo_t::NO_LINK; + newsfx.Rolloff.RolloffType = ROLLOFF_Doom; + newsfx.Rolloff.MinDistance = 0; + newsfx.Rolloff.MaxDistance = 0; + newsfx.LoopStart = -1; + + if (resid >= 0) ResIdMap[resid] = S_sfx.Size() - 1; + return (int)S_sfx.Size()-1; +} + +//========================================================================== +// +// S_FindSoundTentative +// +// Given a logical name, find the sound's index in S_sfx without +// using the hash table. If it does not exist, a new sound without +// an associated lump is created. +//========================================================================== + +int SoundEngine::FindSoundTentative(const char* name) +{ + int id = FindSoundNoHash(name); + if (id == 0) + { + id = AddSoundLump(name, -1, 0); + S_sfx[id].bTentative = true; + } + return id; +} + + +//========================================================================== +// +// S_CacheRandomSound +// +// Loads all sounds a random sound might play. +// +//========================================================================== + +void SoundEngine::CacheRandomSound(sfxinfo_t* sfx) +{ + if (sfx->bRandomHeader) + { + const FRandomSoundList* list = &S_rnd[sfx->link]; + for (unsigned i = 0; i < list->Choices.Size(); ++i) + { + sfx = &S_sfx[list->Choices[i]]; + sfx->bUsed = true; + CacheSound(&S_sfx[list->Choices[i]]); + } + } +} + +//========================================================================== +// +// S_GetSoundMSLength +// +// Returns duration of sound +// GZDoom does not use this due to player sound handling +// +//========================================================================== + +unsigned int SoundEngine::GetMSLength(FSoundID sound) +{ + if ((unsigned int)sound >= S_sfx.Size()) + { + return 0; + } + + sfxinfo_t* sfx = &S_sfx[sound]; + + // Resolve player sounds, random sounds, and aliases + if (sfx->link != sfxinfo_t::NO_LINK) + { + if (sfx->bRandomHeader) + { + // Hm... What should we do here? + // Pick the longest or the shortest sound? + // I think the longest one makes more sense. + + int length = 0; + const FRandomSoundList* list = &S_rnd[sfx->link]; + + for (auto& me : list->Choices) + { + // unfortunately we must load all sounds to find the longest one... :( + int thislen = GetMSLength(me); + if (thislen > length) length = thislen; + } + return length; + } + else + { + sfx = &S_sfx[sfx->link]; + } + } + + sfx = LoadSound(sfx, nullptr); + if (sfx != NULL) return GSnd->GetMSLength(sfx->data); + else return 0; +} + +//========================================================================== +// +// S_PickReplacement +// +// Picks a replacement sound from the associated random list. If this sound +// is not the head of a random list, then the sound passed is returned. +//========================================================================== + +int SoundEngine::PickReplacement(int refid) +{ + while (S_sfx[refid].bRandomHeader) + { + const FRandomSoundList* list = &S_rnd[S_sfx[refid].link]; + refid = list->Choices[rand() % int(list->Choices.Size())]; + } + return refid; +} + +//========================================================================== +// +// S_HashSounds +// +// Fills in the next and index fields of S_sfx to form a working hash table. +//========================================================================== + +void SoundEngine::HashSounds() +{ + unsigned int i; + unsigned int j; + unsigned int size; + + S_sfx.ShrinkToFit(); + size = S_sfx.Size(); + + // Mark all buckets as empty + for (i = 0; i < size; i++) + S_sfx[i].index = 0; + + // Now set up the chains + for (i = 1; i < size; i++) + { + j = MakeKey(S_sfx[i].name) % size; + S_sfx[i].next = S_sfx[j].index; + S_sfx[j].index = i; + } + S_rnd.ShrinkToFit(); +} + +void SoundEngine::AddRandomSound(int Owner, TArray list) +{ + auto index = S_rnd.Reserve(1); + auto& random = S_rnd.Last(); + random.Choices = std::move(list); + random.Owner = Owner; + S_sfx[Owner].link = index; + S_sfx[Owner].bRandomHeader = true; + S_sfx[Owner].NearLimit = -1; +} diff --git a/src/sound/s_sound.h b/src/sound/s_sound.h index 18ab9c2793..dc8c06d493 100644 --- a/src/sound/s_sound.h +++ b/src/sound/s_sound.h @@ -28,7 +28,6 @@ #ifndef __S_SOUND__ #define __S_SOUND__ -#include "doomtype.h" #include "i_soundinternal.h" class AActor; @@ -37,58 +36,36 @@ class FSerializer; struct FLevelLocals; #include "s_soundinternal.h" +#include "s_doomsound.h" // Per level startup code. // Kills playing sounds at start of level and starts new music. // -void S_Start (); // Called after a level is loaded. Ensures that most sounds are loaded. -void S_PrecacheLevel (FLevelLocals *l); - -// Start sound for thing at -void S_Sound (int channel, FSoundID sfxid, float volume, float attenuation); - -void S_SoundPitch (int channel, FSoundID sfxid, float volume, float attenuation, float pitch); struct FSoundLoadBuffer; -int S_PickReplacement (int refid); -void S_CacheRandomSound (sfxinfo_t *sfx); - -// Stop sound for all channels -void S_StopAllChannels (void); -void S_SetPitch(FSoundChan *chan, float dpitch); - -void S_RestoreEvictedChannels(); // [RH] S_sfx "maintenance" routines void S_ClearSoundData(); void S_ParseSndInfo (bool redefine); -void S_HashSounds (); -int S_FindSoundNoHash (const char *logicalname); bool S_AreSoundsEquivalent (AActor *actor, int id1, int id2); bool S_AreSoundsEquivalent (AActor *actor, const char *name1, const char *name2); int S_LookupPlayerSound (const char *playerclass, int gender, const char *logicalname); int S_LookupPlayerSound (const char *playerclass, int gender, FSoundID refid); int S_FindSkinnedSound (AActor *actor, FSoundID refid); int S_FindSkinnedSoundEx (AActor *actor, const char *logicalname, const char *extendedname); -int S_FindSoundByLump (int lump); int S_AddSound (const char *logicalname, const char *lumpname, FScanner *sc=NULL); // Add sound by lumpname -int S_AddSoundLump (const char *logicalname, int lump); // Add sound by lump index int S_AddPlayerSound (const char *playerclass, const int gender, int refid, const char *lumpname); int S_AddPlayerSound (const char *playerclass, const int gender, int refid, int lumpnum, bool fromskin=false); int S_AddPlayerSoundExisting (const char *playerclass, const int gender, int refid, int aliasto, bool fromskin=false); void S_MarkPlayerSounds (AActor *player); void S_ShrinkPlayerSoundLists (); -void S_UnloadSound (sfxinfo_t *sfx); -sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer = nullptr); unsigned int S_GetMSLength(FSoundID sound); // [RH] Prints sound debug info to the screen. // Modelled after Hexen's noise cheat. -void S_NoiseDebug (); -#include "s_doomsound.h" #endif diff --git a/src/sound/s_soundinternal.h b/src/sound/s_soundinternal.h index b8af56ef7d..86eac0cdbe 100644 --- a/src/sound/s_soundinternal.h +++ b/src/sound/s_soundinternal.h @@ -1,7 +1,14 @@ #pragma once -#include "i_soundinternal.h" +#include "i_sound.h" +struct FRandomSoundList +{ + TArray Choices; + uint32_t Owner = 0; +}; + +extern int sfx_empty; // // SoundFX struct. @@ -21,19 +28,20 @@ struct sfxinfo_t unsigned int next, index; // [RH] For hashing float Volume; - uint8_t ResourceId; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. + int ResourceId; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. uint8_t PitchMask; int16_t NearLimit; // 0 means unlimited float LimitRange; // Range for sound limiting (squared for faster computations) unsigned bRandomHeader:1; - unsigned bPlayerReserve:1; unsigned bLoadRAW:1; - unsigned bPlayerCompat:1; unsigned b16bit:1; unsigned bUsed:1; unsigned bSingular:1; + unsigned bTentative:1; + unsigned bPlayerReserve : 1; + unsigned bPlayerCompat : 1; unsigned bPlayerSilent:1; // This player sound is intentionally silent. int RawRate; // Sample rate to use when bLoadRAW is true @@ -61,9 +69,6 @@ enum int S_FindSound(const char *logicalname); int S_FindSoundByResID(int snd_id); -// the complete set of sound effects -extern TArray S_sfx; - // An index into the S_sfx[] array. class FSoundID { @@ -110,18 +115,6 @@ public: { return ID; } - operator FString() const - { - return ID ? S_sfx[ID].name : ""; - } - operator const char *() const - { - return ID ? S_sfx[ID].name.GetChars() : NULL; - } - void MarkUsed() const - { - S_sfx[ID].MarkUsed(); - } private: int ID; protected: @@ -136,13 +129,8 @@ public: using FSoundID::operator=; }; -extern FRolloffInfo S_Rolloff; -extern TArray S_SoundCurve; -class AActor; -struct sector_t; -struct FPolyObj; struct FSoundChan : public FISoundChannel { FSoundChan *NextChan; // Next channel in this list. @@ -163,25 +151,6 @@ struct FSoundChan : public FISoundChannel }; }; -extern FSoundChan *Channels; - -void S_ReturnChannel(FSoundChan *chan); -void S_EvictAllChannels(); - -void S_StopChannel(FSoundChan *chan); -void S_LinkChannel(FSoundChan *chan, FSoundChan **head); -void S_UnlinkChannel(FSoundChan *chan); - -// Initializes sound stuff, including volume -// Sets channels, SFX and music volume, -// allocates channel buffer, sets S_sfx lookup. -// -void S_Init (); -void S_InitData (); -void S_Shutdown (); - -// Loads a sound, including any random sounds it might reference. -void S_CacheSound (sfxinfo_t *sfx); // sound channels // channel 0 never willingly overrides @@ -237,9 +206,6 @@ enum #define ATTN_IDLE 1.001f #define ATTN_STATIC 3.f // diminish very rapidly with distance -// Checks if a copy of this sound is already playing. -bool S_CheckSingular (int sound_id); - enum // This cannot be remain as this, but for now it has to suffice. { SOURCE_None, // Sound is always on top of the listener. @@ -250,31 +216,6 @@ enum // This cannot be remain as this, but for now it has to suffice. }; -// -// Updates music & sounds -// -void S_UpdateSounds (int time); - -FSoundChan* S_StartSound(int sourcetype, const void* source, - const FVector3* pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f); - -// Stops an origin-less sound from playing from this channel. -void S_StopSound(int channel); -void S_StopSound(int sourcetype, const void* actor, int channel); - -void S_RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); -void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume); -void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch); -bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id); - -// Stop and resume music, during game PAUSE. -void S_PauseSound (bool notmusic, bool notsfx); -void S_ResumeSound (bool notsfx); -void S_SetSoundPaused (int state); -bool S_GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id); -void S_UnloadAllSounds(); -void S_Reset(); - extern ReverbContainer *Environments; extern ReverbContainer *DefaultEnvironments[26]; @@ -285,3 +226,163 @@ ReverbContainer *S_FindEnvironment (const char *name); ReverbContainer *S_FindEnvironment (int id); void S_AddEnvironment (ReverbContainer *settings); +class SoundEngine +{ +protected: + bool SoundPaused = false; // whether sound is paused + int RestartEvictionsAt = 0; // do not restart evicted channels before this time + SoundListener listener{}; + + FSoundChan* Channels = nullptr; + FSoundChan* FreeChannels = nullptr; + + // the complete set of sound effects + TArray S_sfx; + FRolloffInfo S_Rolloff; + TArray S_SoundCurve; + TMap ResIdMap; + TArray S_rnd; + +private: + void LoadSound3D(sfxinfo_t* sfx, FSoundLoadBuffer* pBuffer); + void LinkChannel(FSoundChan* chan, FSoundChan** head); + void UnlinkChannel(FSoundChan* chan); + void ReturnChannel(FSoundChan* chan); + void RestartChannel(FSoundChan* chan); + void RestoreEvictedChannel(FSoundChan* chan); + + bool IsChannelUsed(int sourcetype, const void* actor, int channel, int* seen); + // This is the actual sound positioning logic which needs to be provided by the client. + virtual void CalcPosVel(int type, const void* source, const float pt[3], int channel, int chanflags, FVector3* pos, FVector3* vel) = 0; + // This can be overridden by the clent to provide some diagnostics. The default lets everything pass. + virtual bool ValidatePosVel(int sourcetype, const void* source, const FVector3& pos, const FVector3& vel) { return true; } + + bool ValidatePosVel(const FSoundChan* const chan, const FVector3& pos, const FVector3& vel); + + // Checks if a copy of this sound is already playing. + bool CheckSingular(int sound_id); + bool CheckSoundLimit(sfxinfo_t* sfx, const FVector3& pos, int near_limit, float limit_range, int sourcetype, const void* actor, int channel); + virtual TArray ReadSound(int lumpnum) = 0; + +public: + virtual ~SoundEngine() = default; + void EvictAllChannels(); + + void StopChannel(FSoundChan* chan); + sfxinfo_t* LoadSound(sfxinfo_t* sfx, FSoundLoadBuffer* pBuffer); + + // Initializes sound stuff, including volume + // Sets channels, SFX and music volume, + // allocates channel buffer, sets S_sfx lookup. + // + void Init(TArray &sndcurve); + void InitData(); + void Shutdown(); + + void StopAllChannels(void); + void SetPitch(FSoundChan* chan, float dpitch); + + FSoundChan* GetChannel(void* syschan); + void RestoreEvictedChannels(); + void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); + + // Loads a sound, including any random sounds it might reference. + void CacheSound(sfxinfo_t* sfx); + void CacheSound(int sfx) { CacheSound(&S_sfx[sfx]); } + void UnloadSound(sfxinfo_t* sfx); + + void UpdateSounds(int time); + + FSoundChan* StartSound(int sourcetype, const void* source, + const FVector3* pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f); + + // Stops an origin-less sound from playing from this channel. + void StopSound(int channel); + void StopSound(int sourcetype, const void* actor, int channel); + + void RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); + void ChangeSoundVolume(int sourcetype, const void* source, int channel, double dvolume); + void ChangeSoundPitch(int sourcetype, const void* source, int channel, double pitch); + bool IsSourcePlayingSomething(int sourcetype, const void* actor, int channel, int sound_id); + + // Stop and resume music, during game PAUSE. + bool GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id); + void UnloadAllSounds(); + void Reset(); + void MarkUsed(int num); + void CacheMarkedSounds(); + TArray AllActiveChannels(); + + void MarkAllUnused() + { + for (auto & s: S_sfx) s.bUsed = false; + } + + bool isListener(const void* object) const + { + return object && listener.ListenerObject == object; + } + bool isPlayerReserve(int snd_id) + { + return S_sfx[snd_id].bPlayerReserve; // Later this needs to be abstracted out of the engine itself. Right now that cannot be done. + } + void SetListener(SoundListener& l) + { + listener = l; + } + void SetRestartTime(int time) + { + RestartEvictionsAt = time; + } + void SetPaused(bool on) + { + SoundPaused = on; + } + FSoundChan* GetChannels() + { + return Channels; + } + const char *GetSoundName(FSoundID id) + { + return id == 0 ? "" : S_sfx[id].name; + } + TArray &GetSounds() //Thio should only be used for constructing the sound list or for diagnostics code prinring information about the sound list. + { + return S_sfx; + } + FRolloffInfo& GlobalRolloff() // like GetSounds this is meant for sound list generators, not for gaining cheap access to the sound engine's innards. + { + return S_Rolloff; + } + FRandomSoundList *ResolveRandomSound(sfxinfo_t* sfx) + { + return &S_rnd[sfx->link]; + } + void ClearRandoms() + { + S_rnd.Clear(); + } + + void ChannelVirtualChanged(FISoundChannel* ichan, bool is_virtual); + FString ListSoundChannels(); + + // Allow this to be overridden for special needs. + virtual float GetRolloff(const FRolloffInfo* rolloff, float distance); + virtual void ChannelEnded(FISoundChannel* ichan); // allows the client to do bookkeeping on the sound. + + // Lookup utilities. + int FindSound(const char* logicalname); + int FindSoundByResID(int rid); + int FindSoundNoHash(const char* logicalname); + int FindSoundByLump(int lump); + int AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid = -1); + int FindSoundTentative(const char* name); + void CacheRandomSound(sfxinfo_t* sfx); + unsigned int GetMSLength(FSoundID sound); + int PickReplacement(int refid); + void HashSounds(); + void AddRandomSound(int Owner, TArray list); +}; + + +extern SoundEngine* soundEngine; diff --git a/src/utility/superfasthash.h b/src/utility/superfasthash.h new file mode 100644 index 0000000000..d6b7dae0c5 --- /dev/null +++ b/src/utility/superfasthash.h @@ -0,0 +1,5 @@ +#pragma once + +extern unsigned int MakeKey (const char *s); +extern unsigned int MakeKey (const char *s, size_t len); +extern unsigned int SuperFastHash (const char *data, size_t len); diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 95b162a7a8..dc52b342cc 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -436,6 +436,8 @@ class Actor : Thinker native MarkSound(BounceSound); MarkSound(WallBounceSound); MarkSound(CrushPainSound); + MarkSound(HowlSound); + MarkSound(MeleeSound); } bool IsPointerEqual(int ptr_select1, int ptr_select2)