/
s_soundinternal.h
427 lines (357 loc) · 11.8 KB
/
s_soundinternal.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
#pragma once
#include "i_sound.h"
#include "name.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{};
FName name; // [RH] Sound name defined in SNDINFO
int lumpnum = sfx_empty; // lump number of sfx
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.
float 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<FName, FSoundID> SoundMap;
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 FindSoundByResIDNoHash(int rid);
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, int nearlimit = 2);
void CacheRandomSound(sfxinfo_t* sfx);
unsigned int GetMSLength(FSoundID sound);
FSoundID PickReplacement(FSoundID refid);
void HashSounds();
void AddRandomSound(FSoundID Owner, TArray<FSoundID> list);
};
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();