From 22e692e21cadb7f4dec2e6bae2ab4ef2600fb212 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 8 Dec 2019 10:07:26 +0100 Subject: [PATCH 1/5] - started separating the sound engine code from game dependent parts. First step: Split the header. Todo: Abstract listener and sound source specifics out of the sound engine. --- src/sound/s_sound.cpp | 9 -- src/sound/s_sound.h | 240 +------------------------------- src/sound/s_soundinternal.h | 268 ++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 248 deletions(-) create mode 100644 src/sound/s_soundinternal.h diff --git a/src/sound/s_sound.cpp b/src/sound/s_sound.cpp index 08417277f..b5bb73234 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -86,15 +86,6 @@ // 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 diff --git a/src/sound/s_sound.h b/src/sound/s_sound.h index a4ef2a335..cf7b52900 100644 --- a/src/sound/s_sound.h +++ b/src/sound/s_sound.h @@ -36,178 +36,11 @@ 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; - +#include "s_soundinternal.h" // 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 (); // Per level startup code. // Kills playing sounds at start of level and starts new music. @@ -217,9 +50,6 @@ 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); @@ -235,61 +65,10 @@ void S_SoundPitchActor (AActor *ent, int channel, FSoundID sfxid, float volume, 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); @@ -321,11 +100,6 @@ 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 // @@ -336,8 +110,6 @@ 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); @@ -364,14 +136,4 @@ void A_PlaySound(AActor *self, int soundid, int channel, double volume, int loop // 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..0c2dd6801 --- /dev/null +++ b/src/sound/s_soundinternal.h @@ -0,0 +1,268 @@ +#pragma once + +#include "i_soundinternal.h" + + +// +// 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 ResourceId; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. + uint8_t PitchMask; + int16_t NearLimit; // 0 means unlimited + float LimitRange; // Range for sound limiting (squared for faster computations) + + unsigned bRandomHeader:1; + unsigned bPlayerReserve:1; + unsigned bLoadRAW:1; + unsigned bPlayerCompat:1; + unsigned b16bit:1; + unsigned bUsed:1; + unsigned bSingular:1; + unsigned bTentative:1; + unsigned 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); + +// the complete set of sound effects +extern TArray S_sfx; + +// 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; + } + 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; + + +typedef FVector3(*GetSourceOrigin)(const float *listenerpos, const void *source); + +class AActor; +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. + struct + { + void *Source; + GetSourceOrigin getOrigin; + }; + 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 (); + +// Loads a sound, including any random sounds it might reference. +void S_CacheSound (sfxinfo_t *sfx); + +// 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 + +// Checks if a copy of this sound is already playing. +bool S_CheckSingular (int sound_id); + +// Stop and resume music, during game PAUSE. +void S_PauseSound (bool notmusic, bool notsfx); +void S_ResumeSound (bool notsfx); +void S_SetSoundPaused (int state); + +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); +void S_UnloadAllSounds(); +void S_SoundReset(); + From fd181f469db0e55fc4d2a1e953c7a1eb931db332 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 8 Dec 2019 13:28:52 +0100 Subject: [PATCH 2/5] - reduced the dependency of the sound system on game state. Many of the simple wrappers have been moved to a separate file and the sound source handling has been abstracted. This is only the first phase, the work is not complete yet. Also changed the license of the sound code to BSD after verifying that this code bears no similarity to id's original sound code anymore, save for a few function names (which are due to be refactored out anyway.) --- src/CMakeLists.txt | 1 + src/playsim/p_acs.cpp | 4 +- src/scripting/vmthunks_actors.cpp | 8 +- src/sound/backend/i_soundinternal.h | 1 + src/sound/s_doomsound.cpp | 710 ++++++++++++++++++++++++ src/sound/s_doomsound.h | 47 ++ src/sound/s_sound.cpp | 832 ++++------------------------ src/sound/s_sound.h | 49 +- src/sound/s_soundinternal.h | 43 +- 9 files changed, 894 insertions(+), 801 deletions(-) create mode 100644 src/sound/s_doomsound.cpp create mode 100644 src/sound/s_doomsound.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc60d5619..668a5b0ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -879,6 +879,7 @@ set (PCH_SOURCES sound/s_advsound.cpp sound/s_environment.cpp sound/s_sndseq.cpp + sound/s_doomsound.cpp sound/s_sound.cpp sound/s_music.cpp serializer.cpp diff --git a/src/playsim/p_acs.cpp b/src/playsim/p_acs.cpp index eec8e752e..e28e8d72b 100644 --- a/src/playsim/p_acs.cpp +++ b/src/playsim/p_acs.cpp @@ -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/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/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index 75874b6c3..ef9e24db3 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -77,6 +77,7 @@ struct SoundListener bool underwater; bool valid; ReverbContainer *Environment; + void* ListenerObject; }; // Default rolloff information. diff --git a/src/sound/s_doomsound.cpp b/src/sound/s_doomsound.cpp new file mode 100644 index 000000000..865ac15bc --- /dev/null +++ b/src/sound/s_doomsound.cpp @@ -0,0 +1,710 @@ +/* +** 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" + + +extern SoundListener listener; +extern int RestartEvictionsAt; // do not restart evicted channels before this time +extern bool SoundPaused; // whether sound is paused +void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); + +//========================================================================== +// +// S_Sound - Unpositioned version +// +//========================================================================== + +void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float pitch) +{ + S_StartSound(SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); +} + +void S_Sound(int channel, FSoundID sound_id, float volume, float attenuation) +{ + S_StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); +} + +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; + + if (S_sfx[sound_id].bPlayerReserve) + { + sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); + if (sound_id <= 0) return; + } + + S_StartSound (SOURCE_Actor, ent, nullptr, channel, sound_id, volume, attenuation, 0, pitch); +} + +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; + + if (S_sfx[sound_id].bPlayerReserve) + { + sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); + if (sound_id <= 0) return; + } + + + FRolloffInfo rolloff; + + rolloff.RolloffType = ROLLOFF_Linear; + rolloff.MinDistance = mindist; + rolloff.MaxDistance = maxdist; + S_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; + S_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); + S_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; + S_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) +{ + S_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) +{ + S_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) +{ + S_StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + +//========================================================================== +// +// S_RelinkSound +// +// Moves all the sounds from one thing to another. If the destination is +// NULL, then the sound becomes a positioned sound. +//========================================================================== + +void S_RelinkSound (AActor *from, AActor *to) +{ + + FVector3 p = from->SoundPos(); + S_RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); +} + +//========================================================================== +// +// S_ChangeSoundVolume +// +//========================================================================== + +void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) +{ + S_ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); +} + +//========================================================================== +// +// S_ChangeSoundPitch +// +//========================================================================== + +void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) +{ + for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) + { + if (chan->SourceType == SOURCE_Actor && + chan->Source == actor && + chan->EntChannel == channel) + { + S_SetPitch(chan, (float)pitch); + return; + } + } + return; +} + +//========================================================================== +// +// S_GetSoundPlayingInfo +// +// Is a sound being played by a specific emitter? +//========================================================================== + +bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) +{ + return S_GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); +} + +bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) +{ + return S_GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); +} + +bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) +{ + return S_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; + } + return S_IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); +} + +//========================================================================== +// +// Sets the internal listener structure +// +//========================================================================== + +static void S_SetListener(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; + } + listener.ListenerObject = listenactor; +} + +//========================================================================== +// +// S_UpdateSounds +// +// Updates music & sounds +//========================================================================== + +void S_UpdateSounds (AActor *listenactor) +{ + // should never happen + S_SetListener(listenactor); + + for (auto Level : AllLevels()) + { + SN_UpdateActiveSequences(Level); + } + + S_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()) + { + 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; +} + + +CCMD (snd_status) +{ + GSnd->PrintStatus (); +} + +CCMD (snd_reset) +{ + S_SoundReset(); +} + +void S_SoundReset() +{ + S_StopMusic(true); + S_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..49f002a8f --- /dev/null +++ b/src/sound/s_doomsound.h @@ -0,0 +1,47 @@ +#pragma once + +// Information about one playing sound. +struct sector_t; +struct FPolyObj; + +void S_UpdateSounds(AActor* listenactor); + +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(); \ No newline at end of file diff --git a/src/sound/s_sound.cpp b/src/sound/s_sound.cpp index b5bb73234..a06be742c 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 @@ -95,15 +75,6 @@ // 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. -}; - // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- extern float S_GetMusicVolume (const char *music); @@ -111,25 +82,24 @@ 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 bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, int sourcetype, const void *actor, int channel); +static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen); static void S_ActivatePlayList(bool goBack); -static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); -static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, const FPolyObj *poly, +void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); +static void CalcPosVel(int type, const void *source, const float pt[3], int channel, int chanflags, FVector3 *pos, FVector3 *vel); static void CalcSectorSoundOrg(const DVector3 &listenpos, const sector_t *sec, int channum, FVector3 &res); static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, FVector3 &res); -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 +bool SoundPaused; // whether sound is paused +int RestartEvictionsAt; // do not restart evicted channels before this time +SoundListener listener; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -628,9 +598,9 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) // //========================================================================= -static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) +void 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); } @@ -642,8 +612,8 @@ 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 channum, int chanflags, FVector3 *pos, FVector3 *vel) +static void CalcPosVel(int type, const void *source, + const float pt[3], int channum, int chanflags, FVector3 *pos, FVector3 *vel) { if (pos != NULL) { @@ -663,6 +633,7 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, pos->Zero(); pgroup = 0; } + if (vel) vel->Zero(); // [BL] Moved this case out of the switch statement to make code easier // on static analysis. @@ -683,16 +654,27 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, break; case SOURCE_Actor: + { + auto actor = (AActor*)source; //assert(actor != NULL); if (actor != NULL) { DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, actor->Sector->PortalGroup); DVector3 posi = actor->Pos() - disp; *pos = { (float)posi.X, (float)posi.Z, (float)posi.Y }; + if (vel) + { + vel->X = float(actor->Vel.X * TICRATE); + vel->Y = float(actor->Vel.Z * TICRATE); + vel->Z = float(actor->Vel.Y * TICRATE); + } } break; + } case SOURCE_Sector: + { + auto sector = (sector_t*)source; assert(sector != NULL); if (sector != NULL) { @@ -706,15 +688,18 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, } else { - + pos->X = (float)(sector->centerspot.X - disp.X); pos->Z = (float)(sector->centerspot.Y - disp.Y); chanflags |= CHAN_LISTENERZ; } } break; + } case SOURCE_Polyobj: + { + auto poly = (FPolyObj*)source; assert(poly != NULL); if (poly != NULL) { @@ -725,6 +710,7 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, } break; } + } if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != NULL) { @@ -732,20 +718,6 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, } } } - 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(); - } - } } //========================================================================== @@ -797,7 +769,7 @@ static bool ValidatePosVel(const AActor *actor, const FVector3 &pos, const FVect static bool ValidatePosVel(const FSoundChan *const chan, const FVector3 &pos, const FVector3 &vel) { - return ValidatePosVel(chan->SourceType == SOURCE_Actor ? chan->Actor : nullptr, pos, vel); + return ValidatePosVel(chan->SourceType == SOURCE_Actor ? (AActor*)chan->Source : nullptr, pos, vel); } //========================================================================== @@ -885,9 +857,9 @@ static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, // //========================================================================== -static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyObj *poly, +FSoundChan *S_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; @@ -902,36 +874,15 @@ 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; - } + 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_Actor ? (AActor*)source : nullptr, pos, vel)) { return nullptr; } @@ -942,7 +893,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } else if ((chanflags & CHAN_MAYBE_LOCAL) && (compatflags & COMPATF_SILENTPICKUP)) { - if (actor != nullptr && actor != players[consoleplayer].camera) + if (source != nullptr && source == listener.ListenerObject) { return nullptr; } @@ -965,14 +916,7 @@ 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; @@ -1026,14 +970,14 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // 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)) + if (near_limit > 0 && S_CheckSoundLimit(sfx, pos, near_limit, limit_range, type, type == SOURCE_Actor? source : nullptr, channel)) { chanflags |= CHAN_EVICTED; } @@ -1056,7 +1000,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; } @@ -1066,11 +1010,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 (!S_IsChannelUsed(type, source, 0, &seen)) { channel = 0; } @@ -1078,7 +1022,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 (!S_IsChannelUsed(type, source, channel, &seen)) { break; } @@ -1091,26 +1035,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 && S_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; } } } @@ -1148,8 +1082,6 @@ 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); chan = (FSoundChan*)GSnd->StartSound3D (sfx->data3d, &listener, float(volume), rolloff, float(attenuation), pitch, basepriority, pos, vel, channel, startflags, NULL); } else @@ -1184,13 +1116,13 @@ 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) @@ -1249,15 +1181,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 && S_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); - 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); @@ -1274,158 +1203,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 @@ -1603,7 +1380,7 @@ 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) + int sourcetype, const void *actor, int channel) { FSoundChan *chan; int count; @@ -1615,7 +1392,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; } @@ -1644,8 +1421,7 @@ void S_StopSound (int channel) 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); } @@ -1661,63 +1437,15 @@ void S_StopSound (int channel) // //========================================================================== -void S_StopSound (AActor *actor, int channel) +void S_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))) - { - 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))) + FSoundChan* next = chan->NextChan; + if (chan->SourceType == sourcetype && + chan->Source == actor && + (chan->EntChannel == channel || channel < 0)) { S_StopChannel(chan); } @@ -1758,7 +1486,7 @@ void S_StopAllChannels () // NULL, then the sound becomes a positioned sound. //========================================================================== -void S_RelinkSound (AActor *from, AActor *to) +void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) { if (from == NULL) return; @@ -1767,20 +1495,19 @@ 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 { @@ -1798,7 +1525,7 @@ void S_RelinkSound (AActor *from, AActor *to) // //========================================================================== -void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) +void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume) { float volume = float(dvolume); // don't let volume get out of bounds @@ -1809,9 +1536,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; @@ -1827,12 +1554,12 @@ void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) // //========================================================================== -void S_ChangeSoundPitch(AActor *actor, int channel, double pitch) +void S_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); @@ -1856,49 +1583,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 S_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; } @@ -1917,7 +1610,7 @@ bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) // //========================================================================== -static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) +static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) { if (*seen & (1 << channel)) { @@ -1925,7 +1618,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) @@ -1943,16 +1636,11 @@ static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) // //========================================================================== -bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) +bool S_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) { @@ -2099,15 +1787,11 @@ void S_RestoreEvictedChannels() // Updates music & sounds //========================================================================== -void S_UpdateSounds (AActor *listenactor) +void S_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) { @@ -2121,57 +1805,16 @@ 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(); } } -//========================================================================== -// -// 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 @@ -2317,7 +1960,7 @@ void S_StopChannel(FSoundChan *chan) chan->ChanFlags |= CHAN_FORGETTABLE; if (chan->SourceType == SOURCE_Actor) { - chan->Actor = NULL; + chan->Source = NULL; } } GSnd->StopChannel(chan); @@ -2328,268 +1971,6 @@ void S_StopChannel(FSoundChan *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() { for (unsigned i = 0; i < S_sfx.Size(); i++) @@ -2598,32 +1979,11 @@ void S_UnloadAllSounds() } } -CCMD (snd_status) +void S_Reset() { - GSnd->PrintStatus (); -} - -CCMD (snd_reset) -{ - S_SoundReset(); -} - -void S_SoundReset() -{ - S_StopMusic(true); S_EvictAllChannels(); I_CloseSound(); I_InitSound(); - S_RestartMusic(); S_RestoreEvictedChannels(); } -CCMD (snd_listdrivers) -{ - GSnd->PrintDriversList (); -} - -ADD_STAT (sound) -{ - return GSnd->GatherStats (); -} diff --git a/src/sound/s_sound.h b/src/sound/s_sound.h index cf7b52900..18ab9c279 100644 --- a/src/sound/s_sound.h +++ b/src/sound/s_sound.h @@ -38,10 +38,6 @@ struct FLevelLocals; #include "s_soundinternal.h" -// Information about one playing sound. -struct sector_t; -struct FPolyObj; - // Per level startup code. // Kills playing sounds at start of level and starts new music. // @@ -52,59 +48,17 @@ void S_PrecacheLevel (FLevelLocals *l); // 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); struct FSoundLoadBuffer; int S_PickReplacement (int refid); void S_CacheRandomSound (sfxinfo_t *sfx); -// 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); - -// -// Updates music & sounds -// -void S_UpdateSounds (AActor *listener); - void S_RestoreEvictedChannels(); // [RH] S_sfx "maintenance" routines @@ -130,10 +84,11 @@ 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 (); +#include "s_doomsound.h" + #endif diff --git a/src/sound/s_soundinternal.h b/src/sound/s_soundinternal.h index 0c2dd6801..b8af56ef7 100644 --- a/src/sound/s_soundinternal.h +++ b/src/sound/s_soundinternal.h @@ -140,8 +140,6 @@ extern FRolloffInfo S_Rolloff; extern TArray S_SoundCurve; -typedef FVector3(*GetSourceOrigin)(const float *listenerpos, const void *source); - class AActor; struct sector_t; struct FPolyObj; @@ -160,14 +158,7 @@ struct FSoundChan : public FISoundChannel float LimitRange; union { - AActor *Actor; // Used for position and velocity. - const sector_t *Sector; // Sector for area sounds. - const FPolyObj *Poly; // Polyobject sound source. - struct - { - void *Source; - GetSourceOrigin getOrigin; - }; + const void *Source; float Point[3]; // Sound is not attached to any source. }; }; @@ -249,10 +240,40 @@ enum // Checks if a copy of this sound is already playing. bool S_CheckSingular (int sound_id); +enum // This cannot be remain as this, but for now it has to suffice. +{ + SOURCE_None, // Sound is always on top of the listener. + 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. +}; + + +// +// Updates music & sounds +// +void S_UpdateSounds (int time); + +FSoundChan* S_StartSound(int sourcetype, const void* source, + const FVector3* pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f); + +// Stops an origin-less sound from playing from this channel. +void S_StopSound(int channel); +void S_StopSound(int sourcetype, const void* actor, int channel); + +void S_RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); +void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume); +void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch); +bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id); + // Stop and resume music, during game PAUSE. void S_PauseSound (bool notmusic, bool notsfx); void S_ResumeSound (bool notsfx); void S_SetSoundPaused (int state); +bool S_GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id); +void S_UnloadAllSounds(); +void S_Reset(); extern ReverbContainer *Environments; extern ReverbContainer *DefaultEnvironments[26]; @@ -263,6 +284,4 @@ 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(); From b9582cc98e083416b39f388124d32f9cbaa3a6e2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 8 Dec 2019 21:22:53 +0100 Subject: [PATCH 3/5] - continued refactoring on sound code. The game independent part of the code has been mostly isolated. --- src/console/c_dispatch.h | 4 +- src/playsim/fragglescript/t_func.cpp | 2 +- src/playsim/p_acs.cpp | 22 +- src/r_data/sprites.cpp | 4 +- src/scripting/backend/codegen.cpp | 2 +- src/scripting/types.cpp | 2 +- src/scripting/vm/jit_move.cpp | 2 +- src/scripting/vm/vmexec.h | 2 +- src/serializer.cpp | 2 +- src/sound/backend/i_sound.cpp | 7 +- src/sound/backend/i_sound.h | 6 +- src/sound/backend/i_soundinternal.h | 1 + src/sound/backend/oalsound.cpp | 41 +- src/sound/s_advsound.cpp | 356 +------ src/sound/s_doomsound.cpp | 877 ++++++++++++++++-- src/sound/s_doomsound.h | 44 +- src/sound/s_sndseq.cpp | 4 +- src/sound/s_sound.cpp | 1223 +++++++++---------------- src/sound/s_sound.h | 25 +- src/sound/s_soundinternal.h | 243 +++-- src/utility/superfasthash.h | 5 + wadsrc/static/zscript/actors/actor.zs | 2 + 22 files changed, 1545 insertions(+), 1331 deletions(-) create mode 100644 src/utility/superfasthash.h diff --git a/src/console/c_dispatch.h b/src/console/c_dispatch.h index e21e977d1..f9fbd31d5 100644 --- a/src/console/c_dispatch.h +++ b/src/console/c_dispatch.h @@ -208,9 +208,7 @@ extern bool ParsingKeyConf, UnsafeExecutionContext; void ResetButtonTriggers (); // Call ResetTriggers for all buttons void ResetButtonStates (); // Same as above, but also clear bDown -extern unsigned int MakeKey (const char *s); -extern unsigned int MakeKey (const char *s, size_t len); -extern unsigned int SuperFastHash (const char *data, size_t len); +#include "superfasthash.h" void execLogfile(const char *fn, bool append = false); diff --git a/src/playsim/fragglescript/t_func.cpp b/src/playsim/fragglescript/t_func.cpp index 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 e28e8d72b..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) 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/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..616120b98 100644 --- a/src/sound/backend/i_sound.cpp +++ b/src/sound/backend/i_sound.cpp @@ -35,8 +35,6 @@ #include #include -#include "doomtype.h" - #include "oalsound.h" #include "i_module.h" @@ -52,6 +50,7 @@ #include "s_music.h" #include "zmusic/zmusic.h" + EXTERN_CVAR (Float, snd_sfxvolume) EXTERN_CVAR (Float, snd_musicvolume) CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -300,8 +299,8 @@ void I_InitSound () void I_CloseSound () { - // Free all loaded samples - S_UnloadAllSounds(); + // Free all loaded samples. Beware that the sound engine may already have been deleted. + if (soundEngine) soundEngine->UnloadAllSounds(); delete GSnd; GSnd = NULL; diff --git a/src/sound/backend/i_sound.h b/src/sound/backend/i_sound.h index 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 ef9e24db3..311ed22b0 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -122,4 +122,5 @@ class SoundStream; + #endif diff --git a/src/sound/backend/oalsound.cpp b/src/sound/backend/oalsound.cpp index 55df4b982..a16d42b41 100644 --- a/src/sound/backend/oalsound.cpp +++ b/src/sound/backend/oalsound.cpp @@ -544,22 +544,7 @@ static size_t GetChannelCount(ChannelConfig chans) static float GetRolloff(const FRolloffInfo *rolloff, float distance) { - if(distance <= rolloff->MinDistance) - return 1.f; - // Logarithmic rolloff has no max distance where it goes silent. - if(rolloff->RolloffType == ROLLOFF_Log) - return rolloff->MinDistance / - (rolloff->MinDistance + rolloff->RolloffFactor*(distance-rolloff->MinDistance)); - if(distance >= rolloff->MaxDistance) - return 0.f; - - float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); - if(rolloff->RolloffType == ROLLOFF_Linear) - return volume; - - if(rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve.Size() > 0) - return S_SoundCurve[int(S_SoundCurve.Size() * (1.f - volume))] / 127.f; - return (powf(10.f, volume) - 1.f) / 9.f; + return soundEngine->GetRolloff(rolloff, distance); } ALCdevice *OpenALSoundRenderer::InitDevice() @@ -983,7 +968,7 @@ void OpenALSoundRenderer::SetSfxVolume(float volume) { SfxVolume = volume; - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel != NULL) @@ -1359,7 +1344,7 @@ void OpenALSoundRenderer::UnloadSound(SoundHandle sfx) return; ALuint buffer = GET_PTRID(sfx.data); - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel) @@ -1495,7 +1480,7 @@ FISoundChannel *OpenALSoundRenderer::StartSound(SoundHandle sfx, float vol, int FreeSfx.Pop(); FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + if(!chan) chan = soundEngine->GetChannel(MAKE_PTRID(source)); else chan->SysChannel = MAKE_PTRID(source); chan->Rolloff.RolloffType = ROLLOFF_Log; @@ -1706,7 +1691,7 @@ FISoundChannel *OpenALSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener FreeSfx.Pop(); FISoundChannel *chan = reuse_chan; - if(!chan) chan = S_GetChannel(MAKE_PTRID(source)); + if(!chan) chan = soundEngine->GetChannel(MAKE_PTRID(source)); else chan->SysChannel = MAKE_PTRID(source); chan->Rolloff = *rolloff; @@ -1765,7 +1750,7 @@ void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) ALuint source = GET_PTRID(chan->SysChannel); // Release first, so it can be properly marked as evicted if it's being killed - S_ChannelEnded(chan); + soundEngine->ChannelEnded(chan); ALint state = AL_INITIAL; alGetSourcei(source, AL_SOURCE_STATE, &state); @@ -1789,7 +1774,7 @@ void OpenALSoundRenderer::ForceStopChannel(FISoundChannel *chan) ALuint source = GET_PTRID(chan->SysChannel); if(!source) return; - S_ChannelEnded(chan); + soundEngine->ChannelEnded(chan); FreeSource(source); } @@ -2009,7 +1994,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); // Apply the updated filters on the sources - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2022,7 +2007,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) } } - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2047,7 +2032,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) alFilterf(EnvFilters[1], AL_LOWPASS_GAIN, 1.f); alFilterf(EnvFilters[1], AL_LOWPASS_GAINHF, 1.f); - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2060,7 +2045,7 @@ void OpenALSoundRenderer::UpdateListener(SoundListener *listener) } } - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while (schan) { ALuint source = GET_PTRID(schan->SysChannel); @@ -2236,7 +2221,7 @@ void OpenALSoundRenderer::PurgeStoppedSources() if(state == AL_INITIAL || state == AL_PLAYING || state == AL_PAUSED) continue; - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); while(schan) { if(schan->SysChannel != NULL && src == GET_PTRID(schan->SysChannel)) @@ -2358,7 +2343,7 @@ void OpenALSoundRenderer::LoadReverb(const ReverbContainer *env) FSoundChan *OpenALSoundRenderer::FindLowestChannel() { - FSoundChan *schan = Channels; + FSoundChan *schan = soundEngine->GetChannels(); FSoundChan *lowest = NULL; while(schan) { diff --git a/src/sound/s_advsound.cpp b/src/sound/s_advsound.cpp index 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 index 865ac15bc..a9a5eb54c 100644 --- a/src/sound/s_doomsound.cpp +++ b/src/sound/s_doomsound.cpp @@ -66,11 +66,209 @@ #include "g_game.h" #include "s_music.h" +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + + +FBoolCVar noisedebug("noise", false, 0); // [RH] Print sound debugging info? +CUSTOM_CVAR(Int, snd_channels, 128, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // number of channels available +{ + if (self < 64) self = 64; +} +CVAR(Bool, snd_waterreverb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + + +static FString LastLocalSndInfo; +static FString LastLocalSndSeq; +void S_AddLocalSndInfo(int lump); + +class DoomSoundEngine : public SoundEngine +{ + // client specific parts of the sound engine go in this class. + void CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FVector3* pos, FVector3* vel) override; + bool ValidatePosVel(int sourcetype, const void* source, const FVector3& pos, const FVector3& vel); + TArray ReadSound(int lumpnum); + int PickReplacement(int refid); + +public: + DoomSoundEngine() = default; + void NoiseDebug(void); + void PrintSoundList(); +}; + +//========================================================================== +// +// S_Init +// +// Initializes sound stuff, including volume. Sets channels, SFX and +// music volume, allocates channel buffer, and sets S_sfx lookup. +//========================================================================== + +void S_Init() +{ + // Must be up before I_InitSound. + if (!soundEngine) + { + soundEngine = new DoomSoundEngine; + } + + I_InitSound(); + + // Heretic and Hexen have sound curve lookup tables. Doom does not. + int curvelump = Wads.CheckNumForName("SNDCURVE"); + TArray curve; + if (curvelump >= 0) + { + curve = Wads.ReadLumpIntoArray(curvelump); + } + soundEngine->Init(curve); +} + +//========================================================================== +// +// S_Shutdown +// +//========================================================================== + +void S_Shutdown() +{ + S_StopMusic(true); + + for (auto Level : AllLevels()) + { + SN_StopAllSequences(Level); + } + + if (soundEngine) + { + soundEngine->Shutdown(); + delete soundEngine; + soundEngine = nullptr; + } + + mus_playing.LastSong = ""; // If this isn't reset here, the song would attempt to resume at the most inpopportune time... + + if (GSnd != nullptr) + { + I_CloseSound(); + } +} + + +//========================================================================== +// +// S_Start +// +// Per level startup code. Kills playing sounds at start of level +// and starts new music. +//========================================================================== + +void S_Start() +{ + if (GSnd && soundEngine) + { + // kill all playing sounds at start of level (trust me - a good idea) + soundEngine->StopAllChannels(); + + // Check for local sound definitions. Only reload if they differ + // from the previous ones. + FString LocalSndInfo; + FString LocalSndSeq; + + // To be certain better check whether level is valid! + if (primaryLevel->info) + { + LocalSndInfo = primaryLevel->info->SoundInfo; + LocalSndSeq = primaryLevel->info->SndSeq; + } + + bool parse_ss = false; + + // This level uses a different local SNDINFO + if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !primaryLevel->info) + { + soundEngine->UnloadAllSounds(); + + // Parse the global SNDINFO + S_ParseSndInfo(true); + + if (LocalSndInfo.IsNotEmpty()) + { + // Now parse the local SNDINFO + int j = Wads.CheckNumForFullName(LocalSndInfo, true); + if (j >= 0) S_AddLocalSndInfo(j); + } + + // Also reload the SNDSEQ if the SNDINFO was replaced! + parse_ss = true; + } + else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0) + { + parse_ss = true; + } + + if (parse_ss) + { + S_ParseSndSeq(LocalSndSeq.IsNotEmpty() ? Wads.CheckNumForFullName(LocalSndSeq, true) : -1); + } + + LastLocalSndInfo = LocalSndInfo; + LastLocalSndSeq = LocalSndSeq; + } +} + +//========================================================================== +// +// S_PrecacheLevel +// +// Like R_PrecacheLevel, but for sounds. +// +//========================================================================== + +void S_PrecacheLevel(FLevelLocals* Level) +{ + if (GSnd && Level == primaryLevel) + { + soundEngine->MarkAllUnused(); + + AActor* actor; + auto iterator = Level->GetThinkerIterator(); + + // Precache all sounds known to be used by the currently spawned actors. + while ((actor = iterator.Next()) != nullptr) + { + IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds) + { + VMValue params[1] = { actor }; + VMCall(func, params, 1, nullptr, 0); + } + } + for (auto snd : gameinfo.PrecachedSounds) + { + soundEngine->MarkUsed(snd); + } + // Precache all extra sounds requested by this map. + for (auto snd : primaryLevel->info->PrecacheSounds) + { + soundEngine->MarkUsed(snd); + } + soundEngine->CacheMarkedSounds(); + } +} + + +//========================================================================== +// +// S_InitData +// +//========================================================================== + +void S_InitData() +{ + LastLocalSndInfo = LastLocalSndSeq = ""; + S_ParseSndInfo(false); + S_ParseSndSeq(-1); +} -extern SoundListener listener; -extern int RestartEvictionsAt; // do not restart evicted channels before this time -extern bool SoundPaused; // whether sound is paused -void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); //========================================================================== // @@ -80,12 +278,12 @@ void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float pitch) { - S_StartSound(SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); + soundEngine->StartSound(SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); } void S_Sound(int channel, FSoundID sound_id, float volume, float attenuation) { - S_StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); + soundEngine->StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); } DEFINE_ACTION_FUNCTION(DObject, S_Sound) @@ -100,23 +298,49 @@ DEFINE_ACTION_FUNCTION(DObject, S_Sound) return 0; } +//========================================================================== +// +// Common checking code for the actor sound functions +// +//========================================================================== + +static bool VerifyActorSound(AActor* ent, FSoundID& sound_id, int& channel) +{ + if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) + return false; + + if ((channel & CHAN_MAYBE_LOCAL) && (compatflags & COMPATF_SILENTPICKUP)) + { + if (soundEngine->isListener(ent)) + { + return false; + } + } + + if (soundEngine->isPlayerReserve(sound_id)) + { + sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); + if (sound_id <= 0) return false; + } + + if (compatflags & COMPATF_MAGICSILENCE) + { // For people who just can't play without a silent BFG. + channel = CHAN_WEAPON; + } + return true; +} + + //========================================================================== // // S_Sound - An actor is source // //========================================================================== + void S_SoundPitchActor(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation, float pitch) { - if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) - return; - - if (S_sfx[sound_id].bPlayerReserve) - { - sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); - if (sound_id <= 0) return; - } - - S_StartSound (SOURCE_Actor, ent, nullptr, channel, sound_id, volume, attenuation, 0, pitch); + if (VerifyActorSound(ent, sound_id, channel)) + soundEngine->StartSound (SOURCE_Actor, ent, nullptr, channel, sound_id, volume, attenuation, 0, pitch); } void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation) @@ -134,22 +358,15 @@ void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float at void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume, float mindist, float maxdist) { - if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) - return; - - if (S_sfx[sound_id].bPlayerReserve) + if (VerifyActorSound(ent, sound_id, channel)) { - sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); - if (sound_id <= 0) return; + FRolloffInfo rolloff; + + rolloff.RolloffType = ROLLOFF_Linear; + rolloff.MinDistance = mindist; + rolloff.MaxDistance = maxdist; + soundEngine->StartSound(SOURCE_Actor, ent, nullptr, channel, sound_id, volume, 1, &rolloff); } - - - FRolloffInfo rolloff; - - rolloff.RolloffType = ROLLOFF_Linear; - rolloff.MinDistance = mindist; - rolloff.MaxDistance = maxdist; - S_StartSound(SOURCE_Actor, ent, nullptr, channel, sound_id, volume, 1, &rolloff); } //========================================================================== @@ -161,7 +378,7 @@ void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation) { if (poly->Level != primaryLevel) return; - S_StartSound (SOURCE_Polyobj, poly, nullptr, channel, sound_id, volume, attenuation); + soundEngine->StartSound (SOURCE_Polyobj, poly, nullptr, channel, sound_id, volume, attenuation); } //========================================================================== @@ -175,7 +392,7 @@ void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sou if (Level != primaryLevel) return; // The sound system switches Y and Z around. FVector3 p((float)pos.X, (float)pos.Z, (float)pos.Y); - S_StartSound (SOURCE_Unattached, nullptr, &p, channel, sound_id, volume, attenuation); + soundEngine->StartSound (SOURCE_Unattached, nullptr, &p, channel, sound_id, volume, attenuation); } //========================================================================== @@ -187,7 +404,7 @@ void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sou void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation) { if (sec->Level != primaryLevel) return; - S_StartSound (SOURCE_Sector, sec, nullptr, channel, sfxid, volume, attenuation); + soundEngine->StartSound (SOURCE_Sector, sec, nullptr, channel, sfxid, volume, attenuation); } //========================================================================== @@ -250,7 +467,7 @@ void A_PlaySound(AActor *self, int soundid, int channel, double volume, int loop void S_StopSound (AActor *actor, int channel) { - S_StopSound(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); + soundEngine->StopSound(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); } @@ -264,7 +481,7 @@ void S_StopSound (AActor *actor, int channel) void S_StopSound (const sector_t *sec, int channel) { - S_StopSound(SOURCE_Sector, sec, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); + soundEngine->StopSound(SOURCE_Sector, sec, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); } //========================================================================== @@ -277,7 +494,7 @@ void S_StopSound (const sector_t *sec, int channel) void S_StopSound (const FPolyObj *poly, int channel) { - S_StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); + soundEngine->StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); } //========================================================================== @@ -285,14 +502,14 @@ void S_StopSound (const FPolyObj *poly, int channel) // S_RelinkSound // // Moves all the sounds from one thing to another. If the destination is -// NULL, then the sound becomes a positioned sound. +// nullptr, then the sound becomes a positioned sound. //========================================================================== void S_RelinkSound (AActor *from, AActor *to) { FVector3 p = from->SoundPos(); - S_RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); + soundEngine->RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); } //========================================================================== @@ -303,7 +520,7 @@ void S_RelinkSound (AActor *from, AActor *to) void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) { - S_ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); + soundEngine->ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); } //========================================================================== @@ -314,17 +531,7 @@ void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) { - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (chan->SourceType == SOURCE_Actor && - chan->Source == actor && - chan->EntChannel == channel) - { - S_SetPitch(chan, (float)pitch); - return; - } - } - return; + soundEngine->ChangeSoundPitch(SOURCE_Actor, actor, channel, pitch); } //========================================================================== @@ -336,17 +543,17 @@ void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) { - return S_GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); + return soundEngine->GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); } bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) { - return S_GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); + return soundEngine->GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); } bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) { - return S_GetSoundPlayingInfo(SOURCE_Polyobj, poly, sound_id); + return soundEngine->GetSoundPlayingInfo(SOURCE_Polyobj, poly, sound_id); } //========================================================================== @@ -359,9 +566,9 @@ bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) { if (compatflags & COMPATF_MAGICSILENCE) { - channel = CHAN_AUTO; + channel = CHAN_AUTO; // checks all channels } - return S_IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); + return soundEngine->IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); } //========================================================================== @@ -372,7 +579,8 @@ bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) static void S_SetListener(AActor *listenactor) { - if (listenactor != NULL) + SoundListener listener; + if (listenactor != nullptr) { listener.angle = (float)listenactor->Angles.Yaw.Radians(); /* @@ -393,10 +601,11 @@ static void S_SetListener(AActor *listenactor) listener.position.Zero(); listener.velocity.Zero(); listener.underwater = false; - listener.Environment = NULL; + listener.Environment = nullptr; listener.valid = false; } listener.ListenerObject = listenactor; + soundEngine->SetListener(listener); } //========================================================================== @@ -416,7 +625,7 @@ void S_UpdateSounds (AActor *listenactor) SN_UpdateActiveSequences(Level); } - S_UpdateSounds(primaryLevel->time); + soundEngine->UpdateSounds(primaryLevel->time); } //========================================================================== @@ -474,21 +683,11 @@ void S_SerializeSounds(FSerializer &arc) if (arc.isWriting()) { - TArray chans; - // Count channels and accumulate them so we can store them in // reverse order. That way, they will be in the same order when // reloaded later as they are now. - for (chan = Channels; chan != NULL; chan = chan->NextChan) - { - // If the sound is forgettable, this is as good a time as - // any to forget about it. And if it's a UI sound, it shouldn't - // be stored in the savegame. - if (!(chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_UI))) - { - chans.Push(chan); - } - } + TArray chans = soundEngine->AllActiveChannels(); + if (chans.Size() > 0 && arc.BeginArray("sounds")) { for (unsigned int i = chans.Size(); i-- != 0; ) @@ -506,13 +705,13 @@ void S_SerializeSounds(FSerializer &arc) { unsigned int count; - S_StopAllChannels(); + soundEngine->StopAllChannels(); if (arc.BeginArray("sounds")) { count = arc.ArraySize(); for (unsigned int i = 0; i < count; ++i) { - chan = (FSoundChan*)S_GetChannel(NULL); + chan = (FSoundChan*)soundEngine->GetChannel(nullptr); arc(nullptr, *chan); // Sounds always start out evicted when restored from a save. chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; @@ -526,12 +725,515 @@ void S_SerializeSounds(FSerializer &arc) // playing before the wipe, and depending on the synchronization // between the main thread and the mixer thread at the time, the // sounds might be heard briefly before pausing for the wipe. - RestartEvictionsAt = primaryLevel->time + 2; + soundEngine->SetRestartTime(primaryLevel->time + 2); } GSnd->Sync(false); GSnd->UpdateSounds(); } +//========================================================================== +// +// S_SetSoundPaused +// +// Called with state non-zero when the app is active, zero when it isn't. +// +//========================================================================== + +void S_SetSoundPaused(int state) +{ + if (state) + { + if (paused == 0) + { + S_ResumeSound(true); + if (GSnd != nullptr) + { + GSnd->SetInactive(SoundRenderer::INACTIVE_Active); + } + } + } + else + { + if (paused == 0) + { + S_PauseSound(false, true); + if (GSnd != nullptr) + { + GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ? + SoundRenderer::INACTIVE_Complete : + SoundRenderer::INACTIVE_Mute); + } + } + } + if (!netgame +#ifdef _DEBUG + && !demoplayback +#endif + ) + { + pauseext = !state; + } +} + + +//========================================================================== +// +// CalcSectorSoundOrg +// +// Returns the perceived sound origin for a sector. If the listener is +// inside the sector, then the origin is their location. Otherwise, the +// origin is from the nearest wall on the sector. +// +//========================================================================== + +static void CalcSectorSoundOrg(const DVector3& listenpos, const sector_t* sec, int channum, FVector3& pos) +{ + if (!(sec->Level->i_compatflags & COMPATF_SECTORSOUNDS)) + { + // Are we inside the sector? If yes, the closest point is the one we're on. + if (primaryLevel->PointInSector(listenpos.X, listenpos.Y) == sec) + { + pos.X = (float)listenpos.X; + pos.Z = (float)listenpos.Y; + } + else + { + // Find the closest point on the sector's boundary lines and use + // that as the perceived origin of the sound. + DVector2 xy; + sec->ClosestPoint(listenpos, xy); + pos.X = (float)xy.X; + pos.Z = (float)xy.Y; + } + } + else + { + pos.X = float(sec->centerspot.X); + pos.Z = float(sec->centerspot.Y); + } + + // Set sound vertical position based on channel. + if (channum == CHAN_FLOOR) + { + pos.Y = (float)MIN(sec->floorplane.ZatPoint(listenpos), listenpos.Z); + } + else if (channum == CHAN_CEILING) + { + pos.Y = (float)MAX(sec->ceilingplane.ZatPoint(listenpos), listenpos.Z); + } + else if (channum == CHAN_INTERIOR) + { + pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); + } +} + +//========================================================================== +// +// CalcPolySoundOrg +// +// Returns the perceived sound origin for a polyobject. This is similar to +// CalcSectorSoundOrg, except there is no special case for being "inside" +// a polyobject, so the sound literally comes from the polyobject's walls. +// Vertical position of the sound always comes from the visible wall. +// +//========================================================================== + +static void CalcPolyobjSoundOrg(const DVector3& listenpos, const FPolyObj* poly, FVector3& pos) +{ + side_t* side; + sector_t* sec; + + DVector2 ppos; + poly->ClosestPoint(listenpos, ppos, &side); + pos.X = (float)ppos.X; + pos.Z = (float)ppos.Y; + sec = side->sector; + pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); +} + +//========================================================================= +// +// CalcPosVel +// +// The game specific part of the sound updater. +// +//========================================================================= + +void DoomSoundEngine::CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FVector3* pos, FVector3* vel) +{ + if (pos != nullptr) + { + DVector3 listenpos; + int pgroup; + AActor* listener = players[consoleplayer].camera; + + if (listener != nullptr) + { + listenpos = listener->Pos(); + *pos = listener->SoundPos(); + pgroup = listener->Sector->PortalGroup; + } + else + { + listenpos.Zero(); + pos->Zero(); + pgroup = 0; + } + if (vel) vel->Zero(); + + // [BL] Moved this case out of the switch statement to make code easier + // on static analysis. + if (type == SOURCE_Unattached) + { + sector_t* sec = primaryLevel->PointInSector(pt[0], pt[2]); + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sec->PortalGroup); + pos->X = pt[0] - (float)disp.X; + pos->Y = !(chanflags & CHAN_LISTENERZ) ? pt[1] : (float)listenpos.Z; + pos->Z = pt[2] - (float)disp.Y; + } + else + { + switch (type) + { + case SOURCE_None: + default: + break; + + case SOURCE_Actor: + { + auto actor = (AActor*)source; + //assert(actor != nullptr); + if (actor != nullptr) + { + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, actor->Sector->PortalGroup); + DVector3 posi = actor->Pos() - disp; + *pos = { (float)posi.X, (float)posi.Z, (float)posi.Y }; + if (vel) + { + vel->X = float(actor->Vel.X * TICRATE); + vel->Y = float(actor->Vel.Z * TICRATE); + vel->Z = float(actor->Vel.Y * TICRATE); + } + } + break; + } + + case SOURCE_Sector: + { + auto sector = (sector_t*)source; + assert(sector != nullptr); + if (sector != nullptr) + { + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sector->PortalGroup); + if (chanflags & CHAN_AREA) + { + // listener must be reversely offset to calculate the proper sound origin. + CalcSectorSoundOrg(listenpos + disp, sector, channum, *pos); + pos->X -= (float)disp.X; + pos->Z -= (float)disp.Y; + } + else + { + + pos->X = (float)(sector->centerspot.X - disp.X); + pos->Z = (float)(sector->centerspot.Y - disp.Y); + chanflags |= CHAN_LISTENERZ; + } + } + break; + } + + case SOURCE_Polyobj: + { + auto poly = (FPolyObj*)source; + assert(poly != nullptr); + if (poly != nullptr) + { + DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup); + CalcPolyobjSoundOrg(listenpos + disp, poly, *pos); + pos->X -= (float)disp.X; + pos->Z -= (float)disp.Y; + } + break; + } + } + + if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != nullptr) + { + pos->Y = (float)listenpos.Z; + } + } + } +} + +//========================================================================== +// +// ValidatePosVel +// +//========================================================================== + +inline bool Validate(const float value, const float limit) +{ + return value >= -limit && value <= limit; +} + +static bool Validate(const FVector3& value, const float limit, const char* const name, const AActor* const actor) +{ + const bool valid = + Validate(value.X, limit) + && Validate(value.Y, limit) + && Validate(value.Z, limit); + + if (!valid) + { + // Sound position and velocity have Y and Z axes swapped comparing to map coordinate system + Printf(TEXTCOLOR_RED "Invalid sound %s " TEXTCOLOR_WHITE "(%f, %f, %f)", name, value.X, value.Z, value.Y); + + if (actor == nullptr) + { + Printf("\n"); + } + else + { + Printf(TEXTCOLOR_RED " for actor of class " TEXTCOLOR_WHITE "%s\n", actor->GetClass()->TypeName.GetChars()); + } + } + + return valid; +} + +bool DoomSoundEngine::ValidatePosVel(int sourcetype, const void* source, const FVector3& pos, const FVector3& vel) +{ + if (sourcetype != SOURCE_Actor) return true; + // The actual limit for map coordinates + auto actor = (AActor*)source; + static const float POSITION_LIMIT = 1024.f * 1024.f; + const bool valid = Validate(pos, POSITION_LIMIT, "position", actor); + + // The maximum velocity is enough to travel through entire map in one tic + static const float VELOCITY_LIMIT = 2 * POSITION_LIMIT * TICRATE; + return Validate(vel, VELOCITY_LIMIT, "velocity", actor) && valid; +} + +//========================================================================== +// +// This is to avoid hardscoding the dependency on Wads into the sound engine +// +//========================================================================== + +TArray DoomSoundEngine::ReadSound(int lumpnum) +{ + auto wlump = Wads.OpenLumpReader(lumpnum); + return wlump.Read(); +} + +//========================================================================== +// +// S_PickReplacement +// +// This is overridden to use a synchronized RNG. +// +//========================================================================== +static FRandom pr_randsound("RandSound"); + +int DoomSoundEngine::PickReplacement(int refid) +{ + while (S_sfx[refid].bRandomHeader) + { + const FRandomSoundList* list = &S_rnd[S_sfx[refid].link]; + refid = list->Choices[pr_randsound(list->Choices.Size())]; + } + return refid; +} + +//========================================================================== +// +// S_NoiseDebug +// +// [RH] Print sound debug info. Called by status bar. +//========================================================================== + +void DoomSoundEngine::NoiseDebug() +{ + FSoundChan* chan; + FVector3 listener; + FVector3 origin; + int y, color; + + y = 32 * CleanYfac; + screen->DrawText(NewConsoleFont, CR_YELLOW, 0, y, "*** SOUND DEBUG INFO ***", TAG_DONE); + y += NewConsoleFont->GetHeight(); + + screen->DrawText(NewConsoleFont, CR_GOLD, 0, y, "name", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 70, y, "x", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 120, y, "y", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 170, y, "z", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 220, y, "vol", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 260, y, "dist", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 300, y, "chan", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 340, y, "pri", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 380, y, "flags", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 460, y, "aud", TAG_DONE); + screen->DrawText(NewConsoleFont, CR_GOLD, 520, y, "pos", TAG_DONE); + y += NewConsoleFont->GetHeight(); + + if (Channels == nullptr) + { + return; + } + + + listener = players[consoleplayer].camera->SoundPos(); + + // Display the oldest channel first. + for (chan = Channels; chan->NextChan != nullptr; chan = chan->NextChan) + { + } + while (y < SCREENHEIGHT - 16) + { + char temp[32]; + + SoundEngine::CalcPosVel(chan, &origin, nullptr); + color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY; + + // Name + Wads.GetLumpName(temp, S_sfx[chan->SoundID].lumpnum); + temp[8] = 0; + screen->DrawText(NewConsoleFont, color, 0, y, temp, TAG_DONE); + + if (!(chan->ChanFlags & CHAN_IS3D)) + { + screen->DrawText(NewConsoleFont, color, 70, y, "---", TAG_DONE); // X + screen->DrawText(NewConsoleFont, color, 120, y, "---", TAG_DONE); // Y + screen->DrawText(NewConsoleFont, color, 170, y, "---", TAG_DONE); // Z + screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); // Distance + } + else + { + // X coordinate + mysnprintf(temp, countof(temp), "%.0f", origin.X); + screen->DrawText(NewConsoleFont, color, 70, y, temp, TAG_DONE); + + // Y coordinate + mysnprintf(temp, countof(temp), "%.0f", origin.Z); + screen->DrawText(NewConsoleFont, color, 120, y, temp, TAG_DONE); + + // Z coordinate + mysnprintf(temp, countof(temp), "%.0f", origin.Y); + screen->DrawText(NewConsoleFont, color, 170, y, temp, TAG_DONE); + + // Distance + if (chan->DistanceScale > 0) + { + mysnprintf(temp, countof(temp), "%.0f", (origin - listener).Length()); + screen->DrawText(NewConsoleFont, color, 260, y, temp, TAG_DONE); + } + else + { + screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); + } + } + + // Volume + mysnprintf(temp, countof(temp), "%.2g", chan->Volume); + screen->DrawText(NewConsoleFont, color, 220, y, temp, TAG_DONE); + + // Channel + mysnprintf(temp, countof(temp), "%d", chan->EntChannel); + screen->DrawText(NewConsoleFont, color, 300, y, temp, TAG_DONE); + + // Priority + mysnprintf(temp, countof(temp), "%d", chan->Priority); + screen->DrawText(NewConsoleFont, color, 340, y, temp, TAG_DONE); + + // Flags + mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE%sV", + (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + (chan->ChanFlags & CHAN_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); + screen->DrawText(NewConsoleFont, color, 380, y, temp, TAG_DONE); + + // Audibility + mysnprintf(temp, countof(temp), "%.4f", GSnd->GetAudibility(chan)); + screen->DrawText(NewConsoleFont, color, 460, y, temp, TAG_DONE); + + // Position + mysnprintf(temp, countof(temp), "%u", GSnd->GetPosition(chan)); + screen->DrawText(NewConsoleFont, color, 520, y, temp, TAG_DONE); + + + y += NewConsoleFont->GetHeight(); + if (chan->PrevChan == &Channels) + { + break; + } + chan = (FSoundChan*)((size_t)chan->PrevChan - myoffsetof(FSoundChan, NextChan)); + } +} + +void S_NoiseDebug(void) +{ + static_cast(soundEngine)->NoiseDebug(); +} + + +//========================================================================== +// +// CCMD soundlist +// +//========================================================================== + +void DoomSoundEngine::PrintSoundList() +{ + auto &S_sfx = soundEngine->GetSounds(); + char lumpname[9]; + unsigned int i; + + lumpname[8] = 0; + for (i = 0; i < S_sfx.Size(); i++) + { + const sfxinfo_t* sfx = &S_sfx[i]; + if (sfx->bRandomHeader) + { + Printf("%3d. %s -> #%d {", i, sfx->name.GetChars(), sfx->link); + const FRandomSoundList* list = &S_rnd[sfx->link]; + for (auto& me : list->Choices) + { + Printf(" %s ", S_sfx[me].name.GetChars()); + } + Printf("}\n"); + } + else if (sfx->bPlayerReserve) + { + Printf("%3d. %s <>\n", i, sfx->name.GetChars(), sfx->link); + } + else if (S_sfx[i].lumpnum != -1) + { + Wads.GetLumpName(lumpname, sfx->lumpnum); + Printf("%3d. %s (%s)\n", i, sfx->name.GetChars(), lumpname); + } + else if (S_sfx[i].link != sfxinfo_t::NO_LINK) + { + Printf("%3d. %s -> %s\n", i, sfx->name.GetChars(), S_sfx[sfx->link].name.GetChars()); + } + else + { + Printf("%3d. %s **not present**\n", i, sfx->name.GetChars()); + } + Printf(" PitchMask = %d\n", sfx->PitchMask); + } +} + +CCMD(soundlist) +{ + static_cast(soundEngine)->PrintSoundList(); +} //========================================================================== // @@ -563,7 +1265,7 @@ CCMD (playsound) CCMD (loopsound) { - if (players[consoleplayer].mo != NULL && !netgame && argv.argc() > 1) + if (players[consoleplayer].mo != nullptr && !netgame && argv.argc() > 1) { FSoundID id = argv[1]; if (id == 0) @@ -573,7 +1275,7 @@ CCMD (loopsound) else { AActor *icon = Spawn(primaryLevel, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE); - if (icon != NULL) + if (icon != nullptr) { S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE); } @@ -599,7 +1301,7 @@ CCMD (cachesound) FSoundID sfxnum = argv[i]; if (sfxnum != FSoundID(0)) { - S_CacheSound (&S_sfx[sfxnum]); + soundEngine->CacheSound(sfxnum); } } } @@ -607,21 +1309,7 @@ CCMD (cachesound) CCMD(listsoundchannels) { - FSoundChan *chan; - int count = 0; - for (chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (!(chan->ChanFlags & CHAN_EVICTED)) - { - FVector3 chanorigin; - - CalcPosVel(chan, &chanorigin, NULL); - - Printf("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)chan->SoundID, chanorigin.X, chanorigin.Y, chanorigin.Z); - count++; - } - } - Printf("%d sounds playing\n", count); + Printf("%s", soundEngine->ListSoundChannels().GetChars()); } // intentionally moved here to keep the s_music include out of the rest of the file. @@ -642,7 +1330,7 @@ void S_PauseSound (bool notmusic, bool notsfx) } if (!notsfx) { - SoundPaused = true; + soundEngine->SetPaused(true); GSnd->SetSfxPaused (true, 0); } } @@ -668,7 +1356,7 @@ void S_ResumeSound (bool notsfx) S_ResumeMusic(); if (!notsfx) { - SoundPaused = false; + soundEngine->SetPaused(false); GSnd->SetSfxPaused (false, 0); } } @@ -682,6 +1370,7 @@ DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound) } + CCMD (snd_status) { GSnd->PrintStatus (); @@ -695,7 +1384,7 @@ CCMD (snd_reset) void S_SoundReset() { S_StopMusic(true); - S_Reset(); + soundEngine->Reset(); S_RestartMusic(); } diff --git a/src/sound/s_doomsound.h b/src/sound/s_doomsound.h index 49f002a8f..e36cf6030 100644 --- a/src/sound/s_doomsound.h +++ b/src/sound/s_doomsound.h @@ -3,8 +3,22 @@ // Information about one playing sound. struct sector_t; struct FPolyObj; +struct FLevelLocals; + +void S_Init(); +void S_InitData(); +void S_Start(); +void S_Shutdown(); void S_UpdateSounds(AActor* listenactor); +void S_SetSoundPaused(int state); + +void S_PrecacheLevel(FLevelLocals* l); + +// Start sound for thing at +void S_Sound(int channel, FSoundID sfxid, float volume, float attenuation); +void S_SoundPitch(int channel, FSoundID sfxid, float volume, float attenuation, float pitch); + void S_Sound (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation); void S_SoundMinMaxDist (AActor *ent, int channel, FSoundID sfxid, float volume, float mindist, float maxdist); @@ -44,4 +58,32 @@ void S_SerializeSounds(FSerializer &arc); void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch); static void S_SetListener(AActor *listenactor); -void S_SoundReset(); \ No newline at end of file +void S_SoundReset(); +void S_ResumeSound(bool state); +void S_PauseSound(bool state1, bool state); +void S_NoiseDebug(); + +inline void S_StopSound(int chan) +{ + soundEngine->StopSound(chan); +} + +inline void S_StopAllChannels() +{ + soundEngine->StopAllChannels(); +} + +inline const char* S_GetSoundName(FSoundID id) +{ + return soundEngine->GetSoundName(id); +} + +inline int S_FindSound(const char* logicalname) +{ + return soundEngine->FindSound(logicalname); +} + +inline int S_FindSoundByResID(int rid) +{ + return soundEngine->FindSoundByResID(rid); +} diff --git a/src/sound/s_sndseq.cpp b/src/sound/s_sndseq.cpp index 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 a06be742c..7b6492c2a 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -41,262 +41,33 @@ #include "i_system.h" #include "i_sound.h" -#include "i_music.h" #include "s_sound.h" -#include "s_sndseq.h" -#include "s_playlist.h" -#include "c_dispatch.h" -#include "m_random.h" -#include "w_wad.h" -#include "p_local.h" -#include "doomstat.h" -#include "cmdlib.h" -#include "v_video.h" -#include "v_text.h" -#include "a_sharedglobal.h" -#include "gstrings.h" -#include "gi.h" -#include "po_man.h" -#include "serializer.h" -#include "d_player.h" -#include "g_levellocals.h" -#include "vm.h" -#include "g_game.h" -#include "s_music.h" - -// MACROS ------------------------------------------------------------------ - -#define NORM_PITCH 128 -#define NORM_PRIORITY 64 -#define NORM_SEP 0 - -#define S_PITCH_PERTURB 1 -#define S_STEREO_SWING 0.75 - -// TYPES ------------------------------------------------------------------- - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -extern float S_GetMusicVolume (const char *music); - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -void I_CloseSound(); +#include "m_swap.h" +#include "superfasthash.h" -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer); -static bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, int sourcetype, const void *actor, int channel); -static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen); -static void S_ActivatePlayList(bool goBack); -void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); -static void CalcPosVel(int type, const void *source, - const float pt[3], int channel, int chanflags, FVector3 *pos, FVector3 *vel); -static void CalcSectorSoundOrg(const DVector3 &listenpos, const sector_t *sec, int channum, FVector3 &res); -static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, FVector3 &res); - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -bool SoundPaused; // whether sound is paused -int RestartEvictionsAt; // do not restart evicted channels before this time -SoundListener listener; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -int sfx_empty; - -FSoundChan *Channels; -FSoundChan *FreeChannels; - -FRolloffInfo S_Rolloff; -TArray S_SoundCurve; - -FBoolCVar noisedebug ("noise", false, 0); // [RH] Print sound debugging info? -CUSTOM_CVAR (Int, snd_channels, 128, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number of channels available +enum { - if (self < 64) self = 64; -} -CVAR(Bool, snd_waterreverb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + DEFAULT_PITCH = 128, +}; -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// S_NoiseDebug -// -// [RH] Print sound debug info. Called by status bar. -//========================================================================== - -void S_NoiseDebug (void) -{ - FSoundChan *chan; - FVector3 listener; - FVector3 origin; - int y, color; - - y = 32 * CleanYfac; - screen->DrawText (NewConsoleFont, CR_YELLOW, 0, y, "*** SOUND DEBUG INFO ***", TAG_DONE); - y += NewConsoleFont->GetHeight(); - - screen->DrawText (NewConsoleFont, CR_GOLD, 0, y, "name", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 70, y, "x", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 120, y, "y", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 170, y, "z", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 220, y, "vol", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 260, y, "dist", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 300, y, "chan", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 340, y, "pri", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 380, y, "flags", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 460, y, "aud", TAG_DONE); - screen->DrawText (NewConsoleFont, CR_GOLD, 520, y, "pos", TAG_DONE); - y += NewConsoleFont->GetHeight(); - - if (Channels == NULL) - { - return; - } - - - listener = players[consoleplayer].camera->SoundPos(); - - // Display the oldest channel first. - for (chan = Channels; chan->NextChan != NULL; chan = chan->NextChan) - { } - while (y < SCREENHEIGHT - 16) - { - char temp[32]; - - CalcPosVel(chan, &origin, NULL); - color = (chan->ChanFlags & CHAN_LOOP) ? CR_BROWN : CR_GREY; - - // Name - Wads.GetLumpName (temp, S_sfx[chan->SoundID].lumpnum); - temp[8] = 0; - screen->DrawText (NewConsoleFont, color, 0, y, temp, TAG_DONE); - - if (!(chan->ChanFlags & CHAN_IS3D)) - { - screen->DrawText(NewConsoleFont, color, 70, y, "---", TAG_DONE); // X - screen->DrawText(NewConsoleFont, color, 120, y, "---", TAG_DONE); // Y - screen->DrawText(NewConsoleFont, color, 170, y, "---", TAG_DONE); // Z - screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); // Distance - } - else - { - // X coordinate - mysnprintf(temp, countof(temp), "%.0f", origin.X); - screen->DrawText(NewConsoleFont, color, 70, y, temp, TAG_DONE); - - // Y coordinate - mysnprintf(temp, countof(temp), "%.0f", origin.Z); - screen->DrawText(NewConsoleFont, color, 120, y, temp, TAG_DONE); - - // Z coordinate - mysnprintf(temp, countof(temp), "%.0f", origin.Y); - screen->DrawText(NewConsoleFont, color, 170, y, temp, TAG_DONE); - - // Distance - if (chan->DistanceScale > 0) - { - mysnprintf(temp, countof(temp), "%.0f", (origin - listener).Length()); - screen->DrawText(NewConsoleFont, color, 260, y, temp, TAG_DONE); - } - else - { - screen->DrawText(NewConsoleFont, color, 260, y, "---", TAG_DONE); - } - } - - // Volume - mysnprintf(temp, countof(temp), "%.2g", chan->Volume); - screen->DrawText(NewConsoleFont, color, 220, y, temp, TAG_DONE); - - // Channel - mysnprintf(temp, countof(temp), "%d", chan->EntChannel); - screen->DrawText(NewConsoleFont, color, 300, y, temp, TAG_DONE); - - // Priority - mysnprintf(temp, countof(temp), "%d", chan->Priority); - screen->DrawText(NewConsoleFont, color, 340, y, temp, TAG_DONE); - - // Flags - mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE%sV", - (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHAN_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); - screen->DrawText(NewConsoleFont, color, 380, y, temp, TAG_DONE); - - // Audibility - mysnprintf(temp, countof(temp), "%.4f", GSnd->GetAudibility(chan)); - screen->DrawText(NewConsoleFont, color, 460, y, temp, TAG_DONE); - - // Position - mysnprintf(temp, countof(temp), "%u", GSnd->GetPosition(chan)); - screen->DrawText(NewConsoleFont, color, 520, y, temp, TAG_DONE); - - - y += NewConsoleFont->GetHeight(); - if (chan->PrevChan == &Channels) - { - break; - } - chan = (FSoundChan *)((size_t)chan->PrevChan - myoffsetof(FSoundChan, NextChan)); - } -} - -static FString LastLocalSndInfo; -static FString LastLocalSndSeq; -void S_AddLocalSndInfo(int lump); +SoundEngine* soundEngine; +int sfx_empty = -1; //========================================================================== // // S_Init // -// Initializes sound stuff, including volume. Sets channels, SFX and -// music volume, allocates channel buffer, and sets S_sfx lookup. //========================================================================== -void S_Init () +void SoundEngine::Init(TArray &curve) { - int curvelump; - - // Heretic and Hexen have sound curve lookup tables. Doom does not. - I_InitSound(); - curvelump = Wads.CheckNumForName ("SNDCURVE"); - if (curvelump >= 0) - { - S_SoundCurve.Resize(Wads.LumpLength (curvelump)); - Wads.ReadLump(curvelump, S_SoundCurve.Data()); - } - else - { - S_SoundCurve.Clear(); - } - // Free all channels for use. while (Channels != NULL) { - S_ReturnChannel(Channels); + ReturnChannel(Channels); } -} - -//========================================================================== -// -// S_InitData -// -//========================================================================== - -void S_InitData () -{ - LastLocalSndInfo = LastLocalSndSeq = ""; - S_ParseSndInfo (false); - S_ParseSndSeq (-1); + S_SoundCurve = std::move(curve); } //========================================================================== @@ -305,13 +76,11 @@ void S_InitData () // //========================================================================== -void S_Shutdown () +void SoundEngine::Shutdown () { FSoundChan *chan, *next; - S_StopMusic(true); - mus_playing.LastSong = ""; // If this isn't reset here, the song would attempt resume at the most inpopportune time... - S_StopAllChannels(); + StopAllChannels(); if (GSnd) GSnd->UpdateSounds(); @@ -322,139 +91,48 @@ void S_Shutdown () delete chan; } FreeChannels = NULL; - - if (GSnd != NULL) - { - I_CloseSound(); - } } - //========================================================================== // -// S_Start +// MarkUsed // -// Per level startup code. Kills playing sounds at start of level -// and starts new music. //========================================================================== -void S_Start () +void SoundEngine::MarkUsed(int id) { - if (GSnd) + if ((unsigned)id < S_sfx.Size()) { - // kill all playing sounds at start of level (trust me - a good idea) - S_StopAllChannels(); - - // Check for local sound definitions. Only reload if they differ - // from the previous ones. - FString LocalSndInfo; - FString LocalSndSeq; - - // To be certain better check whether level is valid! - if (primaryLevel->info) - { - LocalSndInfo = primaryLevel->info->SoundInfo; - LocalSndSeq = primaryLevel->info->SndSeq; - } - - bool parse_ss = false; - - // This level uses a different local SNDINFO - if (LastLocalSndInfo.CompareNoCase(LocalSndInfo) != 0 || !primaryLevel->info) - { - // First delete the old sound list - for(unsigned i = 1; i < S_sfx.Size(); i++) - { - S_UnloadSound(&S_sfx[i]); - } - - // Parse the global SNDINFO - S_ParseSndInfo(true); - - if (LocalSndInfo.IsNotEmpty()) - { - // Now parse the local SNDINFO - int j = Wads.CheckNumForFullName(LocalSndInfo, true); - if (j>=0) S_AddLocalSndInfo(j); - } - - // Also reload the SNDSEQ if the SNDINFO was replaced! - parse_ss = true; - } - else if (LastLocalSndSeq.CompareNoCase(LocalSndSeq) != 0) - { - parse_ss = true; - } - - if (parse_ss) - { - S_ParseSndSeq(LocalSndSeq.IsNotEmpty()? Wads.CheckNumForFullName(LocalSndSeq, true) : -1); - } - - LastLocalSndInfo = LocalSndInfo; - LastLocalSndSeq = LocalSndSeq; + S_sfx[id].bUsed = true; } } //========================================================================== // -// S_PrecacheLevel -// -// Like R_PrecacheLevel, but for sounds. +// Cache all marked sounds // //========================================================================== -void S_PrecacheLevel (FLevelLocals *Level) +void SoundEngine::CacheMarkedSounds() { - unsigned int i; - - if (GSnd && Level == primaryLevel) + // Don't unload sounds that are playing right now. + for (FSoundChan* chan = Channels; chan != nullptr; chan = chan->NextChan) { - for (i = 0; i < S_sfx.Size(); ++i) - { - S_sfx[i].bUsed = false; - } + MarkUsed(chan->SoundID); + } - AActor *actor; - auto iterator = Level->GetThinkerIterator(); - - // Precache all sounds known to be used by the currently spawned actors. - while ( (actor = iterator.Next()) != NULL ) + for (unsigned i = 1; i < S_sfx.Size(); ++i) + { + if (S_sfx[i].bUsed) { - IFVIRTUALPTR(actor, AActor, MarkPrecacheSounds) - { - VMValue params[1] = { actor }; - VMCall(func, params, 1, nullptr, 0); - } + CacheSound(&S_sfx[i]); } - for (auto snd : gameinfo.PrecachedSounds) + } + for (unsigned i = 1; i < S_sfx.Size(); ++i) + { + if (!S_sfx[i].bUsed && S_sfx[i].link == sfxinfo_t::NO_LINK) { - FSoundID(snd).MarkUsed(); - } - // Precache all extra sounds requested by this map. - for (auto snd : primaryLevel->info->PrecacheSounds) - { - FSoundID(snd).MarkUsed(); - } - // Don't unload sounds that are playing right now. - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - chan->SoundID.MarkUsed(); - } - - for (i = 1; i < S_sfx.Size(); ++i) - { - if (S_sfx[i].bUsed) - { - S_CacheSound (&S_sfx[i]); - } - } - for (i = 1; i < S_sfx.Size(); ++i) - { - if (!S_sfx[i].bUsed && S_sfx[i].link == sfxinfo_t::NO_LINK) - { - S_UnloadSound (&S_sfx[i]); - } + UnloadSound(&S_sfx[i]); } } } @@ -465,7 +143,7 @@ void S_PrecacheLevel (FLevelLocals *Level) // //========================================================================== -void S_CacheSound (sfxinfo_t *sfx) +void SoundEngine::CacheSound (sfxinfo_t *sfx) { if (GSnd) { @@ -480,14 +158,14 @@ void S_CacheSound (sfxinfo_t *sfx) } if (sfx->bRandomHeader) { - S_CacheRandomSound(sfx); + CacheRandomSound(sfx); } else { // Since we do not know in what format the sound will be used, we have to cache both. FSoundLoadBuffer SoundBuffer; - S_LoadSound(sfx, &SoundBuffer); - S_LoadSound3D(sfx, &SoundBuffer); + LoadSound(sfx, &SoundBuffer); + LoadSound3D(sfx, &SoundBuffer); sfx->bUsed = true; } } @@ -499,14 +177,12 @@ void S_CacheSound (sfxinfo_t *sfx) // //========================================================================== -void S_UnloadSound (sfxinfo_t *sfx) +void SoundEngine::UnloadSound (sfxinfo_t *sfx) { if (sfx->data3d.isValid() && sfx->data != sfx->data3d) GSnd->UnloadSound(sfx->data3d); if (sfx->data.isValid()) GSnd->UnloadSound(sfx->data); - if (sfx->data.isValid() || sfx->data3d.isValid()) - DPrintf(DMSG_NOTIFY, "Unloaded sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); sfx->data.Clear(); sfx->data3d.Clear(); } @@ -519,21 +195,21 @@ void S_UnloadSound (sfxinfo_t *sfx) // //========================================================================== -FISoundChannel *S_GetChannel(void *syschan) +FSoundChan *SoundEngine::GetChannel(void *syschan) { FSoundChan *chan; if (FreeChannels != NULL) { chan = FreeChannels; - S_UnlinkChannel(chan); + UnlinkChannel(chan); } else { chan = new FSoundChan; memset(chan, 0, sizeof(*chan)); } - S_LinkChannel(chan, &Channels); + LinkChannel(chan, &Channels); chan->SysChannel = syschan; return chan; } @@ -546,11 +222,11 @@ FISoundChannel *S_GetChannel(void *syschan) // //========================================================================== -void S_ReturnChannel(FSoundChan *chan) +void SoundEngine::ReturnChannel(FSoundChan *chan) { - S_UnlinkChannel(chan); + UnlinkChannel(chan); memset(chan, 0, sizeof(*chan)); - S_LinkChannel(chan, &FreeChannels); + LinkChannel(chan, &FreeChannels); } //========================================================================== @@ -559,7 +235,7 @@ void S_ReturnChannel(FSoundChan *chan) // //========================================================================== -void S_UnlinkChannel(FSoundChan *chan) +void SoundEngine::UnlinkChannel(FSoundChan *chan) { *(chan->PrevChan) = chan->NextChan; if (chan->NextChan != NULL) @@ -574,7 +250,7 @@ void S_UnlinkChannel(FSoundChan *chan) // //========================================================================== -void S_LinkChannel(FSoundChan *chan, FSoundChan **head) +void SoundEngine::LinkChannel(FSoundChan *chan, FSoundChan **head) { chan->NextChan = *head; if (chan->NextChan != NULL) @@ -585,6 +261,56 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) chan->PrevChan = head; } +//========================================================================== +// +// +// +//========================================================================== + +TArray SoundEngine::AllActiveChannels() +{ + TArray chans; + + for (auto chan = Channels; chan != nullptr; chan = chan->NextChan) + { + // If the sound is forgettable, this is as good a time as + // any to forget about it. And if it's a UI sound, it shouldn't + // be stored in the savegame. + if (!(chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_UI))) + { + chans.Push(chan); + } + } + return chans; +} + +//========================================================================== +// +// +// +//========================================================================== + +FString SoundEngine::ListSoundChannels() +{ + FString output; + FSoundChan* chan; + int count = 0; + for (chan = Channels; chan != nullptr; chan = chan->NextChan) + { + if (!(chan->ChanFlags & CHAN_EVICTED)) + { + FVector3 chanorigin; + + CalcPosVel(chan, &chanorigin, nullptr); + + output.AppendFormat("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)S_sfx[chan->SoundID].name.GetChars(), chanorigin.X, chanorigin.Y, chanorigin.Z); + count++; + } + } + output.AppendFormat("%d sounds playing\n", count); + return output; +} + // [RH] Split S_StartSoundAtVolume into multiple parts so that sounds can // be specified both by id and by name. Also borrowed some stuff from // Hexen and parameters from Quake. @@ -598,253 +324,15 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) // //========================================================================= -void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) +void SoundEngine::CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) { CalcPosVel(chan->SourceType, chan->Source, chan->Point, chan->EntChannel, chan->ChanFlags, pos, vel); } -//========================================================================= -// -// CalcPosVel -// -// This version is for sounds that haven't started yet so have no channel. -// -//========================================================================= - -static void CalcPosVel(int type, const void *source, - const float pt[3], int channum, int chanflags, FVector3 *pos, FVector3 *vel) +bool SoundEngine::ValidatePosVel(const FSoundChan* const chan, const FVector3& pos, const FVector3& vel) { - if (pos != NULL) - { - DVector3 listenpos; - int pgroup; - AActor *listener = players[consoleplayer].camera; - - if (listener != NULL) - { - listenpos = listener->Pos(); - *pos = listener->SoundPos(); - pgroup = listener->Sector->PortalGroup; - } - else - { - listenpos.Zero(); - pos->Zero(); - pgroup = 0; - } - if (vel) vel->Zero(); - - // [BL] Moved this case out of the switch statement to make code easier - // on static analysis. - if(type == SOURCE_Unattached) - { - sector_t *sec = primaryLevel->PointInSector(pt[0], pt[2]); - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sec->PortalGroup); - pos->X = pt[0] - (float)disp.X; - pos->Y = !(chanflags & CHAN_LISTENERZ) ? pt[1] : (float)listenpos.Z; - pos->Z = pt[2] - (float)disp.Y; - } - else - { - switch (type) - { - case SOURCE_None: - default: - break; - - case SOURCE_Actor: - { - auto actor = (AActor*)source; - //assert(actor != NULL); - if (actor != NULL) - { - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, actor->Sector->PortalGroup); - DVector3 posi = actor->Pos() - disp; - *pos = { (float)posi.X, (float)posi.Z, (float)posi.Y }; - if (vel) - { - vel->X = float(actor->Vel.X * TICRATE); - vel->Y = float(actor->Vel.Z * TICRATE); - vel->Z = float(actor->Vel.Y * TICRATE); - } - } - break; - } - - case SOURCE_Sector: - { - auto sector = (sector_t*)source; - assert(sector != NULL); - if (sector != NULL) - { - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, sector->PortalGroup); - if (chanflags & CHAN_AREA) - { - // listener must be reversely offset to calculate the proper sound origin. - CalcSectorSoundOrg(listenpos + disp, sector, channum, *pos); - pos->X -= (float)disp.X; - pos->Z -= (float)disp.Y; - } - else - { - - pos->X = (float)(sector->centerspot.X - disp.X); - pos->Z = (float)(sector->centerspot.Y - disp.Y); - chanflags |= CHAN_LISTENERZ; - } - } - break; - } - - case SOURCE_Polyobj: - { - auto poly = (FPolyObj*)source; - assert(poly != NULL); - if (poly != NULL) - { - DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, poly->CenterSubsector->sector->PortalGroup); - CalcPolyobjSoundOrg(listenpos + disp, poly, *pos); - pos->X -= (float)disp.X; - pos->Z -= (float)disp.Y; - } - break; - } - } - - if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != NULL) - { - pos->Y = (float)listenpos.Z; - } - } - } -} - -//========================================================================== -// -// ValidatePosVel -// -//========================================================================== - -inline bool Validate(const float value, const float limit) -{ - return value >= -limit && value <= limit; -} - -static bool Validate(const FVector3 &value, const float limit, const char *const name, const AActor *const actor) -{ - const bool valid = - Validate(value.X, limit) - && Validate(value.Y, limit) - && Validate(value.Z, limit); - - if (!valid) - { - // Sound position and velocity have Y and Z axes swapped comparing to map coordinate system - Printf(TEXTCOLOR_RED "Invalid sound %s " TEXTCOLOR_WHITE "(%f, %f, %f)", name, value.X, value.Z, value.Y); - - if (actor == nullptr) - { - Printf("\n"); - } - else - { - Printf(TEXTCOLOR_RED " for actor of class " TEXTCOLOR_WHITE "%s\n", actor->GetClass()->TypeName.GetChars()); - } - } - - return valid; -} - -static bool ValidatePosVel(const AActor *actor, const FVector3 &pos, const FVector3 &vel) -{ - // The actual limit for map coordinates - static const float POSITION_LIMIT = 1024.f * 1024.f; - const bool valid = Validate(pos, POSITION_LIMIT, "position", actor); - - // The maximum velocity is enough to travel through entire map in one tic - static const float VELOCITY_LIMIT = 2 * POSITION_LIMIT * TICRATE; - return Validate(vel, VELOCITY_LIMIT, "velocity", actor) && valid; -} - -static bool ValidatePosVel(const FSoundChan *const chan, const FVector3 &pos, const FVector3 &vel) -{ - return ValidatePosVel(chan->SourceType == SOURCE_Actor ? (AActor*)chan->Source : nullptr, pos, vel); -} - -//========================================================================== -// -// CalcSectorSoundOrg -// -// Returns the perceived sound origin for a sector. If the listener is -// inside the sector, then the origin is their location. Otherwise, the -// origin is from the nearest wall on the sector. -// -//========================================================================== - -static void CalcSectorSoundOrg(const DVector3 &listenpos, const sector_t *sec, int channum, FVector3 &pos) -{ - if (!(sec->Level->i_compatflags & COMPATF_SECTORSOUNDS)) - { - // Are we inside the sector? If yes, the closest point is the one we're on. - if (primaryLevel->PointInSector(listenpos.X, listenpos.Y) == sec) - { - pos.X = (float)listenpos.X; - pos.Z = (float)listenpos.Y; - } - else - { - // Find the closest point on the sector's boundary lines and use - // that as the perceived origin of the sound. - DVector2 xy; - sec->ClosestPoint(listenpos, xy); - pos.X = (float)xy.X; - pos.Z = (float)xy.Y; - } - } - else - { - pos.X = float(sec->centerspot.X); - pos.Z = float(sec->centerspot.Y); - } - - // Set sound vertical position based on channel. - if (channum == CHAN_FLOOR) - { - pos.Y = (float)MIN(sec->floorplane.ZatPoint(listenpos), listenpos.Z); - } - else if (channum == CHAN_CEILING) - { - pos.Y = (float)MAX(sec->ceilingplane.ZatPoint(listenpos), listenpos.Z); - } - else if (channum == CHAN_INTERIOR) - { - pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); - } -} - -//========================================================================== -// -// CalcPolySoundOrg -// -// Returns the perceived sound origin for a polyobject. This is similar to -// CalcSectorSoundOrg, except there is no special case for being "inside" -// a polyobject, so the sound literally comes from the polyobject's walls. -// Vertical position of the sound always comes from the visible wall. -// -//========================================================================== - -static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, FVector3 &pos) -{ - side_t *side; - sector_t *sec; - - DVector2 ppos; - poly->ClosestPoint(listenpos, ppos, &side); - pos.X = (float)ppos.X; - pos.Z = (float)ppos.Y; - sec = side->sector; - pos.Y = (float)clamp(listenpos.Z, sec->floorplane.ZatPoint(listenpos), sec->ceilingplane.ZatPoint(listenpos)); + return ValidatePosVel(chan->SourceType, chan->Source, pos, vel); } //========================================================================== @@ -857,7 +345,7 @@ static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, // //========================================================================== -FSoundChan *S_StartSound(int type, const void *source, +FSoundChan *SoundEngine::StartSound(int type, const void *source, const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo *forcedrolloff, float spitch) { @@ -874,6 +362,7 @@ FSoundChan *S_StartSound(int type, const void *source, if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) return NULL; + // prevent crashes. if (type == SOURCE_Unattached && pt == nullptr) type = SOURCE_None; org_id = sound_id; @@ -882,27 +371,15 @@ FSoundChan *S_StartSound(int type, const void *source, CalcPosVel(type, source, &pt->X, channel, chanflags, &pos, &vel); - if (!ValidatePosVel(type == SOURCE_Actor ? (AActor*)source : nullptr, pos, vel)) + if (!ValidatePosVel(type, source, pos, vel)) { return nullptr; } - if (compatflags & COMPATF_MAGICSILENCE) - { // For people who just can't play without a silent BFG. - channel = CHAN_WEAPON; - } - else if ((chanflags & CHAN_MAYBE_LOCAL) && (compatflags & COMPATF_SILENTPICKUP)) - { - if (source != nullptr && source == listener.ListenerObject) - { - return nullptr; - } - } - sfx = &S_sfx[sound_id]; // Scale volume according to SNDINFO data. - volume = MIN(volume * sfx->Volume, 1.f); + volume = std::min(volume * sfx->Volume, 1.f); if (volume <= 0) return NULL; @@ -920,7 +397,7 @@ FSoundChan *S_StartSound(int type, const void *source, { // Random sounds attenuate based on the original (random) sound as well as the chosen one. attenuation *= sfx->Attenuation; - sound_id = FSoundID(S_PickReplacement (sound_id)); + sound_id = FSoundID(PickReplacement (sound_id)); if (near_limit < 0) { near_limit = S_sfx[sound_id].NearLimit; @@ -963,7 +440,7 @@ FSoundChan *S_StartSound(int type, const void *source, } // If this is a singular sound, don't play it if it's already playing. - if (sfx->bSingular && S_CheckSingular(sound_id)) + if (sfx->bSingular && CheckSingular(sound_id)) { chanflags |= CHAN_EVICTED; } @@ -976,8 +453,8 @@ FSoundChan *S_StartSound(int type, const void *source, } // If this sound doesn't like playing near itself, don't play it if - // that's what would happen. - if (near_limit > 0 && S_CheckSoundLimit(sfx, pos, near_limit, limit_range, type, type == SOURCE_Actor? source : nullptr, channel)) + // that's what would happen. (Does this really need the SOURCE_Actor restriction?) + if (near_limit > 0 && CheckSoundLimit(sfx, pos, near_limit, limit_range, type, type == SOURCE_Actor? source : nullptr, channel)) { chanflags |= CHAN_EVICTED; } @@ -991,7 +468,7 @@ FSoundChan *S_StartSound(int type, const void *source, } // Make sure the sound is loaded. - sfx = S_LoadSound(sfx, &SoundBuffer); + sfx = LoadSound(sfx, &SoundBuffer); // The empty sound never plays. if (sfx->lumpnum == sfx_empty) @@ -1014,7 +491,7 @@ FSoundChan *S_StartSound(int type, const void *source, { // Select a channel that isn't already playing something. // Try channel 0 first, then travel from channel 7 down. - if (!S_IsChannelUsed(type, source, 0, &seen)) + if (!IsChannelUsed(type, source, 0, &seen)) { channel = 0; } @@ -1022,7 +499,7 @@ FSoundChan *S_StartSound(int type, const void *source, { for (channel = 7; channel > 0; --channel) { - if (!S_IsChannelUsed(type, source, channel, &seen)) + if (!IsChannelUsed(type, source, channel, &seen)) { break; } @@ -1035,7 +512,7 @@ FSoundChan *S_StartSound(int type, const void *source, } // If this actor is already playing something on the selected channel, stop it. - if (type != SOURCE_None && ((source == NULL && channel != CHAN_AUTO) || (source != NULL && S_IsChannelUsed(type, source, channel, &seen)))) + if (type != SOURCE_None && ((source == NULL && channel != CHAN_AUTO) || (source != NULL && IsChannelUsed(type, source, channel, &seen)))) { for (chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1060,11 +537,11 @@ FSoundChan *S_StartSound(int type, const void *source, // Vary the sfx pitches. if (pitchmask != 0) { - pitch = NORM_PITCH - (M_Random() & pitchmask) + (M_Random() & pitchmask); + pitch = DEFAULT_PITCH - (rand() & pitchmask) + (rand() & pitchmask); } else { - pitch = NORM_PITCH; + pitch = DEFAULT_PITCH; } if (chanflags & CHAN_EVICTED) @@ -1081,7 +558,7 @@ FSoundChan *S_StartSound(int type, const void *source, if (attenuation > 0) { - S_LoadSound3D(sfx, &SoundBuffer); + LoadSound3D(sfx, &SoundBuffer); chan = (FSoundChan*)GSnd->StartSound3D (sfx->data3d, &listener, float(volume), rolloff, float(attenuation), pitch, basepriority, pos, vel, channel, startflags, NULL); } else @@ -1091,7 +568,7 @@ FSoundChan *S_StartSound(int type, const void *source, } if (chan == NULL && (chanflags & CHAN_LOOP)) { - chan = (FSoundChan*)S_GetChannel(NULL); + chan = (FSoundChan*)GetChannel(NULL); GSnd->MarkStartTime(chan); chanflags |= CHAN_EVICTED; } @@ -1126,7 +603,7 @@ FSoundChan *S_StartSound(int type, const void *source, } if (spitch > 0.0) - S_SetPitch(chan, spitch); + SetPitch(chan, spitch); } return chan; @@ -1140,7 +617,7 @@ FSoundChan *S_StartSound(int type, const void *source, // //========================================================================== -void S_RestartSound(FSoundChan *chan) +void SoundEngine::RestartChannel(FSoundChan *chan) { assert(chan->ChanFlags & CHAN_EVICTED); @@ -1149,10 +626,10 @@ void S_RestartSound(FSoundChan *chan) FSoundLoadBuffer SoundBuffer; // If this is a singular sound, don't play it if it's already playing. - if (sfx->bSingular && S_CheckSingular(chan->SoundID)) + if (sfx->bSingular && CheckSingular(chan->SoundID)) return; - sfx = S_LoadSound(sfx, &SoundBuffer); + sfx = LoadSound(sfx, &SoundBuffer); // The empty sound never plays. if (sfx->lumpnum == sfx_empty) @@ -1181,12 +658,12 @@ void S_RestartSound(FSoundChan *chan) // If this sound doesn't like playing near itself, don't play it if // that's what would happen. - if (chan->NearLimit > 0 && S_CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0)) + if (chan->NearLimit > 0 && CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0)) { return; } - S_LoadSound3D(sfx, &SoundBuffer); + LoadSound3D(sfx, &SoundBuffer); chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME); ochan = (FSoundChan*)GSnd->StartSound3D(sfx->data3d, &listener, chan->Volume, &chan->Rolloff, chan->DistanceScale, chan->Pitch, chan->Priority, pos, vel, chan->EntChannel, startflags, chan); @@ -1211,7 +688,7 @@ void S_RestartSound(FSoundChan *chan) // //========================================================================== -sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) +sfxinfo_t *SoundEngine::LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (GSnd->IsNull()) return sfx; @@ -1231,7 +708,7 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (S_sfx[i].data.isValid() && S_sfx[i].link == sfxinfo_t::NO_LINK && S_sfx[i].lumpnum == sfx->lumpnum) { - DPrintf (DMSG_NOTIFY, "Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); + //DPrintf (DMSG_NOTIFY, "Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); sfx->link = i; // This is necessary to avoid using the rolloff settings of the linked sound if its // settings are different. @@ -1240,13 +717,12 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) } } - DPrintf(DMSG_NOTIFY, "Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + //DPrintf(DMSG_NOTIFY, "Loading sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); - int size = Wads.LumpLength(sfx->lumpnum); + auto sfxdata = ReadSound(sfx->lumpnum); + int size = sfxdata.Size(); if (size > 8) { - auto wlump = Wads.OpenLumpReader(sfx->lumpnum); - auto sfxdata = wlump.Read(size); int32_t dmxlen = LittleLong(((int32_t *)sfxdata.Data())[1]); std::pair snd; @@ -1291,14 +767,14 @@ sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) return sfx; } -static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) +void SoundEngine::LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) { if (GSnd->IsNull()) return; if(sfx->data3d.isValid()) return; - DPrintf(DMSG_NOTIFY, "Loading monoized sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); + //DPrintf(DMSG_NOTIFY, "Loading monoized sound \"%s\" (%td)\n", sfx->name.GetChars(), sfx - &S_sfx[0]); std::pair snd; @@ -1308,11 +784,9 @@ static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) } else { - int size = Wads.LumpLength(sfx->lumpnum); + auto sfxdata = ReadSound(sfx->lumpnum); + int size = sfxdata.Size(); if (size <= 8) return; - - auto wlump = Wads.OpenLumpReader(sfx->lumpnum); - auto sfxdata = wlump.Read(size); int32_t dmxlen = LittleLong(((int32_t *)sfxdata.Data())[1]); // If the sound is voc, use the custom loader. @@ -1350,7 +824,7 @@ static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer) // //========================================================================== -bool S_CheckSingular(int sound_id) +bool SoundEngine::CheckSingular(int sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1379,7 +853,7 @@ bool S_CheckSingular(int sound_id) // //========================================================================== -bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, +bool SoundEngine::CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, int sourcetype, const void *actor, int channel) { FSoundChan *chan; @@ -1415,7 +889,7 @@ bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, floa // //========================================================================== -void S_StopSound (int channel) +void SoundEngine::StopSound (int channel) { FSoundChan *chan = Channels; while (chan != NULL) @@ -1423,7 +897,7 @@ void S_StopSound (int channel) FSoundChan *next = chan->NextChan; if (chan->SourceType == SOURCE_None) { - S_StopChannel(chan); + StopChannel(chan); } chan = next; } @@ -1437,7 +911,7 @@ void S_StopSound (int channel) // //========================================================================== -void S_StopSound(int sourcetype, const void* actor, int channel) +void SoundEngine::StopSound(int sourcetype, const void* actor, int channel) { FSoundChan* chan = Channels; while (chan != NULL) @@ -1447,7 +921,7 @@ void S_StopSound(int sourcetype, const void* actor, int channel) chan->Source == actor && (chan->EntChannel == channel || channel < 0)) { - S_StopChannel(chan); + StopChannel(chan); } chan = next; } @@ -1459,18 +933,13 @@ void S_StopSound(int sourcetype, const void* actor, int channel) // //========================================================================== -void S_StopAllChannels () +void SoundEngine::StopAllChannels () { - for (auto Level : AllLevels()) - { - SN_StopAllSequences(Level); - } - FSoundChan *chan = Channels; while (chan != NULL) { FSoundChan *next = chan->NextChan; - S_StopChannel(chan); + StopChannel(chan); chan = next; } @@ -1486,7 +955,7 @@ void S_StopAllChannels () // NULL, then the sound becomes a positioned sound. //========================================================================== -void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) +void SoundEngine::RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) { if (from == NULL) return; @@ -1511,7 +980,7 @@ void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVec } else { - S_StopChannel(chan); + StopChannel(chan); } } chan = next; @@ -1525,7 +994,7 @@ void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVec // //========================================================================== -void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume) +void SoundEngine::ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume) { float volume = float(dvolume); // don't let volume get out of bounds @@ -1554,7 +1023,7 @@ void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double // //========================================================================== -void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch) +void SoundEngine::ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1562,18 +1031,18 @@ void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double chan->Source == source && chan->EntChannel == channel) { - S_SetPitch(chan, (float)pitch); + SetPitch(chan, (float)pitch); return; } } return; } -void S_SetPitch(FSoundChan *chan, float pitch) +void SoundEngine::SetPitch(FSoundChan *chan, float pitch) { assert(chan != nullptr); - GSnd->ChannelPitch(chan, MAX(0.0001f, pitch)); - chan->Pitch = MAX(1, int(float(NORM_PITCH) * pitch)); + GSnd->ChannelPitch(chan, std::max(0.0001f, pitch)); + chan->Pitch = std::max(1, int(float(DEFAULT_PITCH) * pitch)); } //========================================================================== @@ -1583,7 +1052,7 @@ void S_SetPitch(FSoundChan *chan, float pitch) // Is a sound being played by a specific emitter? //========================================================================== -bool S_GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) +bool SoundEngine::GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) { if (sound_id > 0) { @@ -1610,7 +1079,7 @@ bool S_GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) // //========================================================================== -static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) +bool SoundEngine::IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) { if (*seen & (1 << channel)) { @@ -1636,7 +1105,7 @@ static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int // //========================================================================== -bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) +bool SoundEngine::IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1651,50 +1120,6 @@ bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, return false; } -//========================================================================== -// -// S_SetSoundPaused -// -// Called with state non-zero when the app is active, zero when it isn't. -// -//========================================================================== - -void S_SetSoundPaused (int state) -{ - if (state) - { - if (paused == 0) - { - S_ResumeSound(true); - if (GSnd != NULL) - { - GSnd->SetInactive(SoundRenderer::INACTIVE_Active); - } - } - } - else - { - if (paused == 0) - { - S_PauseSound(false, true); - if (GSnd != NULL) - { - GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ? - SoundRenderer::INACTIVE_Complete : - SoundRenderer::INACTIVE_Mute); - } - } - } - if (!netgame -#ifdef _DEBUG - && !demoplayback -#endif - ) - { - pauseext = !state; - } -} - //========================================================================== // // S_EvictAllChannels @@ -1704,7 +1129,7 @@ void S_SetSoundPaused (int state) // //========================================================================== -void S_EvictAllChannels() +void SoundEngine::EvictAllChannels() { FSoundChan *chan, *next; @@ -1722,7 +1147,7 @@ void S_EvictAllChannels() chan->StartTime = GSnd ? GSnd->GetPosition(chan) : 0; chan->ChanFlags |= CHAN_ABSTIME; } - S_StopChannel(chan); + StopChannel(chan); } // assert(chan->NextChan == next); } @@ -1737,21 +1162,21 @@ void S_EvictAllChannels() // //========================================================================== -void S_RestoreEvictedChannel(FSoundChan *chan) +void SoundEngine::RestoreEvictedChannel(FSoundChan *chan) { if (chan == NULL) { return; } - S_RestoreEvictedChannel(chan->NextChan); + RestoreEvictedChannel(chan->NextChan); if (chan->ChanFlags & CHAN_EVICTED) { - S_RestartSound(chan); + RestartChannel(chan); if (!(chan->ChanFlags & CHAN_LOOP)) { if (chan->ChanFlags & CHAN_EVICTED) { // Still evicted and not looping? Forget about it. - S_ReturnChannel(chan); + ReturnChannel(chan); } else if (!(chan->ChanFlags & CHAN_JUSTSTARTED)) { // Should this sound become evicted again, it's okay to forget about it. @@ -1761,7 +1186,7 @@ void S_RestoreEvictedChannel(FSoundChan *chan) } else if (chan->SysChannel == NULL && (chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_LOOP)) == CHAN_FORGETTABLE) { - S_ReturnChannel(chan); + ReturnChannel(chan); } } @@ -1774,10 +1199,10 @@ void S_RestoreEvictedChannel(FSoundChan *chan) // //========================================================================== -void S_RestoreEvictedChannels() +void SoundEngine::RestoreEvictedChannels() { // Restart channels in the same order they were originally played. - S_RestoreEvictedChannel(Channels); + RestoreEvictedChannel(Channels); } //========================================================================== @@ -1787,7 +1212,7 @@ void S_RestoreEvictedChannels() // Updates music & sounds //========================================================================== -void S_UpdateSounds(int time) +void SoundEngine::UpdateSounds(int time) { FVector3 pos, vel; @@ -1811,7 +1236,7 @@ void S_UpdateSounds(int time) if (time >= RestartEvictionsAt) { RestartEvictionsAt = 0; - S_RestoreEvictedChannels(); + RestoreEvictedChannels(); } } @@ -1821,63 +1246,46 @@ void S_UpdateSounds(int time) // //========================================================================== -float S_GetRolloff(FRolloffInfo *rolloff, float distance, bool logarithmic) +float SoundEngine::GetRolloff(const FRolloffInfo* rolloff, float distance) { if (rolloff == NULL) { return 0; } - if (distance <= rolloff->MinDistance) { - return 1; + return 1.f; } + // Logarithmic rolloff has no max distance where it goes silent. if (rolloff->RolloffType == ROLLOFF_Log) - { // Logarithmic rolloff has no max distance where it goes silent. + { return rolloff->MinDistance / (rolloff->MinDistance + rolloff->RolloffFactor * (distance - rolloff->MinDistance)); } if (distance >= rolloff->MaxDistance) { - return 0; + return 0.f; } float volume = (rolloff->MaxDistance - distance) / (rolloff->MaxDistance - rolloff->MinDistance); + if (rolloff->RolloffType == ROLLOFF_Linear) + { + return volume; + } + if (rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve.Size() > 0) { - volume = S_SoundCurve[int(S_SoundCurve.Size() * (1 - volume))] / 127.f; - } - if (logarithmic) - { - if (rolloff->RolloffType == ROLLOFF_Linear) - { - return volume; - } - else - { - return float((powf(10.f, volume) - 1.) / 9.); - } - } - else - { - if (rolloff->RolloffType == ROLLOFF_Linear) - { - return float(log10(9. * volume + 1.)); - } - else - { - return volume; - } + return S_SoundCurve[int(S_SoundCurve.Size() * (1.f - volume))] / 127.f; } + return (powf(10.f, volume) - 1.f) / 9.f; } - //========================================================================== // // S_ChannelEnded (callback for sound interface code) // //========================================================================== -void S_ChannelEnded(FISoundChannel *ichan) +void SoundEngine::ChannelEnded(FISoundChannel *ichan) { FSoundChan *schan = static_cast(ichan); bool evicted; @@ -1911,7 +1319,7 @@ void S_ChannelEnded(FISoundChannel *ichan) } if (!evicted) { - S_ReturnChannel(schan); + ReturnChannel(schan); } else { @@ -1927,7 +1335,7 @@ void S_ChannelEnded(FISoundChannel *ichan) // //========================================================================== -void S_ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) +void SoundEngine::ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) { FSoundChan *schan = static_cast(ichan); if (is_virtual) @@ -1942,11 +1350,11 @@ void S_ChannelVirtualChanged(FISoundChannel *ichan, bool is_virtual) //========================================================================== // -// S_StopChannel +// StopChannel // //========================================================================== -void S_StopChannel(FSoundChan *chan) +void SoundEngine::StopChannel(FSoundChan *chan) { if (chan == NULL) return; @@ -1967,23 +1375,292 @@ void S_StopChannel(FSoundChan *chan) } else { - S_ReturnChannel(chan); + ReturnChannel(chan); } } -void S_UnloadAllSounds() +void SoundEngine::UnloadAllSounds() { for (unsigned i = 0; i < S_sfx.Size(); i++) { - S_UnloadSound(&S_sfx[i]); + UnloadSound(&S_sfx[i]); } } -void S_Reset() +void SoundEngine::Reset() { - S_EvictAllChannels(); + EvictAllChannels(); I_CloseSound(); I_InitSound(); - S_RestoreEvictedChannels(); + RestoreEvictedChannels(); } + +//========================================================================== +// +// S_FindSound +// +// Given a logical name, find the sound's index in S_sfx. +//========================================================================== + +int SoundEngine::FindSound(const char* logicalname) +{ + int i; + + if (logicalname != NULL) + { + i = S_sfx[MakeKey(logicalname) % S_sfx.Size()].index; + + while ((i != 0) && stricmp(S_sfx[i].name, logicalname)) + i = S_sfx[i].next; + + return i; + } + else + { + return 0; + } +} + +int SoundEngine::FindSoundByResID(int resid) +{ + auto p = ResIdMap.CheckKey(resid); + return p ? *p : 0; +} + +//========================================================================== +// +// S_FindSoundNoHash +// +// Given a logical name, find the sound's index in S_sfx without +// using the hash table. +//========================================================================== + +int SoundEngine::FindSoundNoHash(const char* logicalname) +{ + unsigned int i; + + for (i = 1; i < S_sfx.Size(); i++) + { + if (stricmp(S_sfx[i].name, logicalname) == 0) + { + return i; + } + } + return 0; +} + +//========================================================================== +// +// S_FindSoundByLump +// +// Given a sound lump, find the sound's index in S_sfx. +//========================================================================== + +int SoundEngine::FindSoundByLump(int lump) +{ + if (lump != -1) + { + unsigned int i; + + for (i = 1; i < S_sfx.Size(); i++) + if (S_sfx[i].lumpnum == lump) + return i; + } + return 0; +} + +//========================================================================== +// +// S_AddSoundLump +// +// Adds a new sound mapping to S_sfx. +//========================================================================== + +int SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid) +{ + S_sfx.Reserve(1); + sfxinfo_t &newsfx = S_sfx.Last(); + + newsfx.data.Clear(); + newsfx.data3d.Clear(); + newsfx.name = logicalname; + newsfx.lumpnum = lump; + newsfx.next = 0; + newsfx.index = 0; + newsfx.Volume = 1; + newsfx.Attenuation = 1; + newsfx.PitchMask = CurrentPitchMask; + newsfx.NearLimit = 2; + newsfx.LimitRange = 256 * 256; + newsfx.bRandomHeader = false; + newsfx.bPlayerReserve = false; + newsfx.bLoadRAW = false; + newsfx.bPlayerCompat = false; + newsfx.b16bit = false; + newsfx.bUsed = false; + newsfx.bSingular = false; + newsfx.bTentative = false; + newsfx.bPlayerSilent = false; + newsfx.ResourceId = resid; + newsfx.RawRate = 0; + newsfx.link = sfxinfo_t::NO_LINK; + newsfx.Rolloff.RolloffType = ROLLOFF_Doom; + newsfx.Rolloff.MinDistance = 0; + newsfx.Rolloff.MaxDistance = 0; + newsfx.LoopStart = -1; + + if (resid >= 0) ResIdMap[resid] = S_sfx.Size() - 1; + return (int)S_sfx.Size()-1; +} + +//========================================================================== +// +// S_FindSoundTentative +// +// Given a logical name, find the sound's index in S_sfx without +// using the hash table. If it does not exist, a new sound without +// an associated lump is created. +//========================================================================== + +int SoundEngine::FindSoundTentative(const char* name) +{ + int id = FindSoundNoHash(name); + if (id == 0) + { + id = AddSoundLump(name, -1, 0); + S_sfx[id].bTentative = true; + } + return id; +} + + +//========================================================================== +// +// S_CacheRandomSound +// +// Loads all sounds a random sound might play. +// +//========================================================================== + +void SoundEngine::CacheRandomSound(sfxinfo_t* sfx) +{ + if (sfx->bRandomHeader) + { + const FRandomSoundList* list = &S_rnd[sfx->link]; + for (unsigned i = 0; i < list->Choices.Size(); ++i) + { + sfx = &S_sfx[list->Choices[i]]; + sfx->bUsed = true; + CacheSound(&S_sfx[list->Choices[i]]); + } + } +} + +//========================================================================== +// +// S_GetSoundMSLength +// +// Returns duration of sound +// GZDoom does not use this due to player sound handling +// +//========================================================================== + +unsigned int SoundEngine::GetMSLength(FSoundID sound) +{ + if ((unsigned int)sound >= S_sfx.Size()) + { + return 0; + } + + sfxinfo_t* sfx = &S_sfx[sound]; + + // Resolve player sounds, random sounds, and aliases + if (sfx->link != sfxinfo_t::NO_LINK) + { + if (sfx->bRandomHeader) + { + // Hm... What should we do here? + // Pick the longest or the shortest sound? + // I think the longest one makes more sense. + + int length = 0; + const FRandomSoundList* list = &S_rnd[sfx->link]; + + for (auto& me : list->Choices) + { + // unfortunately we must load all sounds to find the longest one... :( + int thislen = GetMSLength(me); + if (thislen > length) length = thislen; + } + return length; + } + else + { + sfx = &S_sfx[sfx->link]; + } + } + + sfx = LoadSound(sfx, nullptr); + if (sfx != NULL) return GSnd->GetMSLength(sfx->data); + else return 0; +} + +//========================================================================== +// +// S_PickReplacement +// +// Picks a replacement sound from the associated random list. If this sound +// is not the head of a random list, then the sound passed is returned. +//========================================================================== + +int SoundEngine::PickReplacement(int refid) +{ + while (S_sfx[refid].bRandomHeader) + { + const FRandomSoundList* list = &S_rnd[S_sfx[refid].link]; + refid = list->Choices[rand() % int(list->Choices.Size())]; + } + return refid; +} + +//========================================================================== +// +// S_HashSounds +// +// Fills in the next and index fields of S_sfx to form a working hash table. +//========================================================================== + +void SoundEngine::HashSounds() +{ + unsigned int i; + unsigned int j; + unsigned int size; + + S_sfx.ShrinkToFit(); + size = S_sfx.Size(); + + // Mark all buckets as empty + for (i = 0; i < size; i++) + S_sfx[i].index = 0; + + // Now set up the chains + for (i = 1; i < size; i++) + { + j = MakeKey(S_sfx[i].name) % size; + S_sfx[i].next = S_sfx[j].index; + S_sfx[j].index = i; + } + S_rnd.ShrinkToFit(); +} + +void SoundEngine::AddRandomSound(int Owner, TArray list) +{ + auto index = S_rnd.Reserve(1); + auto& random = S_rnd.Last(); + random.Choices = std::move(list); + random.Owner = Owner; + S_sfx[Owner].link = index; + S_sfx[Owner].bRandomHeader = true; + S_sfx[Owner].NearLimit = -1; +} diff --git a/src/sound/s_sound.h b/src/sound/s_sound.h index 18ab9c279..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; @@ -37,58 +36,36 @@ class FSerializer; struct FLevelLocals; #include "s_soundinternal.h" +#include "s_doomsound.h" // Per level startup code. // Kills playing sounds at start of level and starts new music. // -void S_Start (); // Called after a level is loaded. Ensures that most sounds are loaded. -void S_PrecacheLevel (FLevelLocals *l); - -// Start sound for thing at -void S_Sound (int channel, FSoundID sfxid, float volume, float attenuation); - -void S_SoundPitch (int channel, FSoundID sfxid, float volume, float attenuation, float pitch); struct FSoundLoadBuffer; -int S_PickReplacement (int refid); -void S_CacheRandomSound (sfxinfo_t *sfx); - -// Stop sound for all channels -void S_StopAllChannels (void); -void S_SetPitch(FSoundChan *chan, float dpitch); - -void S_RestoreEvictedChannels(); // [RH] S_sfx "maintenance" routines void S_ClearSoundData(); void S_ParseSndInfo (bool redefine); -void S_HashSounds (); -int S_FindSoundNoHash (const char *logicalname); bool S_AreSoundsEquivalent (AActor *actor, int id1, int id2); bool S_AreSoundsEquivalent (AActor *actor, const char *name1, const char *name2); int S_LookupPlayerSound (const char *playerclass, int gender, const char *logicalname); int S_LookupPlayerSound (const char *playerclass, int gender, FSoundID refid); int S_FindSkinnedSound (AActor *actor, FSoundID refid); int S_FindSkinnedSoundEx (AActor *actor, const char *logicalname, const char *extendedname); -int S_FindSoundByLump (int lump); int S_AddSound (const char *logicalname, const char *lumpname, FScanner *sc=NULL); // Add sound by lumpname -int S_AddSoundLump (const char *logicalname, int lump); // Add sound by lump index int S_AddPlayerSound (const char *playerclass, const int gender, int refid, const char *lumpname); int S_AddPlayerSound (const char *playerclass, const int gender, int refid, int lumpnum, bool fromskin=false); int S_AddPlayerSoundExisting (const char *playerclass, const int gender, int refid, int aliasto, bool fromskin=false); void S_MarkPlayerSounds (AActor *player); void S_ShrinkPlayerSoundLists (); -void S_UnloadSound (sfxinfo_t *sfx); -sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer = nullptr); unsigned int S_GetMSLength(FSoundID sound); // [RH] Prints sound debug info to the screen. // Modelled after Hexen's noise cheat. -void S_NoiseDebug (); -#include "s_doomsound.h" #endif diff --git a/src/sound/s_soundinternal.h b/src/sound/s_soundinternal.h index b8af56ef7..86eac0cdb 100644 --- a/src/sound/s_soundinternal.h +++ b/src/sound/s_soundinternal.h @@ -1,7 +1,14 @@ #pragma once -#include "i_soundinternal.h" +#include "i_sound.h" +struct FRandomSoundList +{ + TArray Choices; + uint32_t Owner = 0; +}; + +extern int sfx_empty; // // SoundFX struct. @@ -21,19 +28,20 @@ struct sfxinfo_t unsigned int next, index; // [RH] For hashing float Volume; - uint8_t ResourceId; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. + int ResourceId; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. uint8_t PitchMask; int16_t NearLimit; // 0 means unlimited float LimitRange; // Range for sound limiting (squared for faster computations) unsigned bRandomHeader:1; - unsigned bPlayerReserve:1; unsigned bLoadRAW:1; - unsigned bPlayerCompat:1; unsigned b16bit:1; unsigned bUsed:1; unsigned bSingular:1; + unsigned bTentative:1; + unsigned bPlayerReserve : 1; + unsigned bPlayerCompat : 1; unsigned bPlayerSilent:1; // This player sound is intentionally silent. int RawRate; // Sample rate to use when bLoadRAW is true @@ -61,9 +69,6 @@ enum int S_FindSound(const char *logicalname); int S_FindSoundByResID(int snd_id); -// the complete set of sound effects -extern TArray S_sfx; - // An index into the S_sfx[] array. class FSoundID { @@ -110,18 +115,6 @@ public: { return ID; } - operator FString() const - { - return ID ? S_sfx[ID].name : ""; - } - operator const char *() const - { - return ID ? S_sfx[ID].name.GetChars() : NULL; - } - void MarkUsed() const - { - S_sfx[ID].MarkUsed(); - } private: int ID; protected: @@ -136,13 +129,8 @@ public: using FSoundID::operator=; }; -extern FRolloffInfo S_Rolloff; -extern TArray S_SoundCurve; -class AActor; -struct sector_t; -struct FPolyObj; struct FSoundChan : public FISoundChannel { FSoundChan *NextChan; // Next channel in this list. @@ -163,25 +151,6 @@ struct FSoundChan : public FISoundChannel }; }; -extern FSoundChan *Channels; - -void S_ReturnChannel(FSoundChan *chan); -void S_EvictAllChannels(); - -void S_StopChannel(FSoundChan *chan); -void S_LinkChannel(FSoundChan *chan, FSoundChan **head); -void S_UnlinkChannel(FSoundChan *chan); - -// Initializes sound stuff, including volume -// Sets channels, SFX and music volume, -// allocates channel buffer, sets S_sfx lookup. -// -void S_Init (); -void S_InitData (); -void S_Shutdown (); - -// Loads a sound, including any random sounds it might reference. -void S_CacheSound (sfxinfo_t *sfx); // sound channels // channel 0 never willingly overrides @@ -237,9 +206,6 @@ enum #define ATTN_IDLE 1.001f #define ATTN_STATIC 3.f // diminish very rapidly with distance -// Checks if a copy of this sound is already playing. -bool S_CheckSingular (int sound_id); - enum // This cannot be remain as this, but for now it has to suffice. { SOURCE_None, // Sound is always on top of the listener. @@ -250,31 +216,6 @@ enum // This cannot be remain as this, but for now it has to suffice. }; -// -// Updates music & sounds -// -void S_UpdateSounds (int time); - -FSoundChan* S_StartSound(int sourcetype, const void* source, - const FVector3* pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f); - -// Stops an origin-less sound from playing from this channel. -void S_StopSound(int channel); -void S_StopSound(int sourcetype, const void* actor, int channel); - -void S_RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); -void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume); -void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch); -bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id); - -// Stop and resume music, during game PAUSE. -void S_PauseSound (bool notmusic, bool notsfx); -void S_ResumeSound (bool notsfx); -void S_SetSoundPaused (int state); -bool S_GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id); -void S_UnloadAllSounds(); -void S_Reset(); - extern ReverbContainer *Environments; extern ReverbContainer *DefaultEnvironments[26]; @@ -285,3 +226,163 @@ ReverbContainer *S_FindEnvironment (const char *name); ReverbContainer *S_FindEnvironment (int id); void S_AddEnvironment (ReverbContainer *settings); +class SoundEngine +{ +protected: + bool SoundPaused = false; // whether sound is paused + int RestartEvictionsAt = 0; // do not restart evicted channels before this time + SoundListener listener{}; + + FSoundChan* Channels = nullptr; + FSoundChan* FreeChannels = nullptr; + + // the complete set of sound effects + TArray S_sfx; + FRolloffInfo S_Rolloff; + TArray S_SoundCurve; + TMap ResIdMap; + TArray S_rnd; + +private: + void LoadSound3D(sfxinfo_t* sfx, FSoundLoadBuffer* pBuffer); + void LinkChannel(FSoundChan* chan, FSoundChan** head); + void UnlinkChannel(FSoundChan* chan); + void ReturnChannel(FSoundChan* chan); + void RestartChannel(FSoundChan* chan); + void RestoreEvictedChannel(FSoundChan* chan); + + bool IsChannelUsed(int sourcetype, const void* actor, int channel, int* seen); + // This is the actual sound positioning logic which needs to be provided by the client. + virtual void CalcPosVel(int type, const void* source, const float pt[3], int channel, int chanflags, FVector3* pos, FVector3* vel) = 0; + // This can be overridden by the clent to provide some diagnostics. The default lets everything pass. + virtual bool ValidatePosVel(int sourcetype, const void* source, const FVector3& pos, const FVector3& vel) { return true; } + + bool ValidatePosVel(const FSoundChan* const chan, const FVector3& pos, const FVector3& vel); + + // Checks if a copy of this sound is already playing. + bool CheckSingular(int sound_id); + bool CheckSoundLimit(sfxinfo_t* sfx, const FVector3& pos, int near_limit, float limit_range, int sourcetype, const void* actor, int channel); + virtual TArray ReadSound(int lumpnum) = 0; + +public: + virtual ~SoundEngine() = default; + void EvictAllChannels(); + + void StopChannel(FSoundChan* chan); + sfxinfo_t* LoadSound(sfxinfo_t* sfx, FSoundLoadBuffer* pBuffer); + + // Initializes sound stuff, including volume + // Sets channels, SFX and music volume, + // allocates channel buffer, sets S_sfx lookup. + // + void Init(TArray &sndcurve); + void InitData(); + void Shutdown(); + + void StopAllChannels(void); + void SetPitch(FSoundChan* chan, float dpitch); + + FSoundChan* GetChannel(void* syschan); + void RestoreEvictedChannels(); + void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); + + // Loads a sound, including any random sounds it might reference. + void CacheSound(sfxinfo_t* sfx); + void CacheSound(int sfx) { CacheSound(&S_sfx[sfx]); } + void UnloadSound(sfxinfo_t* sfx); + + void UpdateSounds(int time); + + FSoundChan* StartSound(int sourcetype, const void* source, + const FVector3* pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f); + + // Stops an origin-less sound from playing from this channel. + void StopSound(int channel); + void StopSound(int sourcetype, const void* actor, int channel); + + void RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); + void ChangeSoundVolume(int sourcetype, const void* source, int channel, double dvolume); + void ChangeSoundPitch(int sourcetype, const void* source, int channel, double pitch); + bool IsSourcePlayingSomething(int sourcetype, const void* actor, int channel, int sound_id); + + // Stop and resume music, during game PAUSE. + bool GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id); + void UnloadAllSounds(); + void Reset(); + void MarkUsed(int num); + void CacheMarkedSounds(); + TArray AllActiveChannels(); + + void MarkAllUnused() + { + for (auto & s: S_sfx) s.bUsed = false; + } + + bool isListener(const void* object) const + { + return object && listener.ListenerObject == object; + } + bool isPlayerReserve(int snd_id) + { + return S_sfx[snd_id].bPlayerReserve; // Later this needs to be abstracted out of the engine itself. Right now that cannot be done. + } + void SetListener(SoundListener& l) + { + listener = l; + } + void SetRestartTime(int time) + { + RestartEvictionsAt = time; + } + void SetPaused(bool on) + { + SoundPaused = on; + } + FSoundChan* GetChannels() + { + return Channels; + } + const char *GetSoundName(FSoundID id) + { + return id == 0 ? "" : S_sfx[id].name; + } + TArray &GetSounds() //Thio should only be used for constructing the sound list or for diagnostics code prinring information about the sound list. + { + return S_sfx; + } + FRolloffInfo& GlobalRolloff() // like GetSounds this is meant for sound list generators, not for gaining cheap access to the sound engine's innards. + { + return S_Rolloff; + } + FRandomSoundList *ResolveRandomSound(sfxinfo_t* sfx) + { + return &S_rnd[sfx->link]; + } + void ClearRandoms() + { + S_rnd.Clear(); + } + + void ChannelVirtualChanged(FISoundChannel* ichan, bool is_virtual); + FString ListSoundChannels(); + + // Allow this to be overridden for special needs. + virtual float GetRolloff(const FRolloffInfo* rolloff, float distance); + virtual void ChannelEnded(FISoundChannel* ichan); // allows the client to do bookkeeping on the sound. + + // Lookup utilities. + int FindSound(const char* logicalname); + int FindSoundByResID(int rid); + int FindSoundNoHash(const char* logicalname); + int FindSoundByLump(int lump); + int AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid = -1); + int FindSoundTentative(const char* name); + void CacheRandomSound(sfxinfo_t* sfx); + unsigned int GetMSLength(FSoundID sound); + int PickReplacement(int refid); + void HashSounds(); + void AddRandomSound(int Owner, TArray list); +}; + + +extern SoundEngine* soundEngine; diff --git a/src/utility/superfasthash.h b/src/utility/superfasthash.h new file mode 100644 index 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) From 83349bee1b2ac0f4927d398238992b0827bc3856 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 8 Dec 2019 21:45:45 +0100 Subject: [PATCH 4/5] - separated reverb data and reverb editor. Again, isolating the part that is game independent from parts that are specific to GZDoom. --- src/CMakeLists.txt | 1 + src/sound/s_environment.cpp | 514 +-------------------------------- src/sound/s_reverbedit.cpp | 555 ++++++++++++++++++++++++++++++++++++ src/sound/s_soundinternal.h | 10 + 4 files changed, 570 insertions(+), 510 deletions(-) create mode 100644 src/sound/s_reverbedit.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 668a5b0ef..fdfb5d0ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -878,6 +878,7 @@ 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 diff --git a/src/sound/s_environment.cpp b/src/sound/s_environment.cpp index affb68de2..9e1d19e37 100644 --- a/src/sound/s_environment.cpp +++ b/src/sound/s_environment.cpp @@ -48,28 +48,7 @@ #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 +89,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 +514,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,7 +524,6 @@ static void ReadReverbDef (int lump) bool inited[NUM_REVERB_FIELDS]; uint8_t bools[32]; - sc.OpenLumpNum(lump); while (sc.GetString ()) { name = copystring (sc.String); @@ -638,17 +616,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; @@ -672,476 +639,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_soundinternal.h b/src/sound/s_soundinternal.h index 86eac0cdb..17cf55e5b 100644 --- a/src/sound/s_soundinternal.h +++ b/src/sound/s_soundinternal.h @@ -386,3 +386,13 @@ public: extern SoundEngine* soundEngine; + +struct FReverbField +{ + int Min, Max; + float REVERB_PROPERTIES::* Float; + int REVERB_PROPERTIES::* Int; + unsigned int Flag; +}; + + From 6725cfcca5ff4add38b717b1cd78bd7b9e01c71d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 8 Dec 2019 22:17:19 +0100 Subject: [PATCH 5/5] - cleaned the includes of the sound backend code of unwanted content. Also simplified the sound init decision making. With FMod gone there is no reason to be pedantic here. Even the check of snd_backend for the Null device could be omitted here, its only realistic use is '-nosound'. --- src/console/c_dispatch.cpp | 5 ++ src/console/c_dispatch.h | 8 +- src/console/c_expr.cpp | 1 + src/gamedata/fonts/v_text.h | 1 - src/r_data/r_vanillatrans.cpp | 1 + src/sound/backend/i_sound.cpp | 18 +---- src/sound/backend/i_soundinternal.h | 1 - src/sound/backend/oalsound.cpp | 113 +++++++++++++++------------- src/sound/s_environment.cpp | 16 +--- src/sound/s_sound.cpp | 4 +- 10 files changed, 77 insertions(+), 91 deletions(-) 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 f9fbd31d5..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); 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/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/sound/backend/i_sound.cpp b/src/sound/backend/i_sound.cpp index 616120b98..0ba595ac7 100644 --- a/src/sound/backend/i_sound.cpp +++ b/src/sound/backend/i_sound.cpp @@ -47,7 +47,6 @@ #include "v_text.h" #include "c_cvars.h" #include "stats.h" -#include "s_music.h" #include "zmusic/zmusic.h" @@ -259,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()) @@ -281,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(); diff --git a/src/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index 311ed22b0..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" diff --git a/src/sound/backend/oalsound.cpp b/src/sound/backend/oalsound.cpp index a16d42b41..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; @@ -2359,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_environment.cpp b/src/sound/s_environment.cpp index 9e1d19e37..276770f6c 100644 --- a/src/sound/s_environment.cpp +++ b/src/sound/s_environment.cpp @@ -32,21 +32,11 @@ ** */ -#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" - FReverbField ReverbFields[] = { @@ -526,7 +516,7 @@ void S_ReadReverbDef (FScanner &sc) while (sc.GetString ()) { - name = copystring (sc.String); + name = strdup (sc.String); sc.MustGetNumber (); id1 = sc.Number; sc.MustGetNumber (); @@ -627,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 diff --git a/src/sound/s_sound.cpp b/src/sound/s_sound.cpp index 7b6492c2a..4fba205dc 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -39,9 +39,7 @@ #include #endif -#include "i_system.h" -#include "i_sound.h" -#include "s_sound.h" +#include "s_soundinternal.h" #include "m_swap.h" #include "superfasthash.h"