mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-03 01:22:19 +00:00
f103e33300
All Duke script code has been changed to use strings as sound names now, just like GZDoom these will be looked up at compile time. Original Duke sound indices still exist but are now being managed as resource IDs, not sound engine indices anymore.
435 lines
12 KiB
C++
435 lines
12 KiB
C++
#pragma once
|
|
|
|
#include "i_sound.h"
|
|
|
|
enum
|
|
{
|
|
sfx_empty = -1
|
|
};
|
|
|
|
|
|
|
|
// 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
|
|
};
|
|
|
|
|
|
// An index into the S_sfx[] array.
|
|
class FSoundID
|
|
{
|
|
public:
|
|
FSoundID() = default;
|
|
|
|
private:
|
|
constexpr FSoundID(int id) : ID(id)
|
|
{
|
|
}
|
|
public:
|
|
static constexpr FSoundID fromInt(int i)
|
|
{
|
|
return FSoundID(i);
|
|
}
|
|
FSoundID(const FSoundID &other) = default;
|
|
FSoundID &operator=(const FSoundID &other) = default;
|
|
bool operator !=(FSoundID other) const
|
|
{
|
|
return ID != other.ID;
|
|
}
|
|
bool operator ==(FSoundID other) const
|
|
{
|
|
return ID == other.ID;
|
|
}
|
|
bool operator ==(int other) const = delete;
|
|
bool operator !=(int other) const = delete;
|
|
constexpr int index() const
|
|
{
|
|
return ID;
|
|
}
|
|
constexpr bool isvalid() const
|
|
{
|
|
return ID > 0;
|
|
}
|
|
private:
|
|
|
|
int ID;
|
|
};
|
|
|
|
constexpr FSoundID NO_SOUND = FSoundID::fromInt(0);
|
|
constexpr FSoundID INVALID_SOUND = FSoundID::fromInt(-1);
|
|
|
|
struct FRandomSoundList
|
|
{
|
|
TArray<FSoundID> Choices;
|
|
FSoundID Owner = NO_SOUND;
|
|
};
|
|
|
|
|
|
//
|
|
// 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{};
|
|
|
|
FString name; // [RH] Sound name defined in SNDINFO
|
|
int lumpnum = sfx_empty; // lump number of sfx
|
|
|
|
unsigned int next = -1, index = 0; // [RH] For hashing
|
|
float Volume = 1.f;
|
|
|
|
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.
|
|
|
|
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;
|
|
|
|
TArray<int> UserData;
|
|
|
|
int RawRate = 0; // Sample rate to use when bLoadRAW is true
|
|
int LoopStart = -1; // -1 means no specific loop defined
|
|
|
|
FSoundID link = NO_LINK;
|
|
constexpr static FSoundID NO_LINK = FSoundID::fromInt(-1);
|
|
|
|
FRolloffInfo Rolloff{};
|
|
float Attenuation = 1.f; // Multiplies the attenuation passed to S_Sound.
|
|
};
|
|
|
|
|
|
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;
|
|
int EntChannel; // Actor's sound channel.
|
|
int UserData; // Not used by the engine, the caller can use this to store some additional info.
|
|
int16_t Pitch; // Pitch variation.
|
|
int16_t NearLimit;
|
|
int8_t Priority;
|
|
uint8_t SourceType;
|
|
float LimitRange;
|
|
const void *Source;
|
|
float Point[3]; // Sound is not attached to any source.
|
|
};
|
|
|
|
|
|
// sound channels
|
|
// channel 0 never willingly overrides
|
|
// other channels (1-7) always override a playing sound on that channel
|
|
//
|
|
// CHAN_AUTO searches down from channel 7 until it finds a channel not in use
|
|
// CHAN_WEAPON is for weapons
|
|
// CHAN_VOICE is for oof, sight, or other voice sounds
|
|
// CHAN_ITEM is for small things and item pickup
|
|
// CHAN_BODY is for generic body sounds
|
|
|
|
enum EChannel
|
|
{
|
|
CHAN_AUTO = 0,
|
|
CHAN_WEAPON = 1,
|
|
CHAN_VOICE = 2,
|
|
CHAN_ITEM = 3,
|
|
CHAN_BODY = 4,
|
|
CHAN_5 = 5,
|
|
CHAN_6 = 6,
|
|
CHAN_7 = 7,
|
|
};
|
|
|
|
|
|
|
|
// sound attenuation values
|
|
#define ATTN_NONE 0.f // full volume the entire level
|
|
#define ATTN_NORM 1.f
|
|
#define ATTN_IDLE 1.001f
|
|
#define ATTN_STATIC 3.f // diminish very rapidly with distance
|
|
|
|
enum // The core source types, implementations may extend this list as they see fit.
|
|
{
|
|
SOURCE_Any = -1, // Input for check functions meaning 'any source'
|
|
SOURCE_Unattached, // Sound is not attached to any particular emitter.
|
|
SOURCE_None, // Sound is always on top of the listener.
|
|
};
|
|
|
|
|
|
extern ReverbContainer *Environments;
|
|
extern ReverbContainer *DefaultEnvironments[26];
|
|
|
|
void S_ParseReverbDef ();
|
|
void S_UnloadReverbDef ();
|
|
void S_SetEnvironment (const ReverbContainer *settings);
|
|
ReverbContainer *S_FindEnvironment (const char *name);
|
|
ReverbContainer *S_FindEnvironment (int id);
|
|
void S_AddEnvironment (ReverbContainer *settings);
|
|
|
|
class SoundEngine
|
|
{
|
|
protected:
|
|
bool SoundPaused = false; // whether sound is paused
|
|
int RestartEvictionsAt = 0; // do not restart evicted channels before this time
|
|
SoundListener listener{};
|
|
|
|
FSoundChan* Channels = nullptr;
|
|
FSoundChan* FreeChannels = nullptr;
|
|
|
|
// the complete set of sound effects
|
|
TArray<sfxinfo_t> S_sfx;
|
|
FRolloffInfo S_Rolloff{};
|
|
TArray<uint8_t> S_SoundCurve;
|
|
TMap<int, FSoundID> ResIdMap;
|
|
TArray<FRandomSoundList> S_rnd;
|
|
bool blockNewSounds = false;
|
|
|
|
private:
|
|
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, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan *chan) = 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(FSoundID sound_id);
|
|
virtual TArray<uint8_t> ReadSound(int lumpnum) = 0;
|
|
|
|
protected:
|
|
virtual bool CheckSoundLimit(sfxinfo_t* sfx, const FVector3& pos, int near_limit, float limit_range, int sourcetype, const void* actor, int channel, float attenuation);
|
|
virtual FSoundID ResolveSound(const void *ent, int srctype, FSoundID soundid, float &attenuation);
|
|
|
|
public:
|
|
virtual ~SoundEngine()
|
|
{
|
|
Shutdown();
|
|
}
|
|
void EvictAllChannels();
|
|
|
|
void BlockNewSounds(bool on)
|
|
{
|
|
blockNewSounds = on;
|
|
}
|
|
|
|
virtual void StopChannel(FSoundChan* chan);
|
|
sfxinfo_t* LoadSound(sfxinfo_t* sfx);
|
|
sfxinfo_t* GetWritableSfx(FSoundID snd)
|
|
{
|
|
if ((unsigned)snd.index() >= S_sfx.Size()) return nullptr;
|
|
return &S_sfx[snd.index()];
|
|
}
|
|
|
|
const sfxinfo_t* GetSfx(FSoundID snd)
|
|
{
|
|
return GetWritableSfx(snd);
|
|
}
|
|
|
|
unsigned GetNumSounds() const
|
|
{
|
|
return S_sfx.Size();
|
|
}
|
|
|
|
sfxinfo_t* AllocateSound()
|
|
{
|
|
return &S_sfx[S_sfx.Reserve(1)];
|
|
}
|
|
|
|
// 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();
|
|
void Clear();
|
|
void Shutdown();
|
|
|
|
void StopAllChannels(void);
|
|
void SetPitch(FSoundChan* chan, float dpitch);
|
|
void SetVolume(FSoundChan* chan, float vol);
|
|
|
|
FSoundChan* GetChannel(void* syschan);
|
|
void RestoreEvictedChannels();
|
|
void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel);
|
|
|
|
// Loads a sound, including any random sounds it might reference.
|
|
virtual void CacheSound(sfxinfo_t* sfx);
|
|
void CacheSound(FSoundID sfx) { CacheSound(&S_sfx[sfx.index()]); }
|
|
void UnloadSound(sfxinfo_t* sfx);
|
|
void UnloadSound(int sfx)
|
|
{
|
|
UnloadSound(&S_sfx[sfx]);
|
|
}
|
|
|
|
void UpdateSounds(int time);
|
|
|
|
FSoundChan* StartSound(int sourcetype, const void* source,
|
|
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);
|
|
|
|
// Stops an origin-less sound from playing from this channel.
|
|
void StopSoundID(FSoundID sound_id);
|
|
void StopSound(int channel, FSoundID sound_id = INVALID_SOUND);
|
|
void StopSound(int sourcetype, const void* actor, int channel, FSoundID sound_id = INVALID_SOUND);
|
|
void StopActorSounds(int sourcetype, const void* actor, int chanmin, int chanmax);
|
|
|
|
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, FSoundID sound_id = INVALID_SOUND);
|
|
bool IsSourcePlayingSomething(int sourcetype, const void* actor, int channel, FSoundID sound_id = INVALID_SOUND);
|
|
|
|
// Stop and resume music, during game PAUSE.
|
|
int GetSoundPlayingInfo(int sourcetype, const void* source, FSoundID sound_id, int chan = -1);
|
|
void UnloadAllSounds();
|
|
void Reset();
|
|
void MarkUsed(FSoundID num);
|
|
void CacheMarkedSounds();
|
|
TArray<FSoundChan*> AllActiveChannels();
|
|
virtual void SetSoundPaused(int state) {}
|
|
|
|
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;
|
|
}
|
|
const SoundListener& GetListener() const
|
|
{
|
|
return listener;
|
|
}
|
|
void SetRestartTime(int time)
|
|
{
|
|
RestartEvictionsAt = time;
|
|
}
|
|
void SetPaused(bool on)
|
|
{
|
|
SoundPaused = on;
|
|
}
|
|
FSoundChan* GetChannels()
|
|
{
|
|
return Channels;
|
|
}
|
|
const char *GetSoundName(FSoundID id)
|
|
{
|
|
return !id.isvalid() ? "" : S_sfx[id.index()].name.GetChars();
|
|
}
|
|
FRolloffInfo& GlobalRolloff() // 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.index()];
|
|
}
|
|
void ClearRandoms()
|
|
{
|
|
S_rnd.Clear();
|
|
}
|
|
int *GetUserData(FSoundID snd)
|
|
{
|
|
return S_sfx[snd.index()].UserData.Data();
|
|
}
|
|
bool isValidSoundId(FSoundID sid)
|
|
{
|
|
int id = sid.index();
|
|
return id > 0 && id < (int)S_sfx.Size() && !S_sfx[id].bTentative && (S_sfx[id].lumpnum != sfx_empty || S_sfx[id].bRandomHeader || S_sfx[id].link != sfxinfo_t::NO_LINK);
|
|
}
|
|
|
|
template<class func> bool EnumerateChannels(func callback)
|
|
{
|
|
FSoundChan* chan = Channels;
|
|
while (chan)
|
|
{
|
|
auto next = chan->NextChan;
|
|
int res = callback(chan);
|
|
if (res) return res > 0;
|
|
chan = next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SetDefaultRolloff(FRolloffInfo* ro)
|
|
{
|
|
S_Rolloff = *ro;
|
|
}
|
|
|
|
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.
|
|
virtual void SoundDone(FISoundChannel* ichan); // gets called when the sound has been completely taken down.
|
|
|
|
// Lookup utilities.
|
|
FSoundID FindSound(const char* logicalname);
|
|
FSoundID FindSoundByResID(int rid);
|
|
FSoundID FindSoundNoHash(const char* logicalname);
|
|
FSoundID FindSoundByLump(int lump);
|
|
virtual FSoundID AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid = -1, int nearlimit = 2);
|
|
FSoundID FindSoundTentative(const char* name);
|
|
void CacheRandomSound(sfxinfo_t* sfx);
|
|
unsigned int GetMSLength(FSoundID sound);
|
|
FSoundID PickReplacement(FSoundID refid);
|
|
void HashSounds();
|
|
void AddRandomSound(FSoundID Owner, TArray<FSoundID> list);
|
|
void RemoveResourceID(int id)
|
|
{
|
|
ResIdMap.Remove(id);
|
|
}
|
|
|
|
TArray<sfxinfo_t>& GetSounds() //We still need this for a short time...
|
|
{
|
|
return S_sfx;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
extern SoundEngine* soundEngine;
|
|
|
|
struct FReverbField
|
|
{
|
|
int Min, Max;
|
|
float REVERB_PROPERTIES::* Float;
|
|
int REVERB_PROPERTIES::* Int;
|
|
unsigned int Flag;
|
|
};
|
|
|
|
|
|
inline FSoundID S_FindSoundByResID(int ndx)
|
|
{
|
|
return soundEngine->FindSoundByResID(ndx);
|
|
}
|
|
|
|
inline FSoundID S_FindSound(const char* name)
|
|
{
|
|
return soundEngine->FindSound(name);
|
|
}
|
|
|
|
int SoundEnabled();
|