diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc60d5619..fdfb5d0ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -878,7 +878,9 @@ set (PCH_SOURCES rendering/r_videoscale.cpp sound/s_advsound.cpp sound/s_environment.cpp + sound/s_reverbedit.cpp sound/s_sndseq.cpp + sound/s_doomsound.cpp sound/s_sound.cpp sound/s_music.cpp serializer.cpp diff --git a/src/console/c_dispatch.cpp b/src/console/c_dispatch.cpp index abd851b35..1921e2be3 100644 --- a/src/console/c_dispatch.cpp +++ b/src/console/c_dispatch.cpp @@ -1134,6 +1134,11 @@ FString BuildString (int argc, FString *argv) // //=========================================================================== +void FConsoleCommand::PrintCommand() +{ + Printf("%s\n", m_Name); +} + FString SubstituteAliasParams (FString &command, FCommandLine &args) { // Do substitution by replacing %x with the argument x. diff --git a/src/console/c_dispatch.h b/src/console/c_dispatch.h index e21e977d1..88976d95f 100644 --- a/src/console/c_dispatch.h +++ b/src/console/c_dispatch.h @@ -34,7 +34,9 @@ #ifndef __C_DISPATCH_H__ #define __C_DISPATCH_H__ -#include "doomtype.h" +#include +#include "tarray.h" +#include "zstring.h" class FConfigFile; @@ -63,7 +65,7 @@ struct FExecList TArray Commands; TArray Pullins; - void AddCommand(const char *cmd, const char *file = NULL); + void AddCommand(const char *cmd, const char *file = nullptr); void ExecCommands() const; void AddPullins(TArray &wads) const; }; @@ -107,7 +109,7 @@ public: FConsoleCommand (const char *name, CCmdRun RunFunc); virtual ~FConsoleCommand (); virtual bool IsAlias (); - void PrintCommand () { Printf ("%s\n", m_Name); } + void PrintCommand(); virtual void Run (FCommandLine &args, AActor *instigator, int key); static FConsoleCommand* FindByName (const char* name); @@ -208,9 +210,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/console/c_expr.cpp b/src/console/c_expr.cpp index 0f5cb9c4f..cde8527e2 100644 --- a/src/console/c_expr.cpp +++ b/src/console/c_expr.cpp @@ -40,6 +40,7 @@ #include "c_dispatch.h" #include "c_cvars.h" +#include "doomtype.h" // MACROS ------------------------------------------------------------------ diff --git a/src/gamedata/fonts/v_text.h b/src/gamedata/fonts/v_text.h index 365674b52..cb16ebb74 100644 --- a/src/gamedata/fonts/v_text.h +++ b/src/gamedata/fonts/v_text.h @@ -34,7 +34,6 @@ #ifndef __V_TEXT_H__ #define __V_TEXT_H__ -#include "doomtype.h" #include "v_font.h" struct FBrokenLines diff --git a/src/playsim/fragglescript/t_func.cpp b/src/playsim/fragglescript/t_func.cpp index ee41d0d9c..5d3917b31 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 eec8e752e..cbcfb0fc6 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) @@ -5953,7 +5953,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) if (args[0] == 0) { - S_ChangeSoundVolume(activator, chan, volume); + S_ChangeActorSoundVolume(activator, chan, volume); } else { @@ -5962,7 +5962,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) while ((spot = it.Next()) != NULL) { - S_ChangeSoundVolume(spot, chan, volume); + S_ChangeActorSoundVolume(spot, chan, volume); } } } diff --git a/src/r_data/r_vanillatrans.cpp b/src/r_data/r_vanillatrans.cpp index 6bf897951..ff2584e58 100644 --- a/src/r_data/r_vanillatrans.cpp +++ b/src/r_data/r_vanillatrans.cpp @@ -28,6 +28,7 @@ #include "templates.h" #include "c_cvars.h" #include "w_wad.h" +#include "doomtype.h" #ifdef _DEBUG #include "c_dispatch.h" #endif diff --git a/src/r_data/sprites.cpp b/src/r_data/sprites.cpp index eecb27dd2..2bab76277 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 ac5f68163..f5f178cfa 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 269de5cc9..aee0b3b0a 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 ca584a326..f9383eaba 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 1d3e6411f..08a398f9e 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/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index e2548489f..7ad73c7e8 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -142,21 +142,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_StopSound, NativeStopSound) return 0; } -DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundPitch, S_ChangeSoundPitch) +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundPitch, S_ChangeActorSoundPitch) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(channel); PARAM_FLOAT(pitch); - S_ChangeSoundPitch(self, channel, pitch); + S_ChangeActorSoundPitch(self, channel, pitch); return 0; } -DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundVolume, S_ChangeSoundVolume) +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundVolume, S_ChangeActorSoundVolume) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(channel); PARAM_FLOAT(volume); - S_ChangeSoundVolume(self, channel, volume); + S_ChangeActorSoundVolume(self, channel, volume); return 0; } diff --git a/src/serializer.cpp b/src/serializer.cpp index f6829d3de..57ce752cb 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 94b1a860e..0ba595ac7 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" @@ -49,9 +47,9 @@ #include "v_text.h" #include "c_cvars.h" #include "stats.h" -#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) @@ -260,20 +258,12 @@ void I_InitSound () return; } -#ifndef NO_OPENAL - // Simplify transition to OpenAL backend - if (stricmp(snd_backend, "fmod") == 0) - { - Printf (TEXTCOLOR_ORANGE "FMOD Ex sound system was removed, switching to OpenAL\n"); - snd_backend = "openal"; - } -#endif // NO_OPENAL - + // Keep it simple: let everything except "null" init the sound. if (stricmp(snd_backend, "null") == 0) { GSnd = new NullSoundRenderer; } - else if(stricmp(snd_backend, "openal") == 0) + else { #ifndef NO_OPENAL if (IsOpenALPresent()) @@ -282,11 +272,6 @@ void I_InitSound () } #endif } - else - { - Printf (TEXTCOLOR_RED"%s: Unknown sound system specified\n", *snd_backend); - snd_backend = "null"; - } if (!GSnd || !GSnd->IsValid ()) { I_CloseSound(); @@ -300,8 +285,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 eedcb18b2..83f5fc2c6 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 75874b6c3..99dc1badb 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -3,7 +3,6 @@ #include -#include "doomtype.h" #include "vectors.h" #include "tarray.h" #include "zmusic/sounddecoder.h" @@ -77,6 +76,7 @@ struct SoundListener bool underwater; bool valid; ReverbContainer *Environment; + void* ListenerObject; }; // Default rolloff information. @@ -121,4 +121,5 @@ class SoundStream; + #endif diff --git a/src/sound/backend/oalsound.cpp b/src/sound/backend/oalsound.cpp index 55df4b982..3f3ede72f 100644 --- a/src/sound/backend/oalsound.cpp +++ b/src/sound/backend/oalsound.cpp @@ -35,17 +35,18 @@ #include #include -#include "doomstat.h" +#include "c_cvars.h" #include "templates.h" #include "oalsound.h" #include "c_dispatch.h" #include "v_text.h" #include "i_module.h" #include "cmdlib.h" -#include "menu/menu.h" +#include "m_fixed.h" #include "zmusic/sounddecoder.h" #include "filereadermusicinterface.h" + const char *GetSampleTypeName(SampleType type); const char *GetChannelConfigName(ChannelConfig chan); @@ -96,58 +97,6 @@ bool IsOpenALPresent() -void I_BuildALDeviceList(FOptionValues *opt) -{ - opt->mValues.Resize(1); - opt->mValues[0].TextValue = "Default"; - opt->mValues[0].Text = "Default"; - -#ifndef NO_OPENAL - if (IsOpenALPresent()) - { - const ALCchar *names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? - alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : - alcGetString(NULL, ALC_DEVICE_SPECIFIER)); - if (!names) - Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); - else while (*names) - { - unsigned int i = opt->mValues.Reserve(1); - opt->mValues[i].TextValue = names; - opt->mValues[i].Text = names; - - names += strlen(names) + 1; - } - } -#endif -} - -void I_BuildALResamplersList(FOptionValues *opt) -{ - opt->mValues.Resize(1); - opt->mValues[0].TextValue = "Default"; - opt->mValues[0].Text = "Default"; - -#ifndef NO_OPENAL - if (!IsOpenALPresent()) - return; - if (!alcGetCurrentContext() || !alIsExtensionPresent("AL_SOFT_source_resampler")) - return; - - LPALGETSTRINGISOFT alGetStringiSOFT = reinterpret_cast(alGetProcAddress("alGetStringiSOFT")); - ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT); - - unsigned int idx = opt->mValues.Reserve(num_resamplers); - for(ALint i = 0;i < num_resamplers;++i) - { - const ALchar *name = alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i); - opt->mValues[idx].TextValue = name; - opt->mValues[idx].Text = name; - ++idx; - } -#endif -} - ReverbContainer *ForcedEnvironment; @@ -544,22 +493,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 +917,7 @@ void OpenALSoundRenderer::SetSfxVolume(float volume) { SfxVolume = volume; - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel != NULL) @@ -1359,7 +1293,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 +1429,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 +1640,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 +1699,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 +1723,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 +1943,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 +1956,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) } } - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2047,7 +1981,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 +1994,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) } } - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2236,7 +2170,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 +2292,7 @@ void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) FSoundChan *OpenALSoundRenderer::FindLowestChannel() { - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); FSoundChan *lowest = NULL; while(schan) { @@ -2374,4 +2308,60 @@ FSoundChan *OpenALSoundRenderer::FindLowestChannel() return lowest; } + +#include "menu/menu.h" + +void I_BuildALDeviceList(FOptionValues* opt) +{ + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; + +#ifndef NO_OPENAL + if (IsOpenALPresent()) + { + const ALCchar* names = (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ? + alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER) : + alcGetString(NULL, ALC_DEVICE_SPECIFIER)); + if (!names) + Printf("Failed to get device list: %s\n", alcGetString(NULL, alcGetError(NULL))); + else while (*names) + { + unsigned int i = opt->mValues.Reserve(1); + opt->mValues[i].TextValue = names; + opt->mValues[i].Text = names; + + names += strlen(names) + 1; + } + } +#endif +} + +void I_BuildALResamplersList(FOptionValues* opt) +{ + opt->mValues.Resize(1); + opt->mValues[0].TextValue = "Default"; + opt->mValues[0].Text = "Default"; + +#ifndef NO_OPENAL + if (!IsOpenALPresent()) + return; + if (!alcGetCurrentContext() || !alIsExtensionPresent("AL_SOFT_source_resampler")) + return; + + LPALGETSTRINGISOFT alGetStringiSOFT = reinterpret_cast(alGetProcAddress("alGetStringiSOFT")); + ALint num_resamplers = alGetInteger(AL_NUM_RESAMPLERS_SOFT); + + unsigned int idx = opt->mValues.Reserve(num_resamplers); + for (ALint i = 0; i < num_resamplers; ++i) + { + const ALchar* name = alGetStringiSOFT(AL_RESAMPLER_NAME_SOFT, i); + opt->mValues[idx].TextValue = name; + opt->mValues[idx].Text = name; + ++idx; + } +#endif +} + + #endif // NO_OPENAL diff --git a/src/sound/s_advsound.cpp b/src/sound/s_advsound.cpp index 19e55ef13..113cb36d7 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 new file mode 100644 index 000000000..a9a5eb54c --- /dev/null +++ b/src/sound/s_doomsound.cpp @@ -0,0 +1,1399 @@ +/* +** doomspund.cpp +** +** Game dependent part of the sound engine. +** +**--------------------------------------------------------------------------- +** +** Copyright 1999-2016 Randy Heit +** Copyright 2002-2019 Christoph Oelckers +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#include +#include +#ifdef _WIN32 +#include +#endif + +#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" + +// 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); +} + + +//========================================================================== +// +// S_Sound - Unpositioned version +// +//========================================================================== + +void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float 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) +{ + soundEngine->StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); +} + +DEFINE_ACTION_FUNCTION(DObject, S_Sound) +{ + PARAM_PROLOGUE; + PARAM_SOUND(id); + PARAM_INT(channel); + PARAM_FLOAT(volume); + PARAM_FLOAT(attn); + PARAM_FLOAT(pitch); + S_SoundPitch(channel, id, static_cast(volume), static_cast(attn), static_cast(pitch)); + 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 (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) +{ + S_SoundPitchActor(ent, channel, sound_id, volume, attenuation, 0.f); +} + +//========================================================================== +// +// S_SoundMinMaxDist - An actor is source +// +// Attenuation is specified as min and max distances, rather than a scalar. +// +//========================================================================== + +void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume, float mindist, float maxdist) +{ + if (VerifyActorSound(ent, sound_id, channel)) + { + FRolloffInfo rolloff; + + rolloff.RolloffType = ROLLOFF_Linear; + rolloff.MinDistance = mindist; + rolloff.MaxDistance = maxdist; + soundEngine->StartSound(SOURCE_Actor, ent, nullptr, channel, sound_id, volume, 1, &rolloff); + } +} + +//========================================================================== +// +// S_Sound - A polyobject is source +// +//========================================================================== + +void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation) +{ + if (poly->Level != primaryLevel) return; + soundEngine->StartSound (SOURCE_Polyobj, poly, nullptr, channel, sound_id, volume, attenuation); +} + +//========================================================================== +// +// S_Sound - A point is source +// +//========================================================================== + +void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sound_id, float volume, float attenuation) +{ + if (Level != primaryLevel) return; + // The sound system switches Y and Z around. + FVector3 p((float)pos.X, (float)pos.Z, (float)pos.Y); + soundEngine->StartSound (SOURCE_Unattached, nullptr, &p, channel, sound_id, volume, attenuation); +} + +//========================================================================== +// +// S_Sound - An entire sector is source +// +//========================================================================== + +void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation) +{ + if (sec->Level != primaryLevel) return; + soundEngine->StartSound (SOURCE_Sector, sec, nullptr, channel, sfxid, volume, attenuation); +} + +//========================================================================== +// +// S_PlaySound - Subfunction used by ACS and DECORATE +// +// Has a local parameter to make the sound audible only to the source +// +//========================================================================== + +void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch) +{ + if (a == nullptr || a->Sector->Flags & SECF_SILENT || a->Level != primaryLevel) + return; + + if (!local) + { + S_SoundPitchActor(a, chan, sid, vol, atten, pitch); + } + else + { + if (a->CheckLocalView()) + { + S_SoundPitch(chan, sid, vol, ATTN_NONE, pitch); + } + } +} + +void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local) +{ + S_PlaySoundPitch(a, chan, sid, vol, atten, local, 0.f); +} + +void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch) +{ + if (!looping) + { + if (!(channel & CHAN_NOSTOP) || !S_IsActorPlayingSomething(self, channel & 7, soundid)) + { + S_PlaySoundPitch(self, channel, soundid, (float)volume, (float)attenuation, local, (float)pitch); + } + } + else + { + if (!S_IsActorPlayingSomething(self, channel & 7, soundid)) + { + S_PlaySoundPitch(self, channel | CHAN_LOOP, soundid, (float)volume, (float)attenuation, local, (float)pitch); + } + } +} + + +//========================================================================== +// +// S_StopSound +// +// Stops a sound from a single actor from playing on a specific channel. +// +//========================================================================== + +void S_StopSound (AActor *actor, int channel) +{ + soundEngine->StopSound(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + + +//========================================================================== +// +// S_StopSound +// +// Stops a sound from a single sector from playing on a specific channel. +// +//========================================================================== + +void S_StopSound (const sector_t *sec, int channel) +{ + soundEngine->StopSound(SOURCE_Sector, sec, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + +//========================================================================== +// +// S_StopSound +// +// Stops a sound from a single polyobject from playing on a specific channel. +// +//========================================================================== + +void S_StopSound (const FPolyObj *poly, int channel) +{ + soundEngine->StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + +//========================================================================== +// +// S_RelinkSound +// +// Moves all the sounds from one thing to another. If the destination is +// nullptr, then the sound becomes a positioned sound. +//========================================================================== + +void S_RelinkSound (AActor *from, AActor *to) +{ + + FVector3 p = from->SoundPos(); + soundEngine->RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); +} + +//========================================================================== +// +// S_ChangeSoundVolume +// +//========================================================================== + +void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) +{ + soundEngine->ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); +} + +//========================================================================== +// +// S_ChangeSoundPitch +// +//========================================================================== + +void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) +{ + soundEngine->ChangeSoundPitch(SOURCE_Actor, actor, channel, pitch); +} + +//========================================================================== +// +// S_GetSoundPlayingInfo +// +// Is a sound being played by a specific emitter? +//========================================================================== + +bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) +{ + return soundEngine->GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); +} + +bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) +{ + return soundEngine->GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); +} + +bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) +{ + return soundEngine->GetSoundPlayingInfo(SOURCE_Polyobj, poly, sound_id); +} + + //========================================================================== +// +// S_IsActorPlayingSomething +// +//========================================================================== + +bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) +{ + if (compatflags & COMPATF_MAGICSILENCE) + { + channel = CHAN_AUTO; // checks all channels + } + return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); +} + +//========================================================================== +// +// Sets the internal listener structure +// +//========================================================================== + +static void S_SetListener(AActor *listenactor) +{ + SoundListener listener; + if (listenactor != nullptr) + { + listener.angle = (float)listenactor->Angles.Yaw.Radians(); + /* + listener.velocity.X = listenactor->vel.x * (TICRATE/65536.f); + listener.velocity.Y = listenactor->vel.z * (TICRATE/65536.f); + listener.velocity.Z = listenactor->vel.y * (TICRATE/65536.f); + */ + listener.velocity.Zero(); + listener.position = listenactor->SoundPos(); + listener.underwater = listenactor->waterlevel == 3; + assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber); + listener.Environment = primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment; + listener.valid = true; + } + else + { + listener.angle = 0; + listener.position.Zero(); + listener.velocity.Zero(); + listener.underwater = false; + listener.Environment = nullptr; + listener.valid = false; + } + listener.ListenerObject = listenactor; + soundEngine->SetListener(listener); +} + +//========================================================================== +// +// S_UpdateSounds +// +// Updates music & sounds +//========================================================================== + +void S_UpdateSounds (AActor *listenactor) +{ + // should never happen + S_SetListener(listenactor); + + for (auto Level : AllLevels()) + { + SN_UpdateActiveSequences(Level); + } + + soundEngine->UpdateSounds(primaryLevel->time); +} + +//========================================================================== +// +// Although saving the sound system's state is supposed to be an engine +// feature, the specifics cannot be set up there, this needs to be on the client side. +// +//========================================================================== + +static FSerializer &Serialize(FSerializer &arc, const char *key, FSoundChan &chan, FSoundChan *def) +{ + if (arc.BeginObject(key)) + { + arc("sourcetype", chan.SourceType) + ("soundid", chan.SoundID) + ("orgid", chan.OrgID) + ("volume", chan.Volume) + ("distancescale", chan.DistanceScale) + ("pitch", chan.Pitch) + ("chanflags", chan.ChanFlags) + ("entchannel", chan.EntChannel) + ("priority", chan.Priority) + ("nearlimit", chan.NearLimit) + ("starttime", chan.StartTime) + ("rolloftype", chan.Rolloff.RolloffType) + ("rolloffmin", chan.Rolloff.MinDistance) + ("rolloffmax", chan.Rolloff.MaxDistance) + ("limitrange", chan.LimitRange); + + switch (chan.SourceType) + { + case SOURCE_None: break; + case SOURCE_Actor: { AActor* s = (AActor*)chan.Source; arc("actor", s); chan.Source = s; break; } + case SOURCE_Sector: { auto s = (sector_t*)chan.Source; arc("sector", s); chan.Source = s; break; } + case SOURCE_Polyobj: { auto s = (FPolyObj*)chan.Source; arc("poly", s); chan.Source = s; break; } + case SOURCE_Unattached: arc.Array("point", chan.Point, 3); break; + default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; + } + arc.EndObject(); + } + return arc; +} + +//========================================================================== +// +// S_SerializeSounds +// +//========================================================================== + +void S_SerializeSounds(FSerializer &arc) +{ + FSoundChan *chan; + + GSnd->Sync(true); + + if (arc.isWriting()) + { + // Count channels and accumulate them so we can store them in + // reverse order. That way, they will be in the same order when + // reloaded later as they are now. + TArray chans = soundEngine->AllActiveChannels(); + + if (chans.Size() > 0 && arc.BeginArray("sounds")) + { + for (unsigned int i = chans.Size(); i-- != 0; ) + { + // Replace start time with sample position. + uint64_t start = chans[i]->StartTime; + chans[i]->StartTime = GSnd ? GSnd->GetPosition(chans[i]) : 0; + arc(nullptr, *chans[i]); + chans[i]->StartTime = start; + } + arc.EndArray(); + } + } + else + { + unsigned int count; + + soundEngine->StopAllChannels(); + if (arc.BeginArray("sounds")) + { + count = arc.ArraySize(); + for (unsigned int i = 0; i < count; ++i) + { + chan = (FSoundChan*)soundEngine->GetChannel(nullptr); + arc(nullptr, *chan); + // Sounds always start out evicted when restored from a save. + chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; + } + arc.EndArray(); + } + // The two tic delay is to make sure any screenwipes have finished. + // This needs to be two because the game is run for one tic before + // the wipe so that it can produce a screen to wipe to. So if we + // only waited one tic to restart the sounds, they would start + // 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. + 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(); +} + +//========================================================================== +// +// CCMD playsound +// +//========================================================================== + +CCMD (playsound) +{ + if (argv.argc() > 1) + { + FSoundID id = argv[1]; + if (id == 0) + { + Printf("'%s' is not a sound\n", argv[1]); + } + else + { + S_Sound (CHAN_AUTO | CHAN_UI, id, 1.f, ATTN_NONE); + } + } +} + +//========================================================================== +// +// CCMD loopsound +// +//========================================================================== + +CCMD (loopsound) +{ + if (players[consoleplayer].mo != nullptr && !netgame && argv.argc() > 1) + { + FSoundID id = argv[1]; + if (id == 0) + { + Printf("'%s' is not a sound\n", argv[1]); + } + else + { + AActor *icon = Spawn(primaryLevel, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE); + if (icon != nullptr) + { + S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE); + } + } + } +} + +//========================================================================== +// +// CCMD cachesound +// +//========================================================================== + +CCMD (cachesound) +{ + if (argv.argc() < 2) + { + Printf ("Usage: cachesound ...\n"); + return; + } + for (int i = 1; i < argv.argc(); ++i) + { + FSoundID sfxnum = argv[i]; + if (sfxnum != FSoundID(0)) + { + soundEngine->CacheSound(sfxnum); + } + } +} + + +CCMD(listsoundchannels) +{ + Printf("%s", soundEngine->ListSoundChannels().GetChars()); +} + +// intentionally moved here to keep the s_music include out of the rest of the file. + +//========================================================================== +// +// S_PauseSound +// +// Stop music and sound effects, during game PAUSE. +//========================================================================== +#include "s_music.h" + +void S_PauseSound (bool notmusic, bool notsfx) +{ + if (!notmusic) + { + S_PauseMusic(); + } + if (!notsfx) + { + soundEngine->SetPaused(true); + GSnd->SetSfxPaused (true, 0); + } +} + +DEFINE_ACTION_FUNCTION(DObject, S_PauseSound) +{ + PARAM_PROLOGUE; + PARAM_BOOL(notmusic); + PARAM_BOOL(notsfx); + S_PauseSound(notmusic, notsfx); + return 0; +} + +//========================================================================== +// +// S_ResumeSound +// +// Resume music and sound effects, after game PAUSE. +//========================================================================== + +void S_ResumeSound (bool notsfx) +{ + S_ResumeMusic(); + if (!notsfx) + { + soundEngine->SetPaused(false); + GSnd->SetSfxPaused (false, 0); + } +} + +DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound) +{ + PARAM_PROLOGUE; + PARAM_BOOL(notsfx); + S_ResumeSound(notsfx); + return 0; +} + + + +CCMD (snd_status) +{ + GSnd->PrintStatus (); +} + +CCMD (snd_reset) +{ + S_SoundReset(); +} + +void S_SoundReset() +{ + S_StopMusic(true); + soundEngine->Reset(); + S_RestartMusic(); +} + +CCMD (snd_listdrivers) +{ + GSnd->PrintDriversList (); +} + +ADD_STAT (sound) +{ + return GSnd->GatherStats (); +} diff --git a/src/sound/s_doomsound.h b/src/sound/s_doomsound.h new file mode 100644 index 000000000..e36cf6030 --- /dev/null +++ b/src/sound/s_doomsound.h @@ -0,0 +1,89 @@ +#pragma once + +// 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); +void S_Sound (const FPolyObj *poly, int channel, FSoundID sfxid, float volume, float attenuation); +void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation); +void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sfxid, float volume, float attenuation); + +void S_SoundPitchActor (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation, float pitch); + +// [Nash] Used by ACS and DECORATE +void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local); +void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch); + +// Stops a sound emanating from one of an emitter's channels. +void S_StopSound (AActor *ent, int channel); +void S_StopSound (const sector_t *sec, int channel); +void S_StopSound (const FPolyObj *poly, int channel); + +// Moves all sounds from one mobj to another +void S_RelinkSound (AActor *from, AActor *to); + +// Is the sound playing on one of the emitter's channels? +bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id); +bool S_GetSoundPlayingInfo (const sector_t *sector, int sound_id); +bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id); + +bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id); + +// Change a playing sound's volume +void S_ChangeActorSoundVolume(AActor *actor, int channel, double volume); + +// Change a playing sound's pitch +void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch); + +// Stores/retrieves playing channel information in an archive. +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(); +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_environment.cpp b/src/sound/s_environment.cpp index affb68de2..276770f6c 100644 --- a/src/sound/s_environment.cpp +++ b/src/sound/s_environment.cpp @@ -32,44 +32,13 @@ ** */ -#include "doomtype.h" -#include "s_sound.h" +#include "s_soundinternal.h" #include "sc_man.h" -#include "cmdlib.h" #include "templates.h" -#include "w_wad.h" -#include "i_system.h" #include "m_misc.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "vm.h" -#include "dobject.h" -#include "menu/menu.h" - - -void InitReverbMenu(); -REVERB_PROPERTIES SavedProperties; -ReverbContainer *CurrentEnv; -extern ReverbContainer *ForcedEnvironment; - -// These are for internal use only and not supposed to be user-settable -CVAR(String, reverbedit_name, "", CVAR_NOSET); -CVAR(Int, reverbedit_id1, 0, CVAR_NOSET); -CVAR(Int, reverbedit_id2, 0, CVAR_NOSET); -CVAR(String, reverbsavename, "", 0); - -struct FReverbField -{ - int Min, Max; - float REVERB_PROPERTIES::*Float; - int REVERB_PROPERTIES::*Int; - unsigned int Flag; -}; - - -static const FReverbField ReverbFields[] = +FReverbField ReverbFields[] = { { 0, 25, 0, &REVERB_PROPERTIES::Environment, 0 }, { 1000, 100000, &REVERB_PROPERTIES::EnvSize, 0, 0 }, @@ -110,8 +79,9 @@ static const FReverbField ReverbFields[] = { 0, 0, 0, 0, 7 } }; #define NUM_REVERB_FIELDS (int(countof(ReverbFields))) +int NumReverbs = NUM_REVERB_FIELDS; -static const char *ReverbFieldNames[NUM_REVERB_FIELDS+2] = +const char *ReverbFieldNames[NUM_REVERB_FIELDS+2] = { "Environment", "EnvironmentSize", @@ -534,9 +504,8 @@ void S_AddEnvironment (ReverbContainer *settings) } } -static void ReadReverbDef (int lump) +void S_ReadReverbDef (FScanner &sc) { - FScanner sc; const ReverbContainer *def; ReverbContainer *newenv; REVERB_PROPERTIES props; @@ -545,10 +514,9 @@ static void ReadReverbDef (int lump) bool inited[NUM_REVERB_FIELDS]; uint8_t bools[32]; - sc.OpenLumpNum(lump); while (sc.GetString ()) { - name = copystring (sc.String); + name = strdup (sc.String); sc.MustGetNumber (); id1 = sc.Number; sc.MustGetNumber (); @@ -638,17 +606,6 @@ static void ReadReverbDef (int lump) } } -void S_ParseReverbDef () -{ - int lump, lastlump = 0; - - while ((lump = Wads.FindLump ("REVERBS", &lastlump)) != -1) - { - ReadReverbDef (lump); - } - InitReverbMenu(); -} - void S_UnloadReverbDef () { ReverbContainer *probe = Environments; @@ -660,7 +617,7 @@ void S_UnloadReverbDef () if (!probe->Builtin) { if (pNext != NULL) *pNext = probe->Next; - delete[] const_cast(probe->Name); + free(const_cast(probe->Name)); delete probe; } else @@ -672,476 +629,3 @@ void S_UnloadReverbDef () Environments = &Off; } -CUSTOM_CVAR(Bool, eaxedit_test, false, CVAR_NOINITCALL) -{ - if (self) - { - ForcedEnvironment = CurrentEnv; - } - else - { - ForcedEnvironment = nullptr; - } -} - -struct EnvFlag -{ - const char *Name; - int CheckboxControl; - unsigned int Flag; -}; - -inline int HIBYTE(int i) -{ - return (i >> 8) & 255; -} - -inline int LOBYTE(int i) -{ - return i & 255; -} - -uint16_t FirstFreeID(uint16_t base, bool builtin) -{ - int tryCount = 0; - int priID = HIBYTE(base); - - // If the original sound is built-in, start searching for a new - // primary ID at 30. - if (builtin) - { - for (priID = 30; priID < 256; ++priID) - { - if (S_FindEnvironment(priID << 8) == nullptr) - { - break; - } - } - if (priID == 256) - { // Oh well. - priID = 30; - } - } - - for (;;) - { - uint16_t lastID = Environments->ID; - const ReverbContainer *env = Environments->Next; - - // Find the lowest-numbered free ID with the same primary ID as base - // If none are available, add 100 to base's primary ID and try again. - // If that fails, then the primary ID gets incremented - // by 1 until a match is found. If all the IDs searchable by this - // algorithm are in use, then you're in trouble. - - while (env != nullptr) - { - if (HIBYTE(env->ID) > priID) - { - break; - } - if (HIBYTE(env->ID) == priID) - { - if (HIBYTE(lastID) == priID) - { - if (LOBYTE(env->ID) - LOBYTE(lastID) > 1) - { - return lastID + 1; - } - } - lastID = env->ID; - } - env = env->Next; - } - if (LOBYTE(lastID) == 255) - { - if (tryCount == 0) - { - base += 100 * 256; - tryCount = 1; - } - else - { - base += 256; - } - } - else if (builtin && lastID == 0) - { - return priID << 8; - } - else - { - return lastID + 1; - } - } -} - -FString SuggestNewName(const ReverbContainer *env) -{ - const ReverbContainer *probe = nullptr; - char text[32]; - size_t len; - int number, numdigits; - - strncpy(text, env->Name, 31); - text[31] = 0; - - len = strlen(text); - while (text[len - 1] >= '0' && text[len - 1] <= '9') - { - len--; - } - number = atoi(text + len); - if (number < 1) - { - number = 1; - } - - if (text[len - 1] != ' ' && len < 31) - { - text[len++] = ' '; - } - - for (; number < 100000; ++number) - { - if (number < 10) numdigits = 1; - else if (number < 100) numdigits = 2; - else if (number < 1000) numdigits = 3; - else if (number < 10000)numdigits = 4; - else numdigits = 5; - if (len + numdigits > 31) - { - len = 31 - numdigits; - } - mysnprintf(text + len, countof(text) - len, "%d", number); - - probe = Environments; - while (probe != nullptr) - { - if (stricmp(probe->Name, text) == 0) - break; - probe = probe->Next; - } - if (probe == nullptr) - { - break; - } - } - return text; -} - -void ExportEnvironments(const char *filename, uint32_t count, const ReverbContainer **envs) -{ - FString dest = M_GetDocumentsPath() + filename; - - FileWriter *f = FileWriter::Open(dest); - - if (f != nullptr) - { - for (uint32_t i = 0; i < count; ++i) - { - const ReverbContainer *env = envs[i]; - const ReverbContainer *base; - size_t j; - - if ((unsigned int)env->Properties.Environment < 26) - { - base = DefaultEnvironments[env->Properties.Environment]; - } - else - { - base = nullptr; - } - f->Printf("\"%s\" %u %u\n{\n", env->Name, HIBYTE(env->ID), LOBYTE(env->ID)); - for (j = 0; j < countof(ReverbFields); ++j) - { - const FReverbField *ctl = &ReverbFields[j]; - const char *ctlName = ReverbFieldNames[j]; - if (ctlName) - { - if (j == 0 || - (ctl->Float && base->Properties.*ctl->Float != env->Properties.*ctl->Float) || - (ctl->Int && base->Properties.*ctl->Int != env->Properties.*ctl->Int)) - { - f->Printf("\t%s ", ctlName); - if (ctl->Float) - { - float v = env->Properties.*ctl->Float * 1000; - int vi = int(v >= 0.0 ? v + 0.5 : v - 0.5); - f->Printf("%d.%03d\n", vi / 1000, abs(vi % 1000)); - } - else - { - f->Printf("%d\n", env->Properties.*ctl->Int); - } - } - else - { - if ((1 << ctl->Flag) & (env->Properties.Flags ^ base->Properties.Flags)) - { - f->Printf("\t%s %s\n", ctlName, ctl->Flag & env->Properties.Flags ? "true" : "false"); - } - } - } - } - f->Printf("}\n\n"); - } - delete f; - } - else - { - M_StartMessage("Save failed", 1); - } -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, GetValue) -{ - PARAM_PROLOGUE; - PARAM_INT(index); - float v = 0; - - if (index >= 0 && index < (int)countof(ReverbFields)) - { - auto rev = &ReverbFields[index]; - if (rev->Int != nullptr) - { - v = float(CurrentEnv->Properties.*(rev->Int)); - } - else if (rev->Float != nullptr) - { - v = CurrentEnv->Properties.*(rev->Float); - } - else - { - v = !!(CurrentEnv->Properties.Flags & (1 << int(rev->Flag))); - } - } - ACTION_RETURN_FLOAT(v); - return 1; -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, SetValue) -{ - PARAM_PROLOGUE; - PARAM_INT(index); - PARAM_FLOAT(v); - - if (index >= 0 && index < (int)countof(ReverbFields)) - { - auto rev = &ReverbFields[index]; - if (rev->Int != nullptr) - { - v = CurrentEnv->Properties.*(rev->Int) = clamp(int(v), rev->Min, rev->Max); - } - else if (rev->Float != nullptr) - { - v = CurrentEnv->Properties.*(rev->Float) = clamp(float(v), rev->Min / 1000.f, rev->Max / 1000.f); - } - else - { - if (v == 0) CurrentEnv->Properties.Flags &= ~(1 << int(rev->Flag)); - else CurrentEnv->Properties.Flags |= (1 << int(rev->Flag)); - } - } - - ACTION_RETURN_FLOAT(v); - return 1; -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, GrayCheck) -{ - PARAM_PROLOGUE; - ACTION_RETURN_BOOL(CurrentEnv->Builtin); - return 1; -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, GetSelectedEnvironment) -{ - PARAM_PROLOGUE; - if (numret > 1) - { - numret = 2; - ret[1].SetInt(CurrentEnv ? CurrentEnv->ID : -1); - } - if (numret > 0) - { - ret[0].SetString(CurrentEnv ? CurrentEnv->Name : ""); - } - return numret; -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, FillSelectMenu) -{ - PARAM_PROLOGUE; - PARAM_STRING(ccmd); - PARAM_OBJECT(desc, DOptionMenuDescriptor); - desc->mItems.Clear(); - for (auto env = Environments; env != nullptr; env = env->Next) - { - FStringf text("(%d, %d) %s", HIBYTE(env->ID), LOBYTE(env->ID), env->Name); - FStringf cmd("%s \"%s\"", ccmd.GetChars(), env->Name); - PClass *cls = PClass::FindClass("OptionMenuItemCommand"); - if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) - { - auto func = dyn_cast(cls->FindSymbol("Init", true)); - if (func != nullptr) - { - DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); - VMValue params[] = { item, &text, FName(cmd).GetIndex(), false, true }; - VMCall(func->Variants[0].Implementation, params, 5, nullptr, 0); - desc->mItems.Push((DMenuItemBase*)item); - } - } - } - return 0; -} - -static TArray> SaveState; - -DEFINE_ACTION_FUNCTION(DReverbEdit, FillSaveMenu) -{ - PARAM_PROLOGUE; - PARAM_OBJECT(desc, DOptionMenuDescriptor); - desc->mItems.Resize(4); - SaveState.Clear(); - for (auto env = Environments; env != nullptr; env = env->Next) - { - if (!env->Builtin) - { - int index = (int)SaveState.Push(std::make_pair(env, false)); - - FStringf text("(%d, %d) %s", HIBYTE(env->ID), LOBYTE(env->ID), env->Name); - PClass *cls = PClass::FindClass("OptionMenuItemReverbSaveSelect"); - if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) - { - auto func = dyn_cast(cls->FindSymbol("Init", true)); - if (func != nullptr) - { - DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); - VMValue params[] = { item, &text, index, FName("OnOff").GetIndex() }; - VMCall(func->Variants[0].Implementation, params, 4, nullptr, 0); - desc->mItems.Push((DMenuItemBase*)item); - } - } - } - } - return 0; -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, GetSaveSelection) -{ - PARAM_PROLOGUE; - PARAM_INT(index); - bool res = false; - if ((unsigned)index <= SaveState.Size()) - { - res = SaveState[index].second; - } - ACTION_RETURN_BOOL(res); -} - -DEFINE_ACTION_FUNCTION(DReverbEdit, ToggleSaveSelection) -{ - PARAM_PROLOGUE; - PARAM_INT(index); - if ((unsigned)index <= SaveState.Size()) - { - SaveState[index].second = !SaveState[index].second; - } - return 0; -} - - -CCMD(savereverbs) -{ - if (SaveState.Size() == 0) return; - - TArray toSave; - - for (auto &p : SaveState) - { - if (p.second) toSave.Push(p.first); - } - ExportEnvironments(reverbsavename, toSave.Size(), &toSave[0]); - SaveState.Clear(); -} - -static void SelectEnvironment(const char *envname) -{ - for (auto env = Environments; env != nullptr; env = env->Next) - { - if (!strcmp(env->Name, envname)) - { - CurrentEnv = env; - SavedProperties = env->Properties; - if (eaxedit_test) ForcedEnvironment = env; - - // Set up defaults for a new environment based on this one. - int newid = FirstFreeID(env->ID, env->Builtin); - UCVarValue cv; - cv.Int = HIBYTE(newid); - reverbedit_id1.ForceSet(cv, CVAR_Int); - cv.Int = LOBYTE(newid); - reverbedit_id2.ForceSet(cv, CVAR_Int); - FString selectname = SuggestNewName(env); - cv.String = selectname.GetChars(); - reverbedit_name.ForceSet(cv, CVAR_String); - return; - } - } -} - -void InitReverbMenu() -{ - // Make sure that the editor's variables are properly initialized. - SelectEnvironment("Off"); -} - -CCMD(selectenvironment) -{ - if (argv.argc() > 1) - { - auto str = argv[1]; - SelectEnvironment(str); - } - else - InitReverbMenu(); -} - -CCMD(revertenvironment) -{ - if (CurrentEnv != nullptr) - { - CurrentEnv->Properties = SavedProperties; - } -} - -CCMD(createenvironment) -{ - if (S_FindEnvironment(reverbedit_name)) - { - M_StartMessage(FStringf("An environment with the name '%s' already exists", *reverbedit_name), 1); - return; - } - int id = (reverbedit_id1 << 8) + reverbedit_id2; - if (S_FindEnvironment(id)) - { - M_StartMessage(FStringf("An environment with the ID (%d, %d) already exists", *reverbedit_id1, *reverbedit_id2), 1); - return; - } - - auto newenv = new ReverbContainer; - newenv->Builtin = false; - newenv->ID = id; - newenv->Name = copystring(reverbedit_name); - newenv->Next = nullptr; - newenv->Properties = CurrentEnv->Properties; - S_AddEnvironment(newenv); - SelectEnvironment(newenv->Name); -} - -CCMD(reverbedit) -{ - C_DoCommand("openmenu reverbedit"); -} - diff --git a/src/sound/s_reverbedit.cpp b/src/sound/s_reverbedit.cpp new file mode 100644 index 000000000..abbaceef1 --- /dev/null +++ b/src/sound/s_reverbedit.cpp @@ -0,0 +1,555 @@ +/* +** +** reverb editor +** +**--------------------------------------------------------------------------- +** Copyright 2005-2016 Randy Heit +** Copyright 2005-2017 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "doomtype.h" +#include "s_sound.h" +#include "sc_man.h" +#include "cmdlib.h" +#include "templates.h" +#include "w_wad.h" +#include "i_system.h" +#include "m_misc.h" + +#include "c_cvars.h" +#include "c_dispatch.h" +#include "vm.h" +#include "dobject.h" +#include "menu/menu.h" + +void S_ReadReverbDef (FScanner &sc); + +extern ReverbContainer *ForcedEnvironment; +ReverbContainer *CurrentEnv; +REVERB_PROPERTIES SavedProperties; + +extern FReverbField ReverbFields[]; +extern const char* ReverbFieldNames[]; +extern int NumReverbs; + + +// These are for internal use only and not supposed to be user-settable +CVAR(String, reverbedit_name, "", CVAR_NOSET); +CVAR(Int, reverbedit_id1, 0, CVAR_NOSET); +CVAR(Int, reverbedit_id2, 0, CVAR_NOSET); +CVAR(String, reverbsavename, "", 0); + + +CUSTOM_CVAR(Bool, eaxedit_test, false, CVAR_NOINITCALL) +{ + if (self) + { + ForcedEnvironment = CurrentEnv; + } + else + { + ForcedEnvironment = nullptr; + } +} + +struct EnvFlag +{ + const char *Name; + int CheckboxControl; + unsigned int Flag; +}; + +inline int HIBYTE(int i) +{ + return (i >> 8) & 255; +} + +inline int LOBYTE(int i) +{ + return i & 255; +} + +uint16_t FirstFreeID(uint16_t base, bool builtin) +{ + int tryCount = 0; + int priID = HIBYTE(base); + + // If the original sound is built-in, start searching for a new + // primary ID at 30. + if (builtin) + { + for (priID = 30; priID < 256; ++priID) + { + if (S_FindEnvironment(priID << 8) == nullptr) + { + break; + } + } + if (priID == 256) + { // Oh well. + priID = 30; + } + } + + for (;;) + { + uint16_t lastID = Environments->ID; + const ReverbContainer *env = Environments->Next; + + // Find the lowest-numbered free ID with the same primary ID as base + // If none are available, add 100 to base's primary ID and try again. + // If that fails, then the primary ID gets incremented + // by 1 until a match is found. If all the IDs searchable by this + // algorithm are in use, then you're in trouble. + + while (env != nullptr) + { + if (HIBYTE(env->ID) > priID) + { + break; + } + if (HIBYTE(env->ID) == priID) + { + if (HIBYTE(lastID) == priID) + { + if (LOBYTE(env->ID) - LOBYTE(lastID) > 1) + { + return lastID + 1; + } + } + lastID = env->ID; + } + env = env->Next; + } + if (LOBYTE(lastID) == 255) + { + if (tryCount == 0) + { + base += 100 * 256; + tryCount = 1; + } + else + { + base += 256; + } + } + else if (builtin && lastID == 0) + { + return priID << 8; + } + else + { + return lastID + 1; + } + } +} + +FString SuggestNewName(const ReverbContainer *env) +{ + const ReverbContainer *probe = nullptr; + char text[32]; + size_t len; + int number, numdigits; + + strncpy(text, env->Name, 31); + text[31] = 0; + + len = strlen(text); + while (text[len - 1] >= '0' && text[len - 1] <= '9') + { + len--; + } + number = atoi(text + len); + if (number < 1) + { + number = 1; + } + + if (text[len - 1] != ' ' && len < 31) + { + text[len++] = ' '; + } + + for (; number < 100000; ++number) + { + if (number < 10) numdigits = 1; + else if (number < 100) numdigits = 2; + else if (number < 1000) numdigits = 3; + else if (number < 10000)numdigits = 4; + else numdigits = 5; + if (len + numdigits > 31) + { + len = 31 - numdigits; + } + mysnprintf(text + len, countof(text) - len, "%d", number); + + probe = Environments; + while (probe != nullptr) + { + if (stricmp(probe->Name, text) == 0) + break; + probe = probe->Next; + } + if (probe == nullptr) + { + break; + } + } + return text; +} + +void ExportEnvironments(const char *filename, uint32_t count, const ReverbContainer **envs) +{ + FString dest = M_GetDocumentsPath() + filename; + + FileWriter *f = FileWriter::Open(dest); + + if (f != nullptr) + { + for (uint32_t i = 0; i < count; ++i) + { + const ReverbContainer *env = envs[i]; + const ReverbContainer *base; + size_t j; + + if ((unsigned int)env->Properties.Environment < 26) + { + base = DefaultEnvironments[env->Properties.Environment]; + } + else + { + base = nullptr; + } + f->Printf("\"%s\" %u %u\n{\n", env->Name, HIBYTE(env->ID), LOBYTE(env->ID)); + for (j = 0; j < NumReverbs; ++j) + { + const FReverbField *ctl = &ReverbFields[j]; + const char *ctlName = ReverbFieldNames[j]; + if (ctlName) + { + if (j == 0 || + (ctl->Float && base->Properties.*ctl->Float != env->Properties.*ctl->Float) || + (ctl->Int && base->Properties.*ctl->Int != env->Properties.*ctl->Int)) + { + f->Printf("\t%s ", ctlName); + if (ctl->Float) + { + float v = env->Properties.*ctl->Float * 1000; + int vi = int(v >= 0.0 ? v + 0.5 : v - 0.5); + f->Printf("%d.%03d\n", vi / 1000, abs(vi % 1000)); + } + else + { + f->Printf("%d\n", env->Properties.*ctl->Int); + } + } + else + { + if ((1 << ctl->Flag) & (env->Properties.Flags ^ base->Properties.Flags)) + { + f->Printf("\t%s %s\n", ctlName, ctl->Flag & env->Properties.Flags ? "true" : "false"); + } + } + } + } + f->Printf("}\n\n"); + } + delete f; + } + else + { + M_StartMessage("Save failed", 1); + } +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, GetValue) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + float v = 0; + + if (index >= 0 && index < NumReverbs) + { + auto rev = &ReverbFields[index]; + if (rev->Int != nullptr) + { + v = float(CurrentEnv->Properties.*(rev->Int)); + } + else if (rev->Float != nullptr) + { + v = CurrentEnv->Properties.*(rev->Float); + } + else + { + v = !!(CurrentEnv->Properties.Flags & (1 << int(rev->Flag))); + } + } + ACTION_RETURN_FLOAT(v); + return 1; +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, SetValue) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + PARAM_FLOAT(v); + + if (index >= 0 && index < NumReverbs) + { + auto rev = &ReverbFields[index]; + if (rev->Int != nullptr) + { + v = CurrentEnv->Properties.*(rev->Int) = clamp(int(v), rev->Min, rev->Max); + } + else if (rev->Float != nullptr) + { + v = CurrentEnv->Properties.*(rev->Float) = clamp(float(v), rev->Min / 1000.f, rev->Max / 1000.f); + } + else + { + if (v == 0) CurrentEnv->Properties.Flags &= ~(1 << int(rev->Flag)); + else CurrentEnv->Properties.Flags |= (1 << int(rev->Flag)); + } + } + + ACTION_RETURN_FLOAT(v); + return 1; +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, GrayCheck) +{ + PARAM_PROLOGUE; + ACTION_RETURN_BOOL(CurrentEnv->Builtin); + return 1; +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, GetSelectedEnvironment) +{ + PARAM_PROLOGUE; + if (numret > 1) + { + numret = 2; + ret[1].SetInt(CurrentEnv ? CurrentEnv->ID : -1); + } + if (numret > 0) + { + ret[0].SetString(CurrentEnv ? CurrentEnv->Name : ""); + } + return numret; +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, FillSelectMenu) +{ + PARAM_PROLOGUE; + PARAM_STRING(ccmd); + PARAM_OBJECT(desc, DOptionMenuDescriptor); + desc->mItems.Clear(); + for (auto env = Environments; env != nullptr; env = env->Next) + { + FStringf text("(%d, %d) %s", HIBYTE(env->ID), LOBYTE(env->ID), env->Name); + FStringf cmd("%s \"%s\"", ccmd.GetChars(), env->Name); + PClass *cls = PClass::FindClass("OptionMenuItemCommand"); + if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) + { + auto func = dyn_cast(cls->FindSymbol("Init", true)); + if (func != nullptr) + { + DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); + VMValue params[] = { item, &text, FName(cmd).GetIndex(), false, true }; + VMCall(func->Variants[0].Implementation, params, 5, nullptr, 0); + desc->mItems.Push((DMenuItemBase*)item); + } + } + } + return 0; +} + +static TArray> SaveState; + +DEFINE_ACTION_FUNCTION(DReverbEdit, FillSaveMenu) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(desc, DOptionMenuDescriptor); + desc->mItems.Resize(4); + SaveState.Clear(); + for (auto env = Environments; env != nullptr; env = env->Next) + { + if (!env->Builtin) + { + int index = (int)SaveState.Push(std::make_pair(env, false)); + + FStringf text("(%d, %d) %s", HIBYTE(env->ID), LOBYTE(env->ID), env->Name); + PClass *cls = PClass::FindClass("OptionMenuItemReverbSaveSelect"); + if (cls != nullptr && cls->IsDescendantOf("OptionMenuItem")) + { + auto func = dyn_cast(cls->FindSymbol("Init", true)); + if (func != nullptr) + { + DMenuItemBase *item = (DMenuItemBase*)cls->CreateNew(); + VMValue params[] = { item, &text, index, FName("OnOff").GetIndex() }; + VMCall(func->Variants[0].Implementation, params, 4, nullptr, 0); + desc->mItems.Push((DMenuItemBase*)item); + } + } + } + } + return 0; +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, GetSaveSelection) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + bool res = false; + if ((unsigned)index <= SaveState.Size()) + { + res = SaveState[index].second; + } + ACTION_RETURN_BOOL(res); +} + +DEFINE_ACTION_FUNCTION(DReverbEdit, ToggleSaveSelection) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + if ((unsigned)index <= SaveState.Size()) + { + SaveState[index].second = !SaveState[index].second; + } + return 0; +} + + +CCMD(savereverbs) +{ + if (SaveState.Size() == 0) return; + + TArray toSave; + + for (auto &p : SaveState) + { + if (p.second) toSave.Push(p.first); + } + ExportEnvironments(reverbsavename, toSave.Size(), &toSave[0]); + SaveState.Clear(); +} + +static void SelectEnvironment(const char *envname) +{ + for (auto env = Environments; env != nullptr; env = env->Next) + { + if (!strcmp(env->Name, envname)) + { + CurrentEnv = env; + SavedProperties = env->Properties; + if (eaxedit_test) ForcedEnvironment = env; + + // Set up defaults for a new environment based on this one. + int newid = FirstFreeID(env->ID, env->Builtin); + UCVarValue cv; + cv.Int = HIBYTE(newid); + reverbedit_id1.ForceSet(cv, CVAR_Int); + cv.Int = LOBYTE(newid); + reverbedit_id2.ForceSet(cv, CVAR_Int); + FString selectname = SuggestNewName(env); + cv.String = selectname.GetChars(); + reverbedit_name.ForceSet(cv, CVAR_String); + return; + } + } +} + +void InitReverbMenu() +{ + // Make sure that the editor's variables are properly initialized. + SelectEnvironment("Off"); +} + +CCMD(selectenvironment) +{ + if (argv.argc() > 1) + { + auto str = argv[1]; + SelectEnvironment(str); + } + else + InitReverbMenu(); +} + +CCMD(revertenvironment) +{ + if (CurrentEnv != nullptr) + { + CurrentEnv->Properties = SavedProperties; + } +} + +CCMD(createenvironment) +{ + if (S_FindEnvironment(reverbedit_name)) + { + M_StartMessage(FStringf("An environment with the name '%s' already exists", *reverbedit_name), 1); + return; + } + int id = (reverbedit_id1 << 8) + reverbedit_id2; + if (S_FindEnvironment(id)) + { + M_StartMessage(FStringf("An environment with the ID (%d, %d) already exists", *reverbedit_id1, *reverbedit_id2), 1); + return; + } + + auto newenv = new ReverbContainer; + newenv->Builtin = false; + newenv->ID = id; + newenv->Name = copystring(reverbedit_name); + newenv->Next = nullptr; + newenv->Properties = CurrentEnv->Properties; + S_AddEnvironment(newenv); + SelectEnvironment(newenv->Name); +} + +CCMD(reverbedit) +{ + C_DoCommand("openmenu reverbedit"); +} + +// This is here because it depends on Doom's resource management and is not universal. +void S_ParseReverbDef () +{ + int lump, lastlump = 0; + + while ((lump = Wads.FindLump ("REVERBS", &lastlump)) != -1) + { + FScanner sc; + sc.OpenLumpNum(lump); + S_ReadReverbDef (sc);; + } + InitReverbMenu(); +} + diff --git a/src/sound/s_sndseq.cpp b/src/sound/s_sndseq.cpp index da2b3d600..9bdbfb923 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 08417277f..4fba205dc 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -1,31 +1,11 @@ -//----------------------------------------------------------------------------- -// -// Copyright 1993-1996 id Software -// Copyright 1999-2016 Randy Heit -// Copyright 2002-2016 Christoph Oelckers -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//----------------------------------------------------------------------------- -// -// DESCRIPTION: none -// -//----------------------------------------------------------------------------- - -/* For code that originates from ZDoom the following applies: +/* +** s_sound.cpp +** Main sound engine ** **--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2002-2019 Christoph Oelckers +** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions @@ -59,283 +39,33 @@ #include #endif -#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" +#include "s_soundinternal.h" +#include "m_swap.h" +#include "superfasthash.h" -// MACROS ------------------------------------------------------------------ - -#ifdef NeXT -// NeXT doesn't need a binary flag in open call -#define O_BINARY 0 -#endif - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#define NORM_PITCH 128 -#define NORM_PRIORITY 64 -#define NORM_SEP 0 - -#define S_PITCH_PERTURB 1 -#define S_STEREO_SWING 0.75 - -// TYPES ------------------------------------------------------------------- enum { - SOURCE_None, // Sound is always on top of the listener. - SOURCE_Actor, // Sound is coming from an actor. - SOURCE_Sector, // Sound is coming from a sector. - SOURCE_Polyobj, // Sound is coming from a polyobject. - SOURCE_Unattached, // Sound is not attached to any particular emitter. + DEFAULT_PITCH = 128, }; -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -extern float S_GetMusicVolume (const char *music); - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -void I_CloseSound(); - -// 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, AActor *actor, int channel); -static bool S_IsChannelUsed(AActor *actor, int channel, int *seen); -static void S_ActivatePlayList(bool goBack); -static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); -static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, const FPolyObj *poly, - 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); -static FSoundChan *S_StartSound(AActor *mover, const sector_t *sec, const FPolyObj *poly, - const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo *rolloff, float spitch); -static void S_SetListener(SoundListener &listener, AActor *listenactor); - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static bool SoundPaused; // whether sound is paused -static int RestartEvictionsAt; // do not restart evicted channels before this time - -// 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 -{ - if (self < 64) self = 64; -} -CVAR(Bool, snd_waterreverb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) - -// 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); } //========================================================================== @@ -344,13 +74,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(); @@ -361,139 +89,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]); } } } @@ -504,7 +141,7 @@ void S_PrecacheLevel (FLevelLocals *Level) // //========================================================================== -void S_CacheSound (sfxinfo_t *sfx) +void SoundEngine::CacheSound (sfxinfo_t *sfx) { if (GSnd) { @@ -519,14 +156,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; } } @@ -538,14 +175,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(); } @@ -558,21 +193,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; } @@ -585,11 +220,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); } //========================================================================== @@ -598,7 +233,7 @@ void S_ReturnChannel(FSoundChan *chan) // //========================================================================== -void S_UnlinkChannel(FSoundChan *chan) +void SoundEngine::UnlinkChannel(FSoundChan *chan) { *(chan->PrevChan) = chan->NextChan; if (chan->NextChan != NULL) @@ -613,7 +248,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) @@ -624,6 +259,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. @@ -637,251 +322,15 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) // //========================================================================= -static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) +void SoundEngine::CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) { - CalcPosVel(chan->SourceType, chan->Actor, chan->Sector, chan->Poly, chan->Point, + 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 AActor *actor, const sector_t *sector, - const FPolyObj *poly, 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; - } - - // [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: - //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 }; - } - break; - - case SOURCE_Sector: - 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: - 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; - } - } - } - if (vel != NULL) - { - // Only actors maintain velocity information. - if (type == SOURCE_Actor && actor != NULL) - { - vel->X = float(actor->Vel.X * TICRATE); - vel->Y = float(actor->Vel.Z * TICRATE); - vel->Z = float(actor->Vel.Y * TICRATE); - } - else - { - vel->Zero(); - } - } -} - -//========================================================================== -// -// 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 ? chan->Actor : 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); } //========================================================================== @@ -894,9 +343,9 @@ static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, // //========================================================================== -static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyObj *poly, +FSoundChan *SoundEngine::StartSound(int type, const void *source, const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, - FRolloffInfo *forcedrolloff=NULL, float spitch = 0.0f) + FRolloffInfo *forcedrolloff, float spitch) { sfxinfo_t *sfx; int chanflags; @@ -911,56 +360,24 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) return NULL; - int type; - - if (actor != NULL) - { - type = SOURCE_Actor; - } - else if (sec != NULL) - { - type = SOURCE_Sector; - } - else if (poly != NULL) - { - type = SOURCE_Polyobj; - } - else if (pt != NULL) - { - type = SOURCE_Unattached; - } - else - { - type = SOURCE_None; - } + // prevent crashes. + if (type == SOURCE_Unattached && pt == nullptr) type = SOURCE_None; org_id = sound_id; chanflags = channel & ~7; channel &= 7; - CalcPosVel(type, actor, sec, poly, &pt->X, channel, chanflags, &pos, &vel); + CalcPosVel(type, source, &pt->X, channel, chanflags, &pos, &vel); - if (!ValidatePosVel(type == SOURCE_Actor ? actor : 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 (actor != nullptr && actor != players[consoleplayer].camera) - { - 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; @@ -974,18 +391,11 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // Resolve player sounds, random sounds, and aliases while (sfx->link != sfxinfo_t::NO_LINK) { - if (sfx->bPlayerReserve) - { - sound_id = FSoundID(S_FindSkinnedSound (actor, sound_id)); - near_limit = S_sfx[sound_id].NearLimit; - limit_range = S_sfx[sound_id].LimitRange; - rolloff = &S_sfx[sound_id].Rolloff; - } - else if (sfx->bRandomHeader) + if (sfx->bRandomHeader) { // 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; @@ -1028,21 +438,21 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } // 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; } // If the sound is unpositioned or comes from the listener, it is // never limited. - if (type == SOURCE_None || actor == players[consoleplayer].camera) + if (type == SOURCE_None || source == listener.ListenerObject) { near_limit = 0; } // 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, actor, 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; } @@ -1056,7 +466,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } // 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) @@ -1065,7 +475,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } // Select priority. - if (type == SOURCE_None || actor == players[consoleplayer].camera) + if (type == SOURCE_None || source == listener.ListenerObject) { basepriority = 80; } @@ -1075,11 +485,11 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } int seen = 0; - if (actor != NULL && channel == CHAN_AUTO) + if (source != NULL && channel == CHAN_AUTO) { // Select a channel that isn't already playing something. // Try channel 0 first, then travel from channel 7 down. - if (!S_IsChannelUsed(actor, 0, &seen)) + if (!IsChannelUsed(type, source, 0, &seen)) { channel = 0; } @@ -1087,7 +497,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO { for (channel = 7; channel > 0; --channel) { - if (!S_IsChannelUsed(actor, channel, &seen)) + if (!IsChannelUsed(type, source, channel, &seen)) { break; } @@ -1100,26 +510,16 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } // If this actor is already playing something on the selected channel, stop it. - if (type != SOURCE_None && ((actor == NULL && channel != CHAN_AUTO) || (actor != NULL && S_IsChannelUsed(actor, 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) { if (chan->SourceType == type && chan->EntChannel == channel) { - bool foundit; - - switch (type) + if (type != SOURCE_Unattached) chan->Source = source; + else { - case SOURCE_Actor: foundit = (chan->Actor == actor); break; - case SOURCE_Sector: foundit = (chan->Sector == sec); break; - case SOURCE_Polyobj: foundit = (chan->Poly == poly); break; - case SOURCE_Unattached: foundit = (chan->Point[0] == pt->X && chan->Point[2] == pt->Z && chan->Point[1] == pt->Y); break; - default: foundit = false; break; - } - if (foundit) - { - S_StopChannel(chan); - break; + chan->Point[0] == pt->X && chan->Point[2] == pt->Z && chan->Point[1] == pt->Y; } } } @@ -1135,11 +535,11 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // 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) @@ -1156,9 +556,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO if (attenuation > 0) { - S_LoadSound3D(sfx, &SoundBuffer); - SoundListener listener; - S_SetListener(listener, players[consoleplayer].camera); + LoadSound3D(sfx, &SoundBuffer); chan = (FSoundChan*)GSnd->StartSound3D (sfx->data3d, &listener, float(volume), rolloff, float(attenuation), pitch, basepriority, pos, vel, channel, startflags, NULL); } else @@ -1168,7 +566,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } if (chan == NULL && (chanflags & CHAN_LOOP)) { - chan = (FSoundChan*)S_GetChannel(NULL); + chan = (FSoundChan*)GetChannel(NULL); GSnd->MarkStartTime(chan); chanflags |= CHAN_EVICTED; } @@ -1193,17 +591,17 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO chan->Priority = basepriority; chan->DistanceScale = float(attenuation); chan->SourceType = type; - switch (type) + if (type == SOURCE_Unattached) { - case SOURCE_Actor: chan->Actor = actor; break; - case SOURCE_Sector: chan->Sector = sec; break; - case SOURCE_Polyobj: chan->Poly = poly; break; - case SOURCE_Unattached: chan->Point[0] = pt->X; chan->Point[1] = pt->Y; chan->Point[2] = pt->Z; break; - default: break; + chan->Point[0] = pt->X; chan->Point[1] = pt->Y; chan->Point[2] = pt->Z; + } + else if (type != SOURCE_None) + { + chan->Source = source; } if (spitch > 0.0) - S_SetPitch(chan, spitch); + SetPitch(chan, spitch); } return chan; @@ -1217,7 +615,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // //========================================================================== -void S_RestartSound(FSoundChan *chan) +void SoundEngine::RestartChannel(FSoundChan *chan) { assert(chan->ChanFlags & CHAN_EVICTED); @@ -1226,10 +624,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) @@ -1258,15 +656,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, NULL, 0)) + if (chan->NearLimit > 0 && CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0)) { return; } - S_LoadSound3D(sfx, &SoundBuffer); - SoundListener listener; - S_SetListener(listener, players[consoleplayer].camera); - + 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); @@ -1283,158 +678,6 @@ void S_RestartSound(FSoundChan *chan) } } -//========================================================================== -// -// S_Sound - Unpositioned version -// -//========================================================================== - -void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float pitch) -{ - S_StartSound(NULL, NULL, NULL, NULL, channel, sound_id, volume, attenuation, 0, pitch); -} - -void S_Sound(int channel, FSoundID sound_id, float volume, float attenuation) -{ - S_StartSound (NULL, NULL, NULL, NULL, channel, sound_id, volume, attenuation, 0, 0.f); -} - -DEFINE_ACTION_FUNCTION(DObject, S_Sound) -{ - PARAM_PROLOGUE; - PARAM_SOUND(id); - PARAM_INT(channel); - PARAM_FLOAT(volume); - PARAM_FLOAT(attn); - PARAM_FLOAT(pitch); - S_SoundPitch(channel, id, static_cast(volume), static_cast(attn), static_cast(pitch)); - return 0; -} - -//========================================================================== -// -// 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; - S_StartSound (ent, nullptr, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); -} - -void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation) -{ - S_SoundPitchActor(ent, channel, sound_id, volume, attenuation, 0.f); -} -//========================================================================== -// -// S_SoundMinMaxDist - An actor is source -// -// Attenuation is specified as min and max distances, rather than a scalar. -// -//========================================================================== - -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; - - FRolloffInfo rolloff; - - rolloff.RolloffType = ROLLOFF_Linear; - rolloff.MinDistance = mindist; - rolloff.MaxDistance = maxdist; - S_StartSound(ent, nullptr, nullptr, nullptr, channel, sound_id, volume, 1, &rolloff); -} - -//========================================================================== -// -// S_Sound - A polyobject is source -// -//========================================================================== - -void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation) -{ - if (poly->Level != primaryLevel) return; - S_StartSound (nullptr, nullptr, poly, nullptr, channel, sound_id, volume, attenuation); -} - -//========================================================================== -// -// S_Sound - A point is source -// -//========================================================================== - -void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sound_id, float volume, float attenuation) -{ - 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 (nullptr, nullptr, nullptr, &p, channel, sound_id, volume, attenuation); -} - -//========================================================================== -// -// S_Sound - An entire sector is source -// -//========================================================================== - -void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation) -{ - if (sec->Level != primaryLevel) return; - S_StartSound (nullptr, sec, nullptr, nullptr, channel, sfxid, volume, attenuation); -} - -//========================================================================== -// -// S_PlaySound - Subfunction used by ACS and DECORATE -// -// Has a local parameter to make the sound audible only to the source -// -//========================================================================== - -void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch) -{ - if (a == nullptr || a->Sector->Flags & SECF_SILENT || a->Level != primaryLevel) - return; - - if (!local) - { - S_SoundPitchActor(a, chan, sid, vol, atten, pitch); - } - else - { - if (a->CheckLocalView()) - { - S_SoundPitch(chan, sid, vol, ATTN_NONE, pitch); - } - } -} - -void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local) -{ - S_PlaySoundPitch(a, chan, sid, vol, atten, local, 0.f); -} - -void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch) -{ - if (!looping) - { - if (!(channel & CHAN_NOSTOP) || !S_IsActorPlayingSomething(self, channel & 7, soundid)) - { - S_PlaySoundPitch(self, channel, soundid, (float)volume, (float)attenuation, local, (float)pitch); - } - } - else - { - if (!S_IsActorPlayingSomething(self, channel & 7, soundid)) - { - S_PlaySoundPitch(self, channel | CHAN_LOOP, soundid, (float)volume, (float)attenuation, local, (float)pitch); - } - } -} - //========================================================================== // // S_LoadSound @@ -1443,7 +686,7 @@ void A_PlaySound(AActor *self, int soundid, int channel, double volume, int loop // //========================================================================== -sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) +sfxinfo_t *SoundEngine::LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (GSnd->IsNull()) return sfx; @@ -1463,7 +706,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. @@ -1472,13 +715,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; @@ -1523,14 +765,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; @@ -1540,11 +782,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. @@ -1582,7 +822,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) { @@ -1611,8 +851,8 @@ bool S_CheckSingular(int sound_id) // //========================================================================== -bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, - AActor *actor, int channel) +bool SoundEngine::CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, + int sourcetype, const void *actor, int channel) { FSoundChan *chan; int count; @@ -1624,7 +864,7 @@ bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, floa FVector3 chanorigin; if (actor != NULL && chan->EntChannel == channel && - chan->SourceType == SOURCE_Actor && chan->Actor == actor) + chan->SourceType == sourcetype && chan->Source == actor) { // We are restarting a playing sound. Always let it play. return false; } @@ -1647,16 +887,15 @@ 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) { FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_None && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) + if (chan->SourceType == SOURCE_None) { - S_StopChannel(chan); + StopChannel(chan); } chan = next; } @@ -1670,65 +909,17 @@ void S_StopSound (int channel) // //========================================================================== -void S_StopSound (AActor *actor, int channel) +void SoundEngine::StopSound(int sourcetype, const void* actor, int channel) { - FSoundChan *chan = Channels; + FSoundChan* chan = Channels; while (chan != NULL) { - FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Actor && - chan->Actor == actor && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) + FSoundChan* next = chan->NextChan; + if (chan->SourceType == sourcetype && + chan->Source == actor && + (chan->EntChannel == channel || channel < 0)) { - S_StopChannel(chan); - } - chan = next; - } -} - -//========================================================================== -// -// S_StopSound -// -// Stops a sound from a single sector from playing on a specific channel. -// -//========================================================================== - -void S_StopSound (const sector_t *sec, int channel) -{ - FSoundChan *chan = Channels; - while (chan != NULL) - { - FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Sector && - chan->Sector == sec && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) - { - S_StopChannel(chan); - } - chan = next; - } -} - -//========================================================================== -// -// S_StopSound -// -// Stops a sound from a single polyobject from playing on a specific channel. -// -//========================================================================== - -void S_StopSound (const FPolyObj *poly, int channel) -{ - FSoundChan *chan = Channels; - while (chan != NULL) - { - FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Polyobj && - chan->Poly == poly && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) - { - S_StopChannel(chan); + StopChannel(chan); } chan = next; } @@ -1740,18 +931,13 @@ void S_StopSound (const FPolyObj *poly, 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; } @@ -1767,7 +953,7 @@ void S_StopAllChannels () // NULL, then the sound becomes a positioned sound. //========================================================================== -void S_RelinkSound (AActor *from, AActor *to) +void SoundEngine::RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) { if (from == NULL) return; @@ -1776,24 +962,23 @@ void S_RelinkSound (AActor *from, AActor *to) while (chan != NULL) { FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Actor && chan->Actor == from) + if (chan->SourceType == sourcetype && chan->Source == from) { if (to != NULL) { - chan->Actor = to; + chan->Source = to; } - else if (!(chan->ChanFlags & CHAN_LOOP) && !(compatflags2 & COMPATF2_SOUNDCUTOFF)) + else if (!(chan->ChanFlags & CHAN_LOOP) && optpos) { - chan->Actor = NULL; + chan->Source = NULL; chan->SourceType = SOURCE_Unattached; - FVector3 p = from->SoundPos(); - chan->Point[0] = p.X; - chan->Point[1] = p.Y; - chan->Point[2] = p.Z; + chan->Point[0] = optpos->X; + chan->Point[1] = optpos->Y; + chan->Point[2] = optpos->Z; } else { - S_StopChannel(chan); + StopChannel(chan); } } chan = next; @@ -1807,7 +992,7 @@ void S_RelinkSound (AActor *from, AActor *to) // //========================================================================== -void S_ChangeSoundVolume(AActor *actor, 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 @@ -1818,9 +1003,9 @@ void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && - chan->Actor == actor && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) + if (chan->SourceType == sourcetype && + chan->Source == source && + (chan->EntChannel == channel || channel == -1)) { GSnd->ChannelVolume(chan, volume); chan->Volume = volume; @@ -1836,26 +1021,26 @@ void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) // //========================================================================== -void S_ChangeSoundPitch(AActor *actor, 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) { - if (chan->SourceType == SOURCE_Actor && - chan->Actor == actor && + if (chan->SourceType == sourcetype && + 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)); } //========================================================================== @@ -1865,49 +1050,15 @@ void S_SetPitch(FSoundChan *chan, float pitch) // Is a sound being played by a specific emitter? //========================================================================== -bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) +bool SoundEngine::GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) { if (sound_id > 0) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { if (chan->OrgID == sound_id && - chan->SourceType == SOURCE_Actor && - chan->Actor == actor) - { - return true; - } - } - } - return false; -} - -bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) -{ - if (sound_id > 0) - { - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (chan->OrgID == sound_id && - chan->SourceType == SOURCE_Sector && - chan->Sector == sec) - { - return true; - } - } - } - return false; -} - -bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) -{ - if (sound_id > 0) - { - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (chan->OrgID == sound_id && - chan->SourceType == SOURCE_Polyobj && - chan->Poly == poly) + chan->SourceType == sourcetype && + chan->Source == source) { return true; } @@ -1926,7 +1077,7 @@ bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) // //========================================================================== -static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) +bool SoundEngine::IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) { if (*seen & (1 << channel)) { @@ -1934,7 +1085,7 @@ static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) } for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && chan->Actor == actor) + if (chan->SourceType == sourcetype && chan->Source == actor) { *seen |= 1 << chan->EntChannel; if (chan->EntChannel == channel) @@ -1952,16 +1103,11 @@ static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) // //========================================================================== -bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) +bool SoundEngine::IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) { - if (compatflags & COMPATF_MAGICSILENCE) - { - channel = 0; - } - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && chan->Actor == actor) + if (chan->SourceType == sourcetype && chan->Source == actor) { if (channel == 0 || chan->EntChannel == channel) { @@ -1972,50 +1118,6 @@ bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) 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 @@ -2025,7 +1127,7 @@ void S_SetSoundPaused (int state) // //========================================================================== -void S_EvictAllChannels() +void SoundEngine::EvictAllChannels() { FSoundChan *chan, *next; @@ -2043,7 +1145,7 @@ void S_EvictAllChannels() chan->StartTime = GSnd ? GSnd->GetPosition(chan) : 0; chan->ChanFlags |= CHAN_ABSTIME; } - S_StopChannel(chan); + StopChannel(chan); } // assert(chan->NextChan == next); } @@ -2058,21 +1160,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. @@ -2082,7 +1184,7 @@ void S_RestoreEvictedChannel(FSoundChan *chan) } else if (chan->SysChannel == NULL && (chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_LOOP)) == CHAN_FORGETTABLE) { - S_ReturnChannel(chan); + ReturnChannel(chan); } } @@ -2095,10 +1197,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); } //========================================================================== @@ -2108,15 +1210,11 @@ void S_RestoreEvictedChannels() // Updates music & sounds //========================================================================== -void S_UpdateSounds (AActor *listenactor) +void SoundEngine::UpdateSounds(int time) { FVector3 pos, vel; - SoundListener listener; - // should never happen - S_SetListener(listener, listenactor); - - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) + for (FSoundChan* chan = Channels; chan != NULL; chan = chan->NextChan) { if ((chan->ChanFlags & (CHAN_EVICTED | CHAN_IS3D)) == CHAN_IS3D) { @@ -2130,120 +1228,62 @@ void S_UpdateSounds (AActor *listenactor) chan->ChanFlags &= ~CHAN_JUSTSTARTED; } - for (auto Level : AllLevels()) - { - SN_UpdateActiveSequences(Level); - } - GSnd->UpdateListener(&listener); GSnd->UpdateSounds(); - if (primaryLevel->time >= RestartEvictionsAt) + if (time >= RestartEvictionsAt) { RestartEvictionsAt = 0; - S_RestoreEvictedChannels(); + RestoreEvictedChannels(); } } -//========================================================================== -// -// Sets the internal listener structure -// -//========================================================================== - -static void S_SetListener(SoundListener &listener, AActor *listenactor) -{ - if (listenactor != NULL) - { - listener.angle = (float)listenactor->Angles.Yaw.Radians(); - /* - listener.velocity.X = listenactor->vel.x * (TICRATE/65536.f); - listener.velocity.Y = listenactor->vel.z * (TICRATE/65536.f); - listener.velocity.Z = listenactor->vel.y * (TICRATE/65536.f); - */ - listener.velocity.Zero(); - listener.position = listenactor->SoundPos(); - listener.underwater = listenactor->waterlevel == 3; - assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber); - listener.Environment = primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment; - listener.valid = true; - } - else - { - listener.angle = 0; - listener.position.Zero(); - listener.velocity.Zero(); - listener.underwater = false; - listener.Environment = NULL; - listener.valid = false; - } -} - - - //========================================================================== // // S_GetRolloff // //========================================================================== -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; @@ -2277,7 +1317,7 @@ void S_ChannelEnded(FISoundChannel *ichan) } if (!evicted) { - S_ReturnChannel(schan); + ReturnChannel(schan); } else { @@ -2293,7 +1333,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) @@ -2308,11 +1348,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; @@ -2326,313 +1366,299 @@ void S_StopChannel(FSoundChan *chan) chan->ChanFlags |= CHAN_FORGETTABLE; if (chan->SourceType == SOURCE_Actor) { - chan->Actor = NULL; + chan->Source = NULL; } } GSnd->StopChannel(chan); } else { - S_ReturnChannel(chan); + ReturnChannel(chan); } } -//========================================================================== -// -// -// -//========================================================================== - -static FSerializer &Serialize(FSerializer &arc, const char *key, FSoundChan &chan, FSoundChan *def) -{ - if (arc.BeginObject(key)) - { - arc("sourcetype", chan.SourceType) - ("soundid", chan.SoundID) - ("orgid", chan.OrgID) - ("volume", chan.Volume) - ("distancescale", chan.DistanceScale) - ("pitch", chan.Pitch) - ("chanflags", chan.ChanFlags) - ("entchannel", chan.EntChannel) - ("priority", chan.Priority) - ("nearlimit", chan.NearLimit) - ("starttime", chan.StartTime) - ("rolloftype", chan.Rolloff.RolloffType) - ("rolloffmin", chan.Rolloff.MinDistance) - ("rolloffmax", chan.Rolloff.MaxDistance) - ("limitrange", chan.LimitRange); - - switch (chan.SourceType) - { - case SOURCE_None: break; - case SOURCE_Actor: arc("actor", chan.Actor); break; - case SOURCE_Sector: arc("sector", chan.Sector); break; - case SOURCE_Polyobj: arc("poly", chan.Poly); break; - case SOURCE_Unattached: arc.Array("point", chan.Point, 3); break; - default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; - } - arc.EndObject(); - } - return arc; -} - -//========================================================================== -// -// S_SerializeSounds -// -//========================================================================== - -void S_SerializeSounds(FSerializer &arc) -{ - FSoundChan *chan; - - GSnd->Sync(true); - - 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); - } - } - if (chans.Size() > 0 && arc.BeginArray("sounds")) - { - for (unsigned int i = chans.Size(); i-- != 0; ) - { - // Replace start time with sample position. - uint64_t start = chans[i]->StartTime; - chans[i]->StartTime = GSnd ? GSnd->GetPosition(chans[i]) : 0; - arc(nullptr, *chans[i]); - chans[i]->StartTime = start; - } - arc.EndArray(); - } - } - else - { - unsigned int count; - - S_StopAllChannels(); - if (arc.BeginArray("sounds")) - { - count = arc.ArraySize(); - for (unsigned int i = 0; i < count; ++i) - { - chan = (FSoundChan*)S_GetChannel(NULL); - arc(nullptr, *chan); - // Sounds always start out evicted when restored from a save. - chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; - } - arc.EndArray(); - } - // The two tic delay is to make sure any screenwipes have finished. - // This needs to be two because the game is run for one tic before - // the wipe so that it can produce a screen to wipe to. So if we - // only waited one tic to restart the sounds, they would start - // 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; - } - GSnd->Sync(false); - GSnd->UpdateSounds(); -} - - -//========================================================================== -// -// CCMD playsound -// -//========================================================================== - -CCMD (playsound) -{ - if (argv.argc() > 1) - { - FSoundID id = argv[1]; - if (id == 0) - { - Printf("'%s' is not a sound\n", argv[1]); - } - else - { - S_Sound (CHAN_AUTO | CHAN_UI, id, 1.f, ATTN_NONE); - } - } -} - -//========================================================================== -// -// CCMD loopsound -// -//========================================================================== - -CCMD (loopsound) -{ - if (players[consoleplayer].mo != NULL && !netgame && argv.argc() > 1) - { - FSoundID id = argv[1]; - if (id == 0) - { - Printf("'%s' is not a sound\n", argv[1]); - } - else - { - AActor *icon = Spawn(primaryLevel, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE); - if (icon != NULL) - { - S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE); - } - } - } -} - -//========================================================================== -// -// CCMD cachesound -// -//========================================================================== - -CCMD (cachesound) -{ - if (argv.argc() < 2) - { - Printf ("Usage: cachesound ...\n"); - return; - } - for (int i = 1; i < argv.argc(); ++i) - { - FSoundID sfxnum = argv[i]; - if (sfxnum != FSoundID(0)) - { - S_CacheSound (&S_sfx[sfxnum]); - } - } -} - - -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); -} - -// intentionally moved here to keep the s_music include out of the rest of the file. - -//========================================================================== -// -// S_PauseSound -// -// Stop music and sound effects, during game PAUSE. -//========================================================================== -#include "s_music.h" - -void S_PauseSound (bool notmusic, bool notsfx) -{ - if (!notmusic) - { - S_PauseMusic(); - } - if (!notsfx) - { - SoundPaused = true; - GSnd->SetSfxPaused (true, 0); - } -} - -DEFINE_ACTION_FUNCTION(DObject, S_PauseSound) -{ - PARAM_PROLOGUE; - PARAM_BOOL(notmusic); - PARAM_BOOL(notsfx); - S_PauseSound(notmusic, notsfx); - return 0; -} - -//========================================================================== -// -// S_ResumeSound -// -// Resume music and sound effects, after game PAUSE. -//========================================================================== - -void S_ResumeSound (bool notsfx) -{ - S_ResumeMusic(); - if (!notsfx) - { - SoundPaused = false; - GSnd->SetSfxPaused (false, 0); - } -} - -DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound) -{ - PARAM_PROLOGUE; - PARAM_BOOL(notsfx); - S_ResumeSound(notsfx); - return 0; -} - - -void S_UnloadAllSounds() +void SoundEngine::UnloadAllSounds() { for (unsigned i = 0; i < S_sfx.Size(); i++) { - S_UnloadSound(&S_sfx[i]); + UnloadSound(&S_sfx[i]); } } -CCMD (snd_status) +void SoundEngine::Reset() { - GSnd->PrintStatus (); -} - -CCMD (snd_reset) -{ - S_SoundReset(); -} - -void S_SoundReset() -{ - S_StopMusic(true); - S_EvictAllChannels(); + EvictAllChannels(); I_CloseSound(); I_InitSound(); - S_RestartMusic(); - S_RestoreEvictedChannels(); + RestoreEvictedChannels(); } -CCMD (snd_listdrivers) + +//========================================================================== +// +// S_FindSound +// +// Given a logical name, find the sound's index in S_sfx. +//========================================================================== + +int SoundEngine::FindSound(const char* logicalname) { - GSnd->PrintDriversList (); + 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; + } } -ADD_STAT (sound) +int SoundEngine::FindSoundByResID(int resid) { - return GSnd->GatherStats (); + 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 a4ef2a335..dc8c06d49 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; @@ -36,342 +35,37 @@ class FScanner; class FSerializer; struct FLevelLocals; -// -// SoundFX struct. -// -struct sfxinfo_t -{ - // Next field is for use by the system sound interface. - // A non-null data means the sound has been loaded. - SoundHandle data; - // Also for the sound interface. Used for 3D positional - // sounds, may be the same as data. - SoundHandle data3d; - - FString name; // [RH] Sound name defined in SNDINFO - int lumpnum; // lump number of sfx - - unsigned int next, index; // [RH] For hashing - float Volume; - - 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 bPlayerSilent:1; // This player sound is intentionally silent. - - int RawRate; // Sample rate to use when bLoadRAW is true - - int LoopStart; // -1 means no specific loop defined - - unsigned int link; - enum { NO_LINK = 0xffffffff }; - - FRolloffInfo Rolloff; - float Attenuation; // Multiplies the attenuation passed to S_Sound. - - void MarkUsed(); // Marks this sound as used. -}; - -// Rolloff types -enum -{ - ROLLOFF_Doom, // Linear rolloff with a logarithmic volume scale - ROLLOFF_Linear, // Linear rolloff with a linear volume scale - ROLLOFF_Log, // Logarithmic rolloff (standard hardware type) - ROLLOFF_Custom // Lookup volume from SNDCURVE -}; - -int S_FindSound (const char *logicalname); - -// the complete set of sound effects -extern TArray S_sfx; - -// An index into the S_sfx[] array. -class FSoundID -{ -public: - FSoundID() = default; - - FSoundID(int id) - { - ID = id; - } - FSoundID(const char *name) - { - ID = S_FindSound(name); - } - FSoundID(const FString &name) - { - ID = S_FindSound(name.GetChars()); - } - FSoundID(const FSoundID &other) = default; - FSoundID &operator=(const FSoundID &other) = default; - FSoundID &operator=(const char *name) - { - ID = S_FindSound(name); - return *this; - } - FSoundID &operator=(const FString &name) - { - ID = S_FindSound(name.GetChars()); - return *this; - } - bool operator !=(FSoundID other) const - { - return ID != other.ID; - } - bool operator !=(int other) const - { - return ID != other; - } - operator int() const - { - 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: - enum EDummy { NoInit }; - FSoundID(EDummy) {} -}; - -class FSoundIDNoInit : public FSoundID -{ -public: - FSoundIDNoInit() : FSoundID(NoInit) {} - using FSoundID::operator=; -}; - -extern FRolloffInfo S_Rolloff; -extern TArray S_SoundCurve; - - -// Information about one playing sound. -struct sector_t; -struct FPolyObj; -struct FSoundChan : public FISoundChannel -{ - FSoundChan *NextChan; // Next channel in this list. - FSoundChan **PrevChan; // Previous channel in this list. - FSoundID SoundID; // Sound ID of playing sound. - FSoundID OrgID; // Sound ID of sound used to start this channel. - float Volume; - int16_t Pitch; // Pitch variation. - uint8_t EntChannel; // Actor's sound channel. - int8_t Priority; - int16_t NearLimit; - uint8_t SourceType; - float LimitRange; - union - { - AActor *Actor; // Used for position and velocity. - const sector_t *Sector; // Sector for area sounds. - const FPolyObj *Poly; // Polyobject sound source. - float Point[3]; // Sound is not attached to any source. - }; -}; - -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 (); +#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); - -// Loads a sound, including any random sounds it might reference. -void S_CacheSound (sfxinfo_t *sfx); - -// Start sound for thing at -void S_Sound (int channel, FSoundID sfxid, float volume, float attenuation); -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); -void S_Sound (const FPolyObj *poly, int channel, FSoundID sfxid, float volume, float attenuation); -void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation); -void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sfxid, float volume, float attenuation); - -void S_SoundPitch (int channel, FSoundID sfxid, float volume, float attenuation, float pitch); -void S_SoundPitchActor (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation, float pitch); - -// [Nash] Used by ACS and DECORATE -void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local); -void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch); - -// sound channels -// channel 0 never willingly overrides -// other channels (1-7) always override a playing sound on that channel -// -// CHAN_AUTO searches down from channel 7 until it finds a channel not in use -// CHAN_WEAPON is for weapons -// CHAN_VOICE is for oof, sight, or other voice sounds -// CHAN_ITEM is for small things and item pickup -// CHAN_BODY is for generic body sounds -// CHAN_PICKUP can optionally be set as a local sound only for "compatibility" - -#define CHAN_AUTO 0 -#define CHAN_WEAPON 1 -#define CHAN_VOICE 2 -#define CHAN_ITEM 3 -#define CHAN_BODY 4 - -// Channel alias for sector sounds. These define how listener height is -// used when calculating 3D sound volume. -#define CHAN_FLOOR 1 // Sound comes from the floor. -#define CHAN_CEILING 2 // Sound comes from the ceiling. -#define CHAN_FULLHEIGHT 3 // Sound comes entire height of the sector. -#define CHAN_INTERIOR 4 // Sound comes height between floor and ceiling. - -// modifier flags -#define CHAN_LISTENERZ 8 -#define CHAN_MAYBE_LOCAL 16 -#define CHAN_UI 32 // Do not record sound in savegames. -#define CHAN_NOPAUSE 64 // Do not pause this sound in menus. -#define CHAN_AREA 128 // Sound plays from all around. Only valid with sector sounds. -#define CHAN_LOOP 256 - -#define CHAN_PICKUP (CHAN_ITEM|CHAN_MAYBE_LOCAL) - -#define CHAN_IS3D 1 // internal: Sound is 3D. -#define CHAN_EVICTED 2 // internal: Sound was evicted. -#define CHAN_FORGETTABLE 4 // internal: Forget channel data when sound stops. -#define CHAN_JUSTSTARTED 512 // internal: Sound has not been updated yet. -#define CHAN_ABSTIME 1024// internal: Start time is absolute and does not depend on current time. -#define CHAN_VIRTUAL 2048// internal: Channel is currently virtual -#define CHAN_NOSTOP 4096// only for A_PlaySound. Does not start if channel is playing something. - -// sound attenuation values -#define ATTN_NONE 0.f // full volume the entire level -#define ATTN_NORM 1.f -#define ATTN_IDLE 1.001f -#define ATTN_STATIC 3.f // diminish very rapidly with distance struct FSoundLoadBuffer; -int S_PickReplacement (int refid); -void S_CacheRandomSound (sfxinfo_t *sfx); - -// Checks if a copy of this sound is already playing. -bool S_CheckSingular (int sound_id); - -// Stops a sound emanating from one of an emitter's channels. -void S_StopSound (AActor *ent, int channel); -void S_StopSound (const sector_t *sec, int channel); -void S_StopSound (const FPolyObj *poly, int channel); - -// Stops an origin-less sound from playing from this channel. -void S_StopSound (int channel); - -// Stop sound for all channels -void S_StopAllChannels (void); - -// Is the sound playing on one of the emitter's channels? -bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id); -bool S_GetSoundPlayingInfo (const sector_t *sector, int sound_id); -bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id); - -bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id); - -// Change a playing sound's volume -void S_ChangeSoundVolume(AActor *actor, int channel, double volume); - -// Change a playing sound's pitch -void S_ChangeSoundPitch(AActor *actor, int channel, double pitch); -void S_SetPitch(FSoundChan *chan, float dpitch); - -// Moves all sounds from one mobj to another -void S_RelinkSound (AActor *from, AActor *to); - -// Stores/retrieves playing channel information in an archive. -void S_SerializeSounds(FSerializer &arc); - -// Stop and resume music, during game PAUSE. -void S_PauseSound (bool notmusic, bool notsfx); -void S_ResumeSound (bool notsfx); -void S_SetSoundPaused (int state); - -// -// Updates music & sounds -// -void S_UpdateSounds (AActor *listener); - -void S_RestoreEvictedChannels(); // [RH] S_sfx "maintenance" routines void S_ClearSoundData(); void S_ParseSndInfo (bool redefine); -void S_ParseReverbDef (); -void S_UnloadReverbDef (); -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); -void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch); // [RH] Prints sound debug info to the screen. // Modelled after Hexen's noise cheat. -void S_NoiseDebug (); -extern ReverbContainer *Environments; -extern ReverbContainer *DefaultEnvironments[26]; -void S_SetEnvironment (const ReverbContainer *settings); -ReverbContainer *S_FindEnvironment (const char *name); -ReverbContainer *S_FindEnvironment (int id); -void S_AddEnvironment (ReverbContainer *settings); -void S_UnloadAllSounds(); -void S_SoundReset(); - #endif diff --git a/src/sound/s_soundinternal.h b/src/sound/s_soundinternal.h new file mode 100644 index 000000000..17cf55e5b --- /dev/null +++ b/src/sound/s_soundinternal.h @@ -0,0 +1,398 @@ +#pragma once + +#include "i_sound.h" + +struct FRandomSoundList +{ + TArray Choices; + uint32_t Owner = 0; +}; + +extern int sfx_empty; + +// +// SoundFX struct. +// +struct sfxinfo_t +{ + // Next field is for use by the system sound interface. + // A non-null data means the sound has been loaded. + SoundHandle data; + // Also for the sound interface. Used for 3D positional + // sounds, may be the same as data. + SoundHandle data3d; + + FString name; // [RH] Sound name defined in SNDINFO + int lumpnum; // lump number of sfx + + unsigned int next, index; // [RH] For hashing + float Volume; + + 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 bLoadRAW: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 + + int LoopStart; // -1 means no specific loop defined + + unsigned int link; + enum { NO_LINK = 0xffffffff }; + + FRolloffInfo Rolloff; + float Attenuation; // Multiplies the attenuation passed to S_Sound. + + void MarkUsed(); // Marks this sound as used. +}; + +// Rolloff types +enum +{ + ROLLOFF_Doom, // Linear rolloff with a logarithmic volume scale + ROLLOFF_Linear, // Linear rolloff with a linear volume scale + ROLLOFF_Log, // Logarithmic rolloff (standard hardware type) + ROLLOFF_Custom // Lookup volume from SNDCURVE +}; + +int S_FindSound(const char *logicalname); +int S_FindSoundByResID(int snd_id); + +// An index into the S_sfx[] array. +class FSoundID +{ +public: + FSoundID() = default; + + static FSoundID byResId(int ndx) + { + return FSoundID(S_FindSoundByResID(ndx)); + } + FSoundID(int id) + { + ID = id; + } + FSoundID(const char *name) + { + ID = S_FindSound(name); + } + FSoundID(const FString &name) + { + ID = S_FindSound(name.GetChars()); + } + FSoundID(const FSoundID &other) = default; + FSoundID &operator=(const FSoundID &other) = default; + FSoundID &operator=(const char *name) + { + ID = S_FindSound(name); + return *this; + } + FSoundID &operator=(const FString &name) + { + ID = S_FindSound(name.GetChars()); + return *this; + } + bool operator !=(FSoundID other) const + { + return ID != other.ID; + } + bool operator !=(int other) const + { + return ID != other; + } + operator int() const + { + return ID; + } +private: + int ID; +protected: + enum EDummy { NoInit }; + FSoundID(EDummy) {} +}; + + class FSoundIDNoInit : public FSoundID +{ +public: + FSoundIDNoInit() : FSoundID(NoInit) {} + using FSoundID::operator=; +}; + + + +struct FSoundChan : public FISoundChannel +{ + FSoundChan *NextChan; // Next channel in this list. + FSoundChan **PrevChan; // Previous channel in this list. + FSoundID SoundID; // Sound ID of playing sound. + FSoundID OrgID; // Sound ID of sound used to start this channel. + float Volume; + int16_t Pitch; // Pitch variation. + uint8_t EntChannel; // Actor's sound channel. + int8_t Priority; + int16_t NearLimit; + uint8_t SourceType; + float LimitRange; + union + { + const void *Source; + float Point[3]; // Sound is not attached to any source. + }; +}; + + +// sound channels +// channel 0 never willingly overrides +// other channels (1-7) always override a playing sound on that channel +// +// CHAN_AUTO searches down from channel 7 until it finds a channel not in use +// CHAN_WEAPON is for weapons +// CHAN_VOICE is for oof, sight, or other voice sounds +// CHAN_ITEM is for small things and item pickup +// CHAN_BODY is for generic body sounds +// CHAN_PICKUP can optionally be set as a local sound only for "compatibility" + +enum +{ + CHAN_AUTO = 0, + CHAN_WEAPON = 1, + CHAN_VOICE = 2, + CHAN_ITEM = 3, + CHAN_BODY = 4, + CHAN_5 = 5, + CHAN_6 = 6, + CHAN_7 = 7, + + // Channel alias for sector sounds. These define how listener height is + // used when calculating 3D sound volume. + CHAN_FLOOR = 1, // Sound comes from the floor. + CHAN_CEILING = 2, // Sound comes from the ceiling. + CHAN_FULLHEIGHT = 3, // Sound comes entire height of the sector. + CHAN_INTERIOR = 4, // Sound comes height between floor and ceiling. + + // modifier flags + CHAN_LISTENERZ = 8, + CHAN_MAYBE_LOCAL = 16, + CHAN_UI = 32, // Do not record sound in savegames. + CHAN_NOPAUSE = 64, // Do not pause this sound in menus. + CHAN_AREA = 128, // Sound plays from all around. Only valid with sector sounds. + CHAN_LOOP = 256, + + CHAN_PICKUP = (CHAN_ITEM|CHAN_MAYBE_LOCAL), + + CHAN_IS3D = 1, // internal: Sound is 3D. + CHAN_EVICTED = 2, // internal: Sound was evicted. + CHAN_FORGETTABLE = 4, // internal: Forget channel data when sound stops. + CHAN_JUSTSTARTED = 512, // internal: Sound has not been updated yet. + CHAN_ABSTIME = 1024, // internal: Start time is absolute and does not depend on current time. + CHAN_VIRTUAL = 2048, // internal: Channel is currently virtual + CHAN_NOSTOP = 4096, // only for A_PlaySound. Does not start if channel is playing something. +}; + +// sound attenuation values +#define ATTN_NONE 0.f // full volume the entire level +#define ATTN_NORM 1.f +#define ATTN_IDLE 1.001f +#define ATTN_STATIC 3.f // diminish very rapidly with distance + +enum // This cannot be remain as this, but for now it has to suffice. +{ + SOURCE_None, // Sound is always on top of the listener. + SOURCE_Actor, // Sound is coming from an actor. + SOURCE_Sector, // Sound is coming from a sector. + SOURCE_Polyobj, // Sound is coming from a polyobject. + SOURCE_Unattached, // Sound is not attached to any particular emitter. +}; + + +extern ReverbContainer *Environments; +extern ReverbContainer *DefaultEnvironments[26]; + +void S_ParseReverbDef (); +void S_UnloadReverbDef (); +void S_SetEnvironment (const ReverbContainer *settings); +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; + +struct FReverbField +{ + int Min, Max; + float REVERB_PROPERTIES::* Float; + int REVERB_PROPERTIES::* Int; + unsigned int Flag; +}; + + diff --git a/src/utility/superfasthash.h b/src/utility/superfasthash.h new file mode 100644 index 000000000..d6b7dae0c --- /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 95b162a7a..dc52b342c 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)