- maintain ambient sounds in a separate structure.

This commit is contained in:
Christoph Oelckers 2019-12-19 01:20:43 +01:00
parent 52d13f5ee0
commit 92c8e4c110
6 changed files with 372 additions and 324 deletions

View file

@ -28,7 +28,7 @@ enum EChanFlag
CHANF_FORGETTABLE = 4, // internal: Forget channel data when sound stops.
CHANF_JUSTSTARTED = 512, // internal: Sound has not been updated yet.
CHANF_ABSTIME = 1024, // internal: Start time is absolute and does not depend on current time.
CHANF_VIRTUAL = 2048, // Channel only plays on demand but won't get deleted automatically.
CHANF_VIRTUAL = 2048, // internal: Channel is currently virtual
CHANF_NOSTOP = 4096, // only for A_PlaySound. Does not start if channel is playing something.
CHANF_OVERLAP = 8192, // [MK] Does not stop any sounds in the channel and instead plays over them.
};

View file

@ -479,7 +479,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source,
// If the sound is blocked and not looped, return now. If the sound
// is blocked and looped, pretend to play it so that it can
// eventually play for real.
if ((chanflags & (CHANF_EVICTED | CHANF_LOOP | CHANF_VIRTUAL)) == CHANF_EVICTED)
if ((chanflags & (CHANF_EVICTED | CHANF_LOOP)) == CHANF_EVICTED)
{
return NULL;
}
@ -550,7 +550,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source,
// sound is paused and a non-looped sound is being started.
// Such a sound would play right after unpausing which wouldn't sound right.
if (!(chanflags & (CHANF_LOOP|CHANF_VIRTUAL)) && !(chanflags & (CHANF_UI|CHANF_NOPAUSE)) && SoundPaused)
if (!(chanflags & CHANF_LOOP) && !(chanflags & (CHANF_UI|CHANF_NOPAUSE)) && SoundPaused)
{
return NULL;
}
@ -587,7 +587,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source,
chan = (FSoundChan*)GSnd->StartSound (sfx->data, float(volume), pitch, startflags, NULL);
}
}
if (chan == NULL && (chanflags & CHANF_LOOP|CHANF_VIRTUAL))
if (chan == NULL && (chanflags & CHANF_LOOP))
{
chan = (FSoundChan*)GetChannel(NULL);
GSnd->MarkStartTime(chan);
@ -640,7 +640,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source,
void SoundEngine::RestartChannel(FSoundChan *chan)
{
if (!(chan->ChanFlags & CHANF_EVICTED)) return;
assert(chan->ChanFlags & CHANF_EVICTED);
FSoundChan *ochan;
sfxinfo_t *sfx = &S_sfx[chan->SoundID];
@ -1014,7 +1014,7 @@ void SoundEngine::RelinkSound (int sourcetype, const void *from, const void *to,
{
chan->Source = to;
}
else if (!(chan->ChanFlags & (CHANF_LOOP|CHANF_VIRTUAL)) && optpos)
else if (!(chan->ChanFlags & CHANF_LOOP) && optpos)
{
chan->Source = NULL;
chan->SourceType = SOURCE_Unattached;
@ -1175,31 +1175,6 @@ bool SoundEngine::IsSourcePlayingSomething (int sourcetype, const void *actor, i
return false;
}
//==========================================================================
//
// S_EvictChannel
//
// Forcibly evicts one single channel.
//
//==========================================================================
void SoundEngine::EvictChannel(FSoundChan *chan)
{
if (!(chan->ChanFlags & CHANF_EVICTED))
{
chan->ChanFlags |= CHANF_EVICTED;
if (chan->SysChannel != NULL)
{
if (!(chan->ChanFlags & CHANF_ABSTIME))
{
chan->StartTime = GSnd ? GSnd->GetPosition(chan) : 0;
chan->ChanFlags |= CHANF_ABSTIME;
}
StopChannel(chan);
}
}
}
//==========================================================================
//
// S_EvictAllChannels
@ -1216,7 +1191,21 @@ void SoundEngine::EvictAllChannels()
for (chan = Channels; chan != NULL; chan = next)
{
next = chan->NextChan;
EvictChannel(chan);
if (!(chan->ChanFlags & CHANF_EVICTED))
{
chan->ChanFlags |= CHANF_EVICTED;
if (chan->SysChannel != NULL)
{
if (!(chan->ChanFlags & CHANF_ABSTIME))
{
chan->StartTime = GSnd ? GSnd->GetPosition(chan) : 0;
chan->ChanFlags |= CHANF_ABSTIME;
}
StopChannel(chan);
}
// assert(chan->NextChan == next);
}
}
}
@ -1238,7 +1227,7 @@ void SoundEngine::RestoreEvictedChannel(FSoundChan *chan)
if (chan->ChanFlags & CHANF_EVICTED)
{
RestartChannel(chan);
if (!(chan->ChanFlags & (CHANF_LOOP|CHANF_VIRTUAL)))
if (!(chan->ChanFlags & CHANF_LOOP))
{
if (chan->ChanFlags & CHANF_EVICTED)
{ // Still evicted and not looping? Forget about it.
@ -1250,7 +1239,7 @@ void SoundEngine::RestoreEvictedChannel(FSoundChan *chan)
}
}
}
else if (chan->SysChannel == NULL && (chan->ChanFlags & (CHANF_FORGETTABLE | CHANF_LOOP | CHANF_VIRTUAL)) == CHANF_FORGETTABLE)
else if (chan->SysChannel == NULL && (chan->ChanFlags & (CHANF_FORGETTABLE | CHANF_LOOP)) == CHANF_FORGETTABLE)
{
ReturnChannel(chan);
}
@ -1359,14 +1348,14 @@ void SoundEngine::ChannelEnded(FISoundChannel *ichan)
if (schan != NULL)
{
// If the sound was stopped with GSnd->StopSound(), then we know
// it wasn't evicted. Otherwise, if it's looping or declared virtual, it must have
// it wasn't evicted. Otherwise, if it's looping, it must have
// been evicted. If it's not looping, then it was evicted if it
// didn't reach the end of its playback.
if (schan->ChanFlags & CHANF_FORGETTABLE)
{
evicted = false;
}
else if (schan->ChanFlags & (CHANF_LOOP | CHANF_VIRTUAL | CHANF_EVICTED))
else if (schan->ChanFlags & (CHANF_LOOP | CHANF_EVICTED))
{
evicted = true;
}

View file

@ -174,8 +174,7 @@ struct FSoundChan : public FISoundChannel
uint8_t SourceType;
float LimitRange;
const void *Source;
float Point[3]; // Sound is not attached to any source, can also be used as auxiliary storage for sounds with a real source.
int UserData[2]; // storage for user-specific data (Shadow Warrior's intermittent sounds use this as a counter.
float Point[3]; // Sound is not attached to any source.
};
@ -253,6 +252,7 @@ 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);
@ -272,7 +272,6 @@ protected:
public:
virtual ~SoundEngine() = default;
void EvictChannel(FSoundChan *chan);
void EvictAllChannels();
void StopChannel(FSoundChan* chan);
@ -292,7 +291,6 @@ public:
void SetVolume(FSoundChan* chan, float vol);
FSoundChan* GetChannel(void* syschan);
void RestartChannel(FSoundChan* chan);
void RestoreEvictedChannels();
void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel);

View file

@ -2087,6 +2087,7 @@ short SoundDist(int x, int y, int z, int basedist);
short SoundAngle(int x, int y);
//void PlaySound(int num, short angle, short vol);
int _PlaySound(int num, SPRITEp sprite, PLAYERp player, vec3_t *pos, Voc3D_Flags flags, int channel);
void InitAmbient(int num, SPRITEp sprite);
inline void PlaySound(int num, SPRITEp sprite, Voc3D_Flags flags, int channel = 8)
{
_PlaySound(num, sprite, nullptr, nullptr, flags, channel);

View file

@ -251,8 +251,7 @@ JS_SpriteSetup(void)
PlaySound(DIGI_WATERFLOW1, sp, v3df_follow|v3df_dontpan|v3df_doppler);
break;
case 460: // Wind Chimes
PlaySound(79, sp, v3df_ambient | v3df_init
| v3df_doppler | v3df_follow);
InitAmbient(79, sp);
break;
}

View file

@ -1,6 +1,7 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
Copyright (C) 2019 Christoph Oelckers
This file is part of Shadow Warrior version 1.2
@ -56,11 +57,8 @@ BEGIN_SW_NS
enum EChanExFlags
{
CHANEXF_AMBIENT = 0x40000000,
CHANEXF_NODOPPLER = 0x20000000,
CHANEXF_DONTPAN = 0x10000000,
CHANEXF_INTERMIT = 0x08000000,
CHANEXF_DONTPAN = 0x40000000,
};
// Parentally locked sounds list
@ -98,69 +96,18 @@ AMB_INFO ambarray[] =
#define MAX_AMBIENT_SOUNDS 82
class SWSoundEngine : 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, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) override;
TArray<uint8_t> ReadSound(int lumpnum);
public:
SWSoundEngine()
{
S_Rolloff.RolloffType = ROLLOFF_Doom;
S_Rolloff.MinDistance = 0; // These are the default values, SW uses a few different rolloff settings.
S_Rolloff.MaxDistance = 1187;
}
};
//==========================================================================
//
//
//
//
//==========================================================================
TArray<uint8_t> SWSoundEngine::ReadSound(int lumpnum)
float S_ConvertPitch(int lpitch)
{
auto wlump = fileSystem.OpenFileReader(lumpnum);
return wlump.Read();
return pow(2, lpitch / 1200.);
}
//==========================================================================
//
//
//
//==========================================================================
void InitFX(void)
{
if (soundEngine) return; // just in case.
soundEngine = new SWSoundEngine;
auto &S_sfx = soundEngine->GetSounds();
S_sfx.Resize(countof(voc));
for (auto& sfx : S_sfx) { sfx.Clear(); sfx.lumpnum = sfx_empty; }
for (size_t i = 1; i < countof(voc); i++)
{
auto& entry = voc[i];
auto lump = fileSystem.FindFile(entry.name);
if (lump > 0)
{
auto& newsfx = S_sfx[i];
newsfx.name = entry.name;
newsfx.lumpnum = lump;
newsfx.NearLimit = 6;
newsfx.bTentative = false;
}
}
soundEngine->HashSounds();
for (auto& sfx : S_sfx)
{
soundEngine->CacheSound(&sfx);
}
}
//==========================================================================
//
// Sound Distance Calculation
@ -251,10 +198,132 @@ FRolloffInfo GetRolloff(int basedist)
//==========================================================================
//
//
// Ambient sounds
//
//
//==========================================================================
int RandomizeAmbientSpecials(int handle)
struct AmbientSound
{
SPRITEp sp;
FSoundChan* sndChan;
int ambIndex;
int vocIndex;
int ChanFlags;
int maxIndex;
int curIndex;
bool intermit;
};
static TArray<AmbientSound*> ambients;
//==========================================================================
//
//
//
//==========================================================================
void StopAmbientSound(void)
{
for (auto& amb : ambients)
{
if (amb->sndChan)
{
soundEngine->StopChannel(amb->sndChan);
}
}
ambients.Clear();
}
//==========================================================================
//
// Play a sound
//
//==========================================================================
void InitAmbient(int num, SPRITEp sp)
{
VOC_INFOp vp;
int pitch = 0;
short angle, sound_dist;
int tx, ty, tz;
uint8_t priority;
int maxtics = 0;
if (!snd_ambience) return;
// Ambient sounds need special treatment
if (num < 0 || num > MAX_AMBIENT_SOUNDS)
{
sprintf(ds, "Invalid or out of range ambient sound number %d\n", num);
PutStringInfo(Player + screenpeek, ds);
return;
}
auto vnum = ambarray[num].diginame;
if (!soundEngine->isValidSoundId(vnum))
{
return; // linked sound does not exist.
}
auto amb = new AmbientSound;
amb->sp = sp;
amb->ambIndex = num;
amb->vocIndex = vnum;
amb->sndChan = nullptr;
amb->ChanFlags = 0;
if (ambarray[num].ambient_flags & v3df_dontpan) amb->ChanFlags |= EChanFlags::FromInt(CHANEXF_DONTPAN);
if (voc[vnum].voc_flags & vf_loop) amb->ChanFlags |= CHANF_LOOP;
amb->maxIndex = ambarray[num].maxtics * 8;
amb->curIndex = 0;
amb->intermit = !!(ambarray[num].ambient_flags & v3df_intermit);
ambients.Push(amb);
}
//==========================================================================
//
//
//
//==========================================================================
void StartAmbientSound(void)
{
short i, nexti;
extern SWBOOL InMenuLevel;
if (InMenuLevel || !SoundEnabled()) return; // Don't restart ambience if no level is active! Will crash game.
TRAVERSE_SPRITE_STAT(headspritestat[STAT_AMBIENT], i, nexti)
{
SPRITEp sp = &sprite[i];
InitAmbient(sp->lotag, sp);
}
}
//==========================================================================
//
//
//
//==========================================================================
static void RestartAmbient(AmbientSound* amb)
{
auto& vp = voc[amb->vocIndex];
auto rolloff = GetRolloff(vp.voc_distance);
int pitch = 0;
if (vp.pitch_hi <= vp.pitch_lo) pitch = vp.pitch_lo;
else pitch = vp.pitch_lo + (STD_RANDOM_RANGE(vp.pitch_hi - vp.pitch_lo));
soundEngine->StartSound(SOURCE_Ambient, amb, nullptr, CHAN_BODY, EChanFlags::FromInt(amb->ChanFlags), amb->vocIndex, 1.f, ATTN_NORM, &rolloff, S_ConvertPitch(pitch));
}
//==========================================================================
//
//
//
//==========================================================================
static int RandomizeAmbientSpecials(int handle)
{
#define MAXRNDAMB 12
int ambrand[] =
@ -280,29 +349,155 @@ int RandomizeAmbientSpecials(int handle)
//==========================================================================
void DoTimedSound(FSoundChan *chan)
static void DoTimedSound(AmbientSound* amb)
{
chan->UserData[1] += synctics; // This got called 5x a second, incrementing by 3 each time, meaning that 15 units are one second. Now it gets called 40x a second.
if (chan->UserData[1] >= chan->UserData[0] * 8) // account for the 8x higher update frequency.
amb->curIndex += synctics;
if (amb->curIndex >= amb->maxIndex)
{
if (chan->ChanFlags & CHANF_EVICTED)
if (amb->sndChan == nullptr)
{
// Check for special case ambient sounds. Since the sound is stopped and doesn't occupy a real channel at this time we can just swap out the sound ID before restarting it.
int ambid = RandomizeAmbientSpecials(chan->SoundID);
int ambid = RandomizeAmbientSpecials(amb->vocIndex);
if (ambid != -1)
{
chan->SoundID = FSoundID(ambarray[ambid].maxtics);
chan->UserData[0] = STD_RANDOM_RANGE(ambarray[ambid].maxtics);
amb->vocIndex = ambid;
amb->maxIndex = STD_RANDOM_RANGE(ambarray[ambid].maxtics);
}
soundEngine->RestartChannel(chan);
RestartAmbient(amb);
}
chan->UserData[1] = 0;
}
}
//==========================================================================
//
//
//
//==========================================================================
static void UpdateAmbients()
{
for (auto& amb : ambients)
{
auto sp = amb->sp;
auto sdist = SoundDist(sp->pos.x, sp->pos.y, sp->pos.z, voc[amb->vocIndex].voc_distance);
if (sdist < 255 && amb->vocIndex == DIGI_WHIPME)
{
PLAYERp pp = Player + screenpeek;
if (!FAFcansee(sp->pos.x, sp->pos.y, sp->pos.z, sp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum))
{
sdist = 255;
}
}
if (sdist < 255)
{
if (amb->intermit) DoTimedSound(amb);
else RestartAmbient(amb);
}
else
{
if (amb->sndChan)
{
soundEngine->StopChannel(amb->sndChan);
amb->sndChan = nullptr;
}
}
}
}
//==========================================================================
//
// end of ambient sounds
//
//==========================================================================
//==========================================================================
//
//
//
//==========================================================================
class SWSoundEngine : 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, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) override;
TArray<uint8_t> ReadSound(int lumpnum) override;
void ChannelEnded(FISoundChannel* chan) override;
public:
SWSoundEngine()
{
S_Rolloff.RolloffType = ROLLOFF_Doom;
S_Rolloff.MinDistance = 0; // These are the default values, SW uses a few different rolloff settings.
S_Rolloff.MaxDistance = 1187;
}
};
//==========================================================================
//
//
//
//==========================================================================
TArray<uint8_t> SWSoundEngine::ReadSound(int lumpnum)
{
auto wlump = fileSystem.OpenFileReader(lumpnum);
return wlump.Read();
}
void SWSoundEngine::ChannelEnded(FISoundChannel* chan)
{
// if this channel belongs to an ambient sound we have to delete the reference to it.
for (auto amb : ambients)
{
if (amb->sndChan == chan)
{
amb->sndChan = nullptr;
}
}
SoundEngine::ChannelEnded(chan);
}
//==========================================================================
//
//
//
//==========================================================================
void InitFX(void)
{
if (soundEngine) return; // just in case.
soundEngine = new SWSoundEngine;
auto &S_sfx = soundEngine->GetSounds();
S_sfx.Resize(countof(voc));
for (auto& sfx : S_sfx) { sfx.Clear(); sfx.lumpnum = sfx_empty; }
for (size_t i = 1; i < countof(voc); i++)
{
auto& entry = voc[i];
auto lump = fileSystem.FindFile(entry.name);
if (lump > 0)
{
auto& newsfx = S_sfx[i];
newsfx.name = entry.name;
newsfx.lumpnum = lump;
newsfx.NearLimit = 6;
newsfx.bTentative = false;
}
}
soundEngine->HashSounds();
for (auto& sfx : S_sfx)
{
soundEngine->CacheSound(&sfx);
}
}
//==========================================================================
//
@ -316,8 +511,8 @@ void SWSoundEngine::CalcPosVel(int type, const void* source, const float pt[3],
{
PLAYERp pp = Player + screenpeek;
FVector3 campos = GetSoundPos((vec3_t*)pp);
vec3_t *vpos = nullptr;
//S_GetCamera(&campos, nullptr, &camsect);
if (vel) vel->Zero();
if (type == SOURCE_Unattached)
@ -328,49 +523,50 @@ void SWSoundEngine::CalcPosVel(int type, const void* source, const float pt[3],
}
else if (type == SOURCE_Actor || type == SOURCE_Player)
{
vpos = type == SOURCE_Actor ? &((SPRITEp)source)->pos : (vec3_t*)&((PLAYERp)source)->posx;
FVector3 npos = GetSoundPos(vpos);
*pos = npos;
if (!(chanflags & CHANEXF_NODOPPLER) && vel)
{
// Hack alert. Velocity may only be set if a) the sound is already running and b) an actual sound channel is modified.
// It remains to be seen if this is actually workable. I have my doubts. The velocity should be taken from a stable source.
if (chan && !(chanflags & (CHANF_JUSTSTARTED | CHANF_EVICTED)))
{
*vel = (npos - FVector3(pt[0], pt[1], pt[2])) * 40; // SW ticks 40 times a second.
chan->Point[0] = npos.X;
chan->Point[1] = npos.Y;
chan->Point[2] = npos.Z;
}
}
}
else if (type == SOURCE_Ambient)
{
auto sp = ((AmbientSound*)source)->sp;
vec3_t* vpos = type == SOURCE_Actor ? &((SPRITEp)source)->pos : (vec3_t*)&((PLAYERp)source)->posx;
FVector3 npos = GetSoundPos(vpos);
// Can the ambient sound see the player? If not, tone it down some.
if ((chanflags & CHANF_LOOP) && (chanflags & CHANEXF_AMBIENT) && type == SOURCE_Actor)
if ((chanflags & CHANF_LOOP))
{
auto sp = (SPRITEp)source;
//MONO_PRINT("PlaySound:Checking sound cansee");
if (!FAFcansee(vpos->x, vpos->y, vpos->z, sp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum))
{
//MONO_PRINT("PlaySound:Reducing sound distance");
auto distvec = npos - campos;
// Special Cases
npos = campos + distvec * 1.75f; // Play more quietly
chanflags |= CHANEXF_NODOPPLER; // Ambient sounds should be stationary, but let's make sure that no doppler gets applied after messing up the position.
}
}
if (chanflags & CHANEXF_DONTPAN)
{
// For unpanned sounds the volume must be set directly and the position taken from the listener.
*pos = campos;
auto sdist = SoundDist(vpos->x, vpos->y, vpos->z, voc[chanSound].voc_distance);
if (chan) SetVolume(chan, (255 - sdist) * (1 / 255.f));
}
else
{
*pos = npos;
// Doppler only makes sense for sounds that are actually positioned in the world (i.e. not in combination with DONTPAN)
if (!(chanflags & CHANEXF_NODOPPLER) && vel)
{
// Hack alert. Velocity may only be set if a) the sound is already running and b) an actual sound channel is modified.
// It remains to be seen if this is actually workable. I have my doubts. The velocity should be taken from a stable source.
if (chan && !(chanflags & (CHANF_JUSTSTARTED|CHANF_EVICTED)))
{
*vel = (npos - FVector3(pt[0], pt[1], pt[2])) * 40; // SW ticks 40 times a second.
chan->Point[0] = npos.X;
chan->Point[1] = npos.Y;
chan->Point[2] = npos.Z;
}
}
}
*pos = npos;
}
if (chanflags & CHANEXF_DONTPAN)
{
// For unpanned sounds the volume must be set directly and the position taken from the listener.
*pos = campos;
auto sdist = SoundDist(vpos->x, vpos->y, vpos->z, voc[chanSound].voc_distance);
if (chan) SetVolume(chan, (255 - sdist) * (1 / 255.f));
}
if ((chanflags & CHANF_LISTENERZ) && campos != nullptr && type != SOURCE_None)
{
pos->Y = campos.Y;
@ -402,172 +598,77 @@ void DoUpdateSounds(void)
listener.ListenerObject = pp;
soundEngine->SetListener(listener);
soundEngine->EnumerateChannels([](FSoundChan* chan)
{
if (chan->ChanFlags & EChanFlags::FromInt(CHANEXF_INTERMIT))
{
DoTimedSound(chan);
}
if (chan->SoundID == DIGI_WHIPME && chan->SourceType == SOURCE_Actor)
{
auto sp = (SPRITEp)chan->Source;
PLAYERp pp = Player + screenpeek;
if (!FAFcansee(sp->pos.x, sp->pos.y, sp->pos.z, sp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum))
{
soundEngine->EvictChannel(chan);
}
else if (chan->ChanFlags & CHANF_EVICTED)
{
soundEngine->RestartChannel(chan);
}
}
return false;
});
UpdateAmbients();
soundEngine->UpdateSounds((int)totalclock);
}
//==========================================================================
//
//
//
//==========================================================================
float S_ConvertPitch(int lpitch)
{
return pow(2, lpitch / 1200.);
}
//==========================================================================
//
// Play a sound
//
//==========================================================================
int _PlaySound(int num, SPRITEp sp, PLAYERp pp, vec3_t *pos, Voc3D_Flags flags, int channel)
int _PlaySound(int num, SPRITEp sp, PLAYERp pp, vec3_t* pos, Voc3D_Flags flags, int channel)
{
VOC_INFOp vp;
int pitch = 0;
short angle, sound_dist;
int tx, ty, tz;
uint8_t priority;
int maxtics = 0;
EChanFlags cflags = channel == 8 ?CHANF_OVERLAP : CHANF_NONE; // for the default channel we do not want to have sounds stopping each other.
if (Prediction || !SoundEnabled() || !soundEngine->isValidSoundId(num))
return -1;
// Weed out parental lock sounds if PLock is active
if (adult_lockout || Global_PLock)
{
unsigned i;
for (i=0; i<sizeof(PLocked_Sounds); i++)
for (unsigned i = 0; i < sizeof(PLocked_Sounds); i++)
{
if (num == PLocked_Sounds[i])
return -1;
}
}
if (Prediction || !SoundEnabled())
return -1;
if (TEST(flags,v3df_ambient) && !TEST(flags,v3df_nolookup)) // Look for invalid ambient numbers
auto vp = &voc[num];
int sourcetype = SOURCE_None;
EChanFlags cflags = channel == 8 ? CHANF_OVERLAP : CHANF_NONE; // for the default channel we do not want to have sounds stopping each other.
void* source = nullptr;
// If the sound is not supposd to be positioned, it may not be linked to the launching actor.
if (!(flags & v3df_follow))
{
// Ambient sounds need special treatment
if (!snd_ambience) return -1;
if (num < 0 || num > MAX_AMBIENT_SOUNDS)
if (sp && !pos)
{
sprintf(ds,"Invalid or out of range ambient sound number %d\n",num);
PutStringInfo(Player+screenpeek, ds);
return -1;
pos = &sp->pos;
sp = nullptr;
}
maxtics = STD_RANDOM_RANGE(ambarray[num].maxtics);
// If the ambient flag is set, do a name conversion to point to actual
// digital sound entry.
flags |= ambarray[num].ambient_flags; // Add to flags if any
num = ambarray[num].diginame;
cflags |= EChanFlags::FromInt(CHANEXF_AMBIENT); // flag the sound as being an ambient sound.
}
//else
{
if (!soundEngine->isValidSoundId(num))
else if (pp && !pos)
{
return -1;
}
vp = &voc[num];
int sourcetype = SOURCE_None;
void* source = nullptr;
// If the sound is not supposd to be positioned, it may not be linked to the launching actor.
if (!(flags & v3df_follow))
{
if (sp && !pos)
{
pos = &sp->pos;
sp = nullptr;
}
else if (pp && !pos)
{
pos = (vec3_t*)&pp->posx;
pp = nullptr;
}
}
if (pos != nullptr)
{
sourcetype = SOURCE_Unattached;
}
else if (sp != nullptr)
{
source = sp;
sourcetype = SOURCE_Actor;
}
else if (pp != nullptr)
{
source = pp;
sourcetype = SOURCE_Player;
}
// Otherwise it's an unpositioned sound.
if (!(flags & v3df_doppler))
{
cflags |= EChanFlags::FromInt(CHANEXF_NODOPPLER); // this must ensure that CalcPosVel always zeros the velocity.
}
if (flags & v3df_dontpan)
{
cflags |= EChanFlags::FromInt(CHANEXF_DONTPAN); // beware of hackery to emulate this.
}
/*
if (flags & v3df_init)
{
// this only gets used for starting looped sounds that are outside hearing range - something the sound engine handles transparently.
}
*/
if (vp->voc_flags & vf_loop)
{
cflags |= CHANF_LOOP; // with the new sound engine these can just be started and don't have to be stopped ever.
}
if (vp->pitch_hi <= vp->pitch_lo)
pitch = vp->pitch_lo;
else if (vp->pitch_hi != vp->pitch_lo)
pitch = vp->pitch_lo + (STD_RANDOM_RANGE(vp->pitch_hi - vp->pitch_lo));
float fpitch = S_ConvertPitch(pitch);
auto rolloff = GetRolloff(vp->voc_distance);
FVector3 spos = pos ? GetSoundPos(pos) : FVector3(0, 0, 0);
auto chan = soundEngine->StartSound(sourcetype, source, &spos, CHAN_BODY, cflags, num, 1.f, ATTN_NORM, &rolloff, fpitch);
if (chan)
{
if (flags & v3df_intermit)
{
chan->ChanFlags |= CHANF_VIRTUAL | EChanFlags::FromInt(CHANEXF_INTERMIT); // for intermittent sounds this must be set after starting the sound so that it actually plays.
}
chan->UserData[0] = maxtics; // counter for intermittent delay.
chan->UserData[1] = 0;
pos = (vec3_t*)&pp->posx;
pp = nullptr;
}
}
if (pos != nullptr)
{
sourcetype = SOURCE_Unattached;
}
else if (sp != nullptr)
{
source = sp;
sourcetype = SOURCE_Actor;
}
else if (pp != nullptr)
{
source = pp;
sourcetype = SOURCE_Player;
}
// Otherwise it's an unpositioned sound.
if (flags & v3df_doppler) cflags |= EChanFlags::FromInt(CHANEXF_NODOPPLER); // this must ensure that CalcPosVel always zeros the velocity.
if (flags & v3df_dontpan) cflags |= EChanFlags::FromInt(CHANEXF_DONTPAN); // beware of hackery to emulate this.
if (vp->voc_flags & vf_loop) cflags |= CHANF_LOOP; // with the new sound engine these can just be started and don't have to be stopped ever.
int pitch = 0;
if (vp->pitch_hi <= vp->pitch_lo) pitch = vp->pitch_lo;
else if (vp->pitch_hi != vp->pitch_lo) pitch = vp->pitch_lo + (STD_RANDOM_RANGE(vp->pitch_hi - vp->pitch_lo));
auto rolloff = GetRolloff(vp->voc_distance);
FVector3 spos = pos ? GetSoundPos(pos) : FVector3(0, 0, 0);
soundEngine->StartSound(sourcetype, source, &spos, CHAN_BODY, cflags, num, 1.f, ATTN_NORM, &rolloff, S_ConvertPitch(pitch));
return 1;
}
@ -633,46 +734,6 @@ void DeleteNoFollowSoundOwner(short spritenum)
soundEngine->StopSound(SOURCE_Actor, sp, -1); // all non-follow sounds are SOURCE_Unattached
}
//==========================================================================
//
//
//
//==========================================================================
void StopAmbientSound(void)
{
soundEngine->EnumerateChannels([](FSoundChan* chan)
{
if (chan->ChanFlags & EChanFlags::FromInt(CHANEXF_AMBIENT))
{
soundEngine->StopChannel(chan);
}
return false;
});
}
//==========================================================================
//
//
//
//==========================================================================
void StartAmbientSound(void)
{
short i, nexti;
extern SWBOOL InMenuLevel;
if (InMenuLevel) return; // Don't restart ambience if no level is active! Will crash game.
TRAVERSE_SPRITE_STAT(headspritestat[STAT_AMBIENT], i, nexti)
{
SPRITEp sp = &sprite[i];
PlaySound(sp->lotag, sp, v3df_ambient | v3df_init | v3df_doppler | v3df_follow);
}
}
//==========================================================================
//
// Terminate the sounds list