From 92c8e4c1109209df8cb222d45c5a5951c67b350d Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@users.noreply.github.com>
Date: Thu, 19 Dec 2019 01:20:43 +0100
Subject: [PATCH] - maintain ambient sounds in a separate structure.

---
 source/common/sound/backend/i_soundinternal.h |   2 +-
 source/common/sound/s_sound.cpp               |  59 +-
 source/common/sound/s_soundinternal.h         |   6 +-
 source/sw/src/game.h                          |   1 +
 source/sw/src/jsector.cpp                     |   3 +-
 source/sw/src/sounds.cpp                      | 625 ++++++++++--------
 6 files changed, 372 insertions(+), 324 deletions(-)

diff --git a/source/common/sound/backend/i_soundinternal.h b/source/common/sound/backend/i_soundinternal.h
index a2bdf81e2..08dd311a2 100644
--- a/source/common/sound/backend/i_soundinternal.h
+++ b/source/common/sound/backend/i_soundinternal.h
@@ -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.
 };
diff --git a/source/common/sound/s_sound.cpp b/source/common/sound/s_sound.cpp
index ff8f13871..69c455252 100644
--- a/source/common/sound/s_sound.cpp
+++ b/source/common/sound/s_sound.cpp
@@ -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;
 		}
diff --git a/source/common/sound/s_soundinternal.h b/source/common/sound/s_soundinternal.h
index f66d46747..130db61b7 100644
--- a/source/common/sound/s_soundinternal.h
+++ b/source/common/sound/s_soundinternal.h
@@ -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);
 
diff --git a/source/sw/src/game.h b/source/sw/src/game.h
index 2ee384cc3..d2875efa0 100644
--- a/source/sw/src/game.h
+++ b/source/sw/src/game.h
@@ -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);
diff --git a/source/sw/src/jsector.cpp b/source/sw/src/jsector.cpp
index 72b085c5d..e56a64056 100644
--- a/source/sw/src/jsector.cpp
+++ b/source/sw/src/jsector.cpp
@@ -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;
 
         }
diff --git a/source/sw/src/sounds.cpp b/source/sw/src/sounds.cpp
index 15843827e..5fabbdcaf 100644
--- a/source/sw/src/sounds.cpp
+++ b/source/sw/src/sounds.cpp
@@ -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