2019-12-08 09:07:26 +00:00
# pragma once
2019-12-08 20:22:53 +00:00
# include "i_sound.h"
2019-12-08 09:07:26 +00:00
2019-12-08 20:22:53 +00:00
struct FRandomSoundList
{
TArray < uint32_t > Choices ;
uint32_t Owner = 0 ;
} ;
extern int sfx_empty ;
2019-12-08 09:07:26 +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.
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 ;
2019-12-08 20:22:53 +00:00
int ResourceId ; // Resource ID as implemented by Blood. Not used by Doom but added for completeness.
2019-12-08 09:07:26 +00:00
uint8_t PitchMask ;
int16_t NearLimit ; // 0 means unlimited
float LimitRange ; // Range for sound limiting (squared for faster computations)
unsigned bRandomHeader : 1 ;
unsigned bLoadRAW : 1 ;
unsigned b16bit : 1 ;
unsigned bUsed : 1 ;
unsigned bSingular : 1 ;
2019-12-08 20:22:53 +00:00
2019-12-08 09:07:26 +00:00
unsigned bTentative : 1 ;
2019-12-08 20:22:53 +00:00
unsigned bPlayerReserve : 1 ;
unsigned bPlayerCompat : 1 ;
2019-12-08 09:07:26 +00:00
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.
2019-12-16 20:45:34 +00:00
void Clear ( )
{
data . Clear ( ) ;
data3d . Clear ( ) ;
lumpnum = - 1 ; // lump number of sfx
next = - 1 ;
index = 0 ; // [RH] For hashing
Volume = 1.f ;
ResourceId = - 1 ;
PitchMask = 0 ;
NearLimit = 4 ; // 0 means unlimited
LimitRange = 256 * 256 ;
bRandomHeader = false ;
bLoadRAW = false ;
b16bit = false ;
bUsed = false ;
bSingular = false ;
bTentative = true ;
RawRate = 0 ; // Sample rate to use when bLoadRAW is true
LoopStart = 0 ; // -1 means no specific loop defined
link = NO_LINK ;
Rolloff = { } ;
Attenuation = 1.f ;
}
2019-12-08 09:07:26 +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
} ;
int S_FindSound ( const char * logicalname ) ;
int S_FindSoundByResID ( int snd_id ) ;
// An index into the S_sfx[] array.
class FSoundID
{
public :
FSoundID ( ) = default ;
static FSoundID byResId ( int ndx )
{
return FSoundID ( S_FindSoundByResID ( ndx ) ) ;
}
FSoundID ( int id )
{
ID = id ;
}
FSoundID ( const char * name )
{
ID = S_FindSound ( name ) ;
}
FSoundID ( const FString & name )
{
ID = S_FindSound ( name . GetChars ( ) ) ;
}
FSoundID ( const FSoundID & other ) = default ;
FSoundID & operator = ( const FSoundID & other ) = default ;
FSoundID & operator = ( const char * name )
{
ID = S_FindSound ( name ) ;
return * this ;
}
FSoundID & operator = ( const FString & name )
{
ID = S_FindSound ( name . GetChars ( ) ) ;
return * this ;
}
bool operator ! = ( FSoundID other ) const
{
return ID ! = other . ID ;
}
bool operator ! = ( int other ) const
{
return ID ! = other ;
}
operator int ( ) const
{
return ID ;
}
private :
int ID ;
protected :
enum EDummy { NoInit } ;
FSoundID ( EDummy ) { }
} ;
class FSoundIDNoInit : public FSoundID
{
public :
FSoundIDNoInit ( ) : FSoundID ( NoInit ) { }
using FSoundID : : operator = ;
} ;
struct FSoundChan : public FISoundChannel
{
FSoundChan * NextChan ; // Next channel in this list.
FSoundChan * * PrevChan ; // Previous channel in this list.
FSoundID SoundID ; // Sound ID of playing sound.
FSoundID OrgID ; // Sound ID of sound used to start this channel.
float Volume ;
2019-12-17 10:43:13 +00:00
int EntChannel ; // Actor's sound channel.
2019-12-08 09:07:26 +00:00
int16_t Pitch ; // Pitch variation.
int16_t NearLimit ;
2019-12-17 10:43:13 +00:00
int8_t Priority ;
2019-12-08 09:07:26 +00:00
uint8_t SourceType ;
float LimitRange ;
union
{
2019-12-08 12:28:52 +00:00
const void * Source ;
2019-12-08 09:07:26 +00:00
float Point [ 3 ] ; // Sound is not attached to any source.
} ;
} ;
// sound channels
// channel 0 never willingly overrides
// other channels (1-7) always override a playing sound on that channel
//
// CHAN_AUTO searches down from channel 7 until it finds a channel not in use
// CHAN_WEAPON is for weapons
// CHAN_VOICE is for oof, sight, or other voice sounds
// CHAN_ITEM is for small things and item pickup
// CHAN_BODY is for generic body sounds
2019-12-16 22:52:39 +00:00
enum EChannel
2019-12-08 09:07:26 +00:00
{
2019-12-16 22:52:39 +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-08 09:07:26 +00:00
} ;
2019-12-16 22:52:39 +00:00
2019-12-08 09:07:26 +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
2019-12-08 12:28:52 +00:00
enum // This cannot be remain as this, but for now it has to suffice.
{
2019-12-16 20:45:34 +00:00
SOURCE_Any = - 1 , // Input for check functions meaning 'any source'
2019-12-08 12:28:52 +00:00
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.
} ;
2019-12-08 09:07:26 +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 ) ;
2019-12-08 20:22:53 +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 ;
FRolloffInfo S_Rolloff ;
TArray < uint8_t > S_SoundCurve ;
TMap < int , int > ResIdMap ;
TArray < FRandomSoundList > 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.
2019-12-19 10:57:58 +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-08 20:22:53 +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 ) ;
bool CheckSoundLimit ( sfxinfo_t * sfx , const FVector3 & pos , int near_limit , float limit_range , int sourcetype , const void * actor , int channel ) ;
virtual TArray < uint8_t > ReadSound ( int lumpnum ) = 0 ;
2019-12-12 00:31:41 +00:00
protected :
2019-12-12 16:30:11 +00:00
virtual FSoundID ResolveSound ( const void * ent , int srctype , FSoundID soundid , float & attenuation ) ;
2019-12-08 20:22:53 +00:00
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 < uint8_t > & sndcurve ) ;
void InitData ( ) ;
2019-12-09 14:06:41 +00:00
void Clear ( ) ;
2019-12-08 20:22:53 +00:00
void Shutdown ( ) ;
void StopAllChannels ( void ) ;
void SetPitch ( FSoundChan * chan , float dpitch ) ;
2019-12-19 10:57:58 +00:00
void SetVolume ( FSoundChan * chan , float vol ) ;
2019-12-08 20:22:53 +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.
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 ,
2019-12-16 22:52:39 +00:00
const FVector3 * pt , int channel , EChanFlags flags , FSoundID sound_id , float volume , float attenuation , FRolloffInfo * rolloff = nullptr , float spitch = 0.0f ) ;
2019-12-08 20:22:53 +00:00
// Stops an origin-less sound from playing from this channel.
2019-12-16 20:45:34 +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 ) ;
2019-12-08 20:22:53 +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-16 20:45:34 +00:00
void ChangeSoundPitch ( int sourcetype , const void * source , int channel , double pitch , int sound_id = - 1 ) ;
2019-12-19 10:57:58 +00:00
bool IsSourcePlayingSomething ( int sourcetype , const void * actor , int channel , int sound_id = - 1 ) ;
2019-12-08 20:22:53 +00:00
// Stop and resume music, during game PAUSE.
2019-12-16 20:45:34 +00:00
int GetSoundPlayingInfo ( int sourcetype , const void * source , int sound_id ) ;
2019-12-08 20:22:53 +00:00
void UnloadAllSounds ( ) ;
void Reset ( ) ;
void MarkUsed ( int num ) ;
void CacheMarkedSounds ( ) ;
TArray < FSoundChan * > 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 )
{
2019-12-16 20:45:34 +00:00
return id = = 0 ? " " : S_sfx [ id ] . name . GetChars ( ) ;
2019-12-08 20:22:53 +00:00
}
TArray < sfxinfo_t > & 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 ( ) ;
}
2019-12-19 10:57:58 +00:00
bool isValidSoundId ( int id )
{
return id > 0 & & id < ( int ) S_sfx . Size ( ) & & ! S_sfx [ id ] . bTentative & & S_sfx [ id ] . lumpnum ! = sfx_empty ;
}
2019-12-08 20:22:53 +00:00
2019-12-16 20:45:34 +00:00
template < class func > bool EnumerateChannels ( func callback )
{
for ( FSoundChan * chan = Channels ; chan ; chan = chan - > NextChan )
{
2019-12-19 10:57:58 +00:00
int res = callback ( chan ) ;
if ( res ) return res > 0 ;
2019-12-16 20:45:34 +00:00
}
return false ;
}
void SetDefaultRolloff ( FRolloffInfo * ro )
{
S_Rolloff = * ro ;
}
2019-12-08 20:22:53 +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.
// Lookup utilities.
int FindSound ( const char * logicalname ) ;
int FindSoundByResID ( int rid ) ;
int FindSoundNoHash ( const char * logicalname ) ;
int FindSoundByLump ( int lump ) ;
2019-12-16 20:45:34 +00:00
int AddSoundLump ( const char * logicalname , int lump , int CurrentPitchMask , int resid = - 1 , int nearlimit = 2 ) ;
2019-12-19 10:57:58 +00:00
int AddSfx ( sfxinfo_t & sfx ) ;
2019-12-08 20:22:53 +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 ;
2019-12-08 20:45:45 +00:00
struct FReverbField
{
int Min , Max ;
float REVERB_PROPERTIES : : * Float ;
int REVERB_PROPERTIES : : * Int ;
unsigned int Flag ;
} ;