2019-12-12 18:21:36 +00:00
# pragma once
2020-04-12 06:09:38 +00:00
# include "i_sound.h"
2019-12-12 18:21:36 +00:00
struct FRandomSoundList
{
TArray < uint32_t > Choices ;
uint32_t Owner = 0 ;
} ;
2020-10-10 07:47:00 +00:00
enum
{
sfx_empty = - 1
} ;
2019-12-12 18:21:36 +00:00
//
// 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.
2020-10-10 07:47:00 +00:00
SoundHandle data { } ;
2019-12-12 18:21:36 +00:00
2020-10-10 07:47:00 +00:00
FString name ; // [RH] Sound name defined in SNDINFO
int lumpnum = sfx_empty ; // lump number of sfx
2019-12-12 18:21:36 +00:00
2020-10-10 07:47:00 +00:00
unsigned int next = - 1 , index = 0 ; // [RH] For hashing
float Volume = 1.f ;
2019-12-12 18:21:36 +00:00
2020-10-10 07:47:00 +00:00
int ResourceId = - 1 ; // Resource ID as implemented by Blood. Not used by Doom but added for completeness.
float LimitRange = 256 * 256 ; // Range for sound limiting (squared for faster computations)
float DefPitch = 0.f ; // A defined pitch instead of a random one the sound plays at, similar to A_StartSound.
float DefPitchMax = 0.f ; // Randomized range with stronger control over pitch itself.
2019-12-12 18:21:36 +00:00
2020-10-10 07:47:00 +00:00
int16_t NearLimit = 4 ; // 0 means unlimited.
uint8_t PitchMask = 0 ;
bool bRandomHeader = false ;
bool bLoadRAW = false ;
bool b16bit = false ;
bool bUsed = false ;
bool bSingular = false ;
bool bTentative = true ;
2019-12-12 18:21:36 +00:00
2020-02-23 17:30:48 +00:00
TArray < int > UserData ;
2019-12-12 18:21:36 +00:00
2020-10-10 07:47:00 +00:00
int RawRate = 0 ; // Sample rate to use when bLoadRAW is true
int LoopStart = - 1 ; // -1 means no specific loop defined
2019-12-12 18:21:36 +00:00
2020-10-10 07:47:00 +00:00
unsigned int link = NO_LINK ; ;
2019-12-12 18:21:36 +00:00
enum { NO_LINK = 0xffffffff } ;
2020-10-10 07:47:00 +00:00
FRolloffInfo Rolloff { } ;
float Attenuation = 1.f ; // Multiplies the attenuation passed to S_Sound.
2019-12-12 18:21:36 +00:00
} ;
// 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
} ;
2020-04-06 19:10:07 +00:00
inline int S_FindSoundByResID ( int ndx ) ;
inline int S_FindSound ( const char * name ) ;
2019-12-12 18:21:36 +00:00
// An index into the S_sfx[] array.
class FSoundID
{
public :
FSoundID ( ) = default ;
2021-12-30 09:30:21 +00:00
2019-12-12 18:21:36 +00:00
static FSoundID byResId ( int ndx )
{
return FSoundID ( S_FindSoundByResID ( ndx ) ) ;
}
FSoundID ( int id )
{
ID = id ;
}
FSoundID ( const char * name )
{
ID = S_FindSound ( name ) ;
}
FSoundID ( const FString & name )
{
ID = S_FindSound ( name . GetChars ( ) ) ;
}
FSoundID ( const FSoundID & other ) = default ;
FSoundID & operator = ( const FSoundID & other ) = default ;
FSoundID & operator = ( const char * name )
{
ID = S_FindSound ( name ) ;
return * this ;
}
FSoundID & operator = ( const FString & name )
{
ID = S_FindSound ( name . GetChars ( ) ) ;
return * this ;
}
bool operator ! = ( FSoundID other ) const
{
return ID ! = other . ID ;
}
bool operator ! = ( int other ) const
{
return ID ! = other ;
}
operator int ( ) const
{
return ID ;
}
private :
int ID ;
protected :
enum EDummy { NoInit } ;
FSoundID ( EDummy ) { }
} ;
2021-12-30 09:30:21 +00:00
2019-12-12 18:21:36 +00:00
class FSoundIDNoInit : public FSoundID
{
public :
FSoundIDNoInit ( ) : FSoundID ( NoInit ) { }
using FSoundID : : operator = ;
} ;
struct FSoundChan : public FISoundChannel
{
FSoundChan * NextChan ; // Next channel in this list.
FSoundChan * * PrevChan ; // Previous channel in this list.
FSoundID SoundID ; // Sound ID of playing sound.
FSoundID OrgID ; // Sound ID of sound used to start this channel.
float Volume ;
2019-12-19 10:47:47 +00:00
int EntChannel ; // Actor's sound channel.
2020-08-26 18:19:54 +00:00
int UserData ; // Not used by the engine, the caller can use this to store some additional info.
2019-12-12 18:21:36 +00:00
int16_t Pitch ; // Pitch variation.
int16_t NearLimit ;
2019-12-19 10:47:47 +00:00
int8_t Priority ;
2019-12-12 18:21:36 +00:00
uint8_t SourceType ;
float LimitRange ;
2019-12-17 18:37:05 +00:00
const void * Source ;
2019-12-19 00:20:43 +00:00
float Point [ 3 ] ; // Sound is not attached to any source.
2019-12-12 18:21:36 +00:00
} ;
// 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
2019-12-16 23:29:38 +00:00
enum EChannel
2019-12-12 18:21:36 +00:00
{
2019-12-16 23:29:38 +00:00
CHAN_AUTO = 0 ,
CHAN_WEAPON = 1 ,
CHAN_VOICE = 2 ,
CHAN_ITEM = 3 ,
CHAN_BODY = 4 ,
CHAN_5 = 5 ,
CHAN_6 = 6 ,
CHAN_7 = 7 ,
2019-12-12 18:21:36 +00:00
} ;
2019-12-16 23:29:38 +00:00
2019-12-12 18:21:36 +00:00
// 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
2020-04-12 06:09:38 +00:00
enum // The core source types, implementations may extend this list as they see fit.
2019-12-12 18:21:36 +00:00
{
2019-12-12 20:42:58 +00:00
SOURCE_Any = - 1 , // Input for check functions meaning 'any source'
2019-12-12 18:21:36 +00:00
SOURCE_Unattached , // Sound is not attached to any particular emitter.
2020-04-12 06:09:38 +00:00
SOURCE_None , // Sound is always on top of the listener.
2019-12-12 18:21:36 +00:00
} ;
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 ) ;
2021-12-30 09:30:21 +00:00
2019-12-12 18:21:36 +00:00
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 < sfxinfo_t > S_sfx ;
2021-02-12 13:44:54 +00:00
FRolloffInfo S_Rolloff { } ;
2019-12-12 18:21:36 +00:00
TArray < uint8_t > S_SoundCurve ;
TMap < int , int > ResIdMap ;
TArray < FRandomSoundList > S_rnd ;
2020-06-11 07:15:44 +00:00
bool blockNewSounds = false ;
2019-12-12 18:21:36 +00:00
private :
void LinkChannel ( FSoundChan * chan , FSoundChan * * head ) ;
void UnlinkChannel ( FSoundChan * chan ) ;
void ReturnChannel ( FSoundChan * chan ) ;
2019-12-19 00:20:43 +00:00
void RestartChannel ( FSoundChan * chan ) ;
2019-12-12 18:21:36 +00:00
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.
2019-12-18 10:09:01 +00:00
virtual void CalcPosVel ( int type , const void * source , const float pt [ 3 ] , int channel , int chanflags , FSoundID chanSound , FVector3 * pos , FVector3 * vel , FSoundChan * chan ) = 0 ;
2019-12-12 18:21:36 +00:00
// 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 ) ;
virtual TArray < uint8_t > ReadSound ( int lumpnum ) = 0 ;
protected :
2020-10-28 20:46:43 +00:00
virtual bool CheckSoundLimit ( sfxinfo_t * sfx , const FVector3 & pos , int near_limit , float limit_range , int sourcetype , const void * actor , int channel , float attenuation ) ;
2019-12-12 18:21:36 +00:00
virtual FSoundID ResolveSound ( const void * ent , int srctype , FSoundID soundid , float & attenuation ) ;
public :
2019-12-26 13:43:44 +00:00
virtual ~ SoundEngine ( )
{
Shutdown ( ) ;
}
2019-12-12 18:21:36 +00:00
void EvictAllChannels ( ) ;
2020-06-11 07:15:44 +00:00
void BlockNewSounds ( bool on )
{
blockNewSounds = on ;
}
2020-04-12 06:09:38 +00:00
virtual void StopChannel ( FSoundChan * chan ) ;
2020-02-09 12:26:51 +00:00
sfxinfo_t * LoadSound ( sfxinfo_t * sfx ) ;
2021-03-02 10:58:29 +00:00
const sfxinfo_t * GetSfx ( unsigned snd )
{
if ( snd > = S_sfx . Size ( ) ) return nullptr ;
return & S_sfx [ snd ] ;
}
2019-12-12 18:21:36 +00:00
// Initializes sound stuff, including volume
// Sets channels, SFX and music volume,
// allocates channel buffer, sets S_sfx lookup.
//
2020-04-12 06:09:38 +00:00
void Init ( TArray < uint8_t > & sndcurve ) ;
2019-12-12 18:21:36 +00:00
void InitData ( ) ;
void Clear ( ) ;
void Shutdown ( ) ;
void StopAllChannels ( void ) ;
void SetPitch ( FSoundChan * chan , float dpitch ) ;
2019-12-18 21:13:19 +00:00
void SetVolume ( FSoundChan * chan , float vol ) ;
2019-12-12 18:21:36 +00:00
FSoundChan * GetChannel ( void * syschan ) ;
void RestoreEvictedChannels ( ) ;
void CalcPosVel ( FSoundChan * chan , FVector3 * pos , FVector3 * vel ) ;
// Loads a sound, including any random sounds it might reference.
2020-04-12 06:09:38 +00:00
virtual void CacheSound ( sfxinfo_t * sfx ) ;
2019-12-12 18:21:36 +00:00
void CacheSound ( int sfx ) { CacheSound ( & S_sfx [ sfx ] ) ; }
void UnloadSound ( sfxinfo_t * sfx ) ;
2020-09-26 15:43:34 +00:00
void UnloadSound ( int sfx )
{
UnloadSound ( & S_sfx [ sfx ] ) ;
}
2019-12-12 18:21:36 +00:00
void UpdateSounds ( int time ) ;
FSoundChan * StartSound ( int sourcetype , const void * source ,
2020-04-12 06:09:38 +00:00
const FVector3 * pt , int channel , EChanFlags flags , FSoundID sound_id , float volume , float attenuation , FRolloffInfo * rolloff = nullptr , float spitch = 0.0f , float startTime = 0.0f ) ;
2019-12-12 18:21:36 +00:00
// Stops an origin-less sound from playing from this channel.
2019-12-12 20:42:58 +00:00
void StopSoundID ( int sound_id ) ;
void StopSound ( int channel , int sound_id = - 1 ) ;
void StopSound ( int sourcetype , const void * actor , int channel , int sound_id = - 1 ) ;
2020-04-12 06:09:38 +00:00
void StopActorSounds ( int sourcetype , const void * actor , int chanmin , int chanmax ) ;
2019-12-12 18:21:36 +00:00
void RelinkSound ( int sourcetype , const void * from , const void * to , const FVector3 * optpos ) ;
void ChangeSoundVolume ( int sourcetype , const void * source , int channel , double dvolume ) ;
2019-12-12 20:42:58 +00:00
void ChangeSoundPitch ( int sourcetype , const void * source , int channel , double pitch , int sound_id = - 1 ) ;
2019-12-17 22:25:07 +00:00
bool IsSourcePlayingSomething ( int sourcetype , const void * actor , int channel , int sound_id = - 1 ) ;
2019-12-12 18:21:36 +00:00
// Stop and resume music, during game PAUSE.
2021-11-28 20:57:03 +00:00
int GetSoundPlayingInfo ( int sourcetype , const void * source , int sound_id , int chan = - 1 ) ;
2019-12-12 18:21:36 +00:00
void UnloadAllSounds ( ) ;
void Reset ( ) ;
void MarkUsed ( int num ) ;
void CacheMarkedSounds ( ) ;
TArray < FSoundChan * > AllActiveChannels ( ) ;
2022-10-02 18:33:18 +00:00
virtual void SetSoundPaused ( int state ) { }
2019-12-12 18:21:36 +00:00
void MarkAllUnused ( )
{
for ( auto & s : S_sfx ) s . bUsed = false ;
}
bool isListener ( const void * object ) const
{
return object & & listener . ListenerObject = = object ;
}
void SetListener ( SoundListener & l )
{
listener = l ;
}
2020-04-12 06:09:38 +00:00
const SoundListener & GetListener ( ) const
{
return listener ;
}
2019-12-12 18:21:36 +00:00
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 . GetChars ( ) ;
}
2020-02-09 12:26:51 +00:00
TArray < sfxinfo_t > & GetSounds ( ) //This should only be used for constructing the sound list or for diagnostics code prinring information about the sound list.
2019-12-12 18:21:36 +00:00
{
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 ( ) ;
}
2020-02-23 17:30:48 +00:00
int * GetUserData ( int snd )
2019-12-12 20:42:58 +00:00
{
2019-12-15 12:34:00 +00:00
return S_sfx [ snd ] . UserData . Data ( ) ;
2019-12-12 20:42:58 +00:00
}
2019-12-15 12:34:00 +00:00
bool isValidSoundId ( int id )
2019-12-12 20:42:58 +00:00
{
2019-12-17 22:25:07 +00:00
return id > 0 & & id < ( int ) S_sfx . Size ( ) & & ! S_sfx [ id ] . bTentative & & S_sfx [ id ] . lumpnum ! = sfx_empty ;
2019-12-12 20:42:58 +00:00
}
2019-12-15 12:34:00 +00:00
template < class func > bool EnumerateChannels ( func callback )
2019-12-12 20:42:58 +00:00
{
2020-02-23 16:12:40 +00:00
FSoundChan * chan = Channels ;
while ( chan )
2019-12-15 12:34:00 +00:00
{
2020-02-24 19:19:03 +00:00
auto next = chan - > NextChan ;
2019-12-17 18:37:05 +00:00
int res = callback ( chan ) ;
if ( res ) return res > 0 ;
2020-02-23 16:12:40 +00:00
chan = next ;
2019-12-15 12:34:00 +00:00
}
return false ;
2019-12-12 20:42:58 +00:00
}
2019-12-12 18:21:36 +00:00
2019-12-15 15:32:39 +00:00
void SetDefaultRolloff ( FRolloffInfo * ro )
{
S_Rolloff = * ro ;
}
2019-12-12 18:21:36 +00:00
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.
2020-09-26 15:43:34 +00:00
virtual void SoundDone ( FISoundChannel * ichan ) ; // gets called when the sound has been completely taken down.
2019-12-12 18:21:36 +00:00
// Lookup utilities.
int FindSound ( const char * logicalname ) ;
int FindSoundByResID ( int rid ) ;
int FindSoundNoHash ( const char * logicalname ) ;
int FindSoundByLump ( int lump ) ;
2020-04-12 06:09:38 +00:00
virtual int AddSoundLump ( const char * logicalname , int lump , int CurrentPitchMask , int resid = - 1 , int nearlimit = 2 ) ;
2019-12-12 18:21:36 +00:00
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 < uint32_t > list ) ;
} ;
extern SoundEngine * soundEngine ;
struct FReverbField
{
int Min , Max ;
float REVERB_PROPERTIES : : * Float ;
int REVERB_PROPERTIES : : * Int ;
unsigned int Flag ;
} ;
2020-01-27 21:29:45 +00:00
2020-04-06 19:10:07 +00:00
inline int S_FindSoundByResID ( int ndx )
{
return soundEngine - > FindSoundByResID ( ndx ) ;
}
inline int S_FindSound ( const char * name )
{
return soundEngine - > FindSound ( name ) ;
}
2021-05-21 23:34:00 +00:00
int SoundEnabled ( ) ;