- SW sound refactoring complete, not tested yet.

This commit is contained in:
Christoph Oelckers 2019-12-18 22:13:19 +01:00
parent a087d566ee
commit 591ace496f
5 changed files with 210 additions and 347 deletions

View file

@ -640,7 +640,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source,
void SoundEngine::RestartChannel(FSoundChan *chan)
{
assert(chan->ChanFlags & CHANF_EVICTED);
if (!(chan->ChanFlags & CHANF_EVICTED)) return;
FSoundChan *ochan;
sfxinfo_t *sfx = &S_sfx[chan->SoundID];
@ -1061,6 +1061,16 @@ void SoundEngine::ChangeSoundVolume(int sourcetype, const void *source, int chan
return;
}
void SoundEngine::SetVolume(FSoundChan* chan, float volume)
{
if (volume < 0.0) volume = 0.0;
else if (volume > 1.0) volume = 1.0;
assert(chan != nullptr);
GSnd->ChannelVolume(chan, volume);
chan->Volume = volume;
}
//==========================================================================
//
// S_ChangeSoundPitch
@ -1165,6 +1175,31 @@ 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
@ -1181,21 +1216,7 @@ void SoundEngine::EvictAllChannels()
for (chan = Channels; chan != NULL; chan = next)
{
next = chan->NextChan;
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);
}
EvictChannel(chan);
}
}
@ -1338,7 +1359,7 @@ 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, it must have
// it wasn't evicted. Otherwise, if it's looping or declared virtual, 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)

View file

@ -174,8 +174,8 @@ struct FSoundChan : public FISoundChannel
uint8_t SourceType;
float LimitRange;
const void *Source;
float Point[3]; // Sound is not attached to any source.
int UserData; // One word of data for user-specific data
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.
};
@ -217,7 +217,7 @@ enum // This cannot be remain as this, but for now it has to suffice.
SOURCE_Actor, // Sound is coming from an actor.
SOURCE_Ambient, // Sound is coming from a blood ambient definition.
SOURCE_Unattached, // Sound is not attached to any particular emitter.
SOURCE_Player, // SW player sound (player in SW is not connected to a sprite so needs to be special.)
SOURCE_Player, // SW player sound (player in SW maintains its own position separately from the sprite so needs to be special.)
};
@ -253,7 +253,6 @@ 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);
@ -273,6 +272,7 @@ protected:
public:
virtual ~SoundEngine() = default;
void EvictChannel(FSoundChan *chan);
void EvictAllChannels();
void StopChannel(FSoundChan* chan);
@ -289,8 +289,10 @@ public:
void StopAllChannels(void);
void SetPitch(FSoundChan* chan, float dpitch);
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

@ -213,8 +213,6 @@ JS_SpriteSetup(void)
else if (tag == AMBIENT_SOUND)
{
change_sprite_stat(SpriteNum, STAT_AMBIENT);
// PlaySound(sp->lotag, sp, v3df_ambient
// | v3df_init | v3df_doppler);
}
else if (tag == TAG_ECHO_SOUND)
{

View file

@ -101,6 +101,34 @@ 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)
{
auto wlump = fileSystem.OpenFileReader(lumpnum);
return wlump.Read();
}
//==========================================================================
//
//
@ -109,6 +137,9 @@ AMB_INFO ambarray[] =
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; }
@ -219,23 +250,11 @@ FRolloffInfo GetRolloff(int basedist)
}
extern USERp User[MAXSPRITES];
void DumpSounds(void);
// Global vars used by ambient sounds to set spritenum of ambient sounds for later lookups in
// the sprite array so FAFcansee can know the sound sprite's current sector location
SWBOOL Use_SoundSpriteNum = FALSE;
int16_t SoundSpriteNum = -1; // Always set this back to -1 for proper validity checking!
////////////////////////////////////////////////////////////////////////////
// Play a sound
////////////////////////////////////////////////////////////////////////////
//==========================================================================
//
//
//
//==========================================================================
int RandomizeAmbientSpecials(int handle)
{
@ -244,339 +263,161 @@ int RandomizeAmbientSpecials(int handle)
{
56,57,58,59,60,61,62,63,64,65,66,67
};
short i;
int i;
// If ambient sound is found in the array, randomly pick a new sound
for (i = 0; i < MAXRNDAMB; i++)
{
if (handle == ambrand[i])
if (handle == ambarray[ambrand[i]].diginame)
return ambrand[STD_RANDOM_RANGE(MAXRNDAMB - 1)];
}
return handle; // Give back the sound, no new one was found
return -1;
}
//==========================================================================
//
//
//
//==========================================================================
void
DoTimedSound(VOC3D_INFOp p)
void DoTimedSound(FSoundChan *chan)
{
p->tics += synctics;
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 (p->tics >= p->maxtics)
if (chan->UserData[1] >= chan->UserData[0] * 8) // account for the 8x higher update frequency.
{
if (!FX_SoundValidAndActive(p->handle))
if (chan->ChanFlags & CHANF_EVICTED)
{
// Check for special case ambient sounds
p->num = RandomizeAmbientSpecials(p->num);
// Sound was bumped from active sounds list, try to play again.
// Don't bother if voices are already maxed out.
if (FX_SoundsPlaying() < snd_numvoices)
// 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);
if (ambid != -1)
{
if (p->flags & v3df_follow)
{
//PlaySound(p->num, p->x, p->y, p->z, p->flags); todo
p->deleted = TRUE; // Mark old sound for deletion
}
else
{
//PlaySound(p->num, &p->fx, &p->fy, &p->fz, p->flags); todo
p->deleted = TRUE; // Mark old sound for deletion
}
chan->SoundID = FSoundID(ambarray[ambid].maxtics);
chan->UserData[0] = STD_RANDOM_RANGE(ambarray[ambid].maxtics);
}
soundEngine->RestartChannel(chan);
}
p->tics = 0;
chan->UserData[1] = 0;
}
}
///////////////////////////////////////////////
//==========================================================================
//
//
//
//==========================================================================
void SWSoundEngine::CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan)
{
if (pos != nullptr)
{
PLAYERp pp = Player + screenpeek;
FVector3 campos = GetSoundPos((vec3_t*)pp);
//S_GetCamera(&campos, nullptr, &camsect);
if (vel) vel->Zero();
if (type == SOURCE_Unattached)
{
pos->X = pt[0];
pos->Y = pt[1];
pos->Z = pt[2];
}
else if (type == SOURCE_Actor || type == SOURCE_Player)
{
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)
{
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);
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;
}
}
}
}
if ((chanflags & CHANF_LISTENERZ) && campos != nullptr && type != SOURCE_None)
{
pos->Y = campos.Y;
}
}
}
//==========================================================================
//
// Main function to update 3D sound array
///////////////////////////////////////////////
typedef struct
//
//==========================================================================
void DoUpdateSounds(void)
{
VOC3D_INFOp p;
short dist;
uint8_t priority;
} TVOC_INFO, * TVOC_INFOp;
void
DoUpdateSounds(void)
{
VOC3D_INFOp p;
SWBOOL looping;
int pitch = 0, pitchmax;
int delta;
short dist, angle;
SWBOOL deletesound = FALSE;
TVOC_INFO TmpVocArray[32];
int i;
// Zero out the temporary array
// Zero out the temporary array
//memset(&TmpVocArray[0],0,sizeof(TmpVocArray));
for (i = 0; i < 32; i++)
{
TmpVocArray[i].p = NULL;
TmpVocArray[i].dist = 0;
TmpVocArray[i].priority = 0;
}
p = nullptr;// voc3dstart;
while (p)
{
ASSERT(p->num >= 0 && p->num < DIGI_MAX);
looping = p->vp->voc_flags & vf_loop;
// //DSPRINTF(ds,"sound %d FX_SoundActive = %d\n,",p->num,FX_SoundActive(p->handle));
// MONO_PRINT(ds);
// If sprite owner is dead, kill this sound as long as it isn't ambient
if (looping && p->owner == -1 && !TEST(p->flags, v3df_ambient))
soundEngine->EnumerateChannels([](FSoundChan* chan)
{
SET(p->flags, v3df_kill);
}
// Is the sound slated for death? Kill it, otherwise play it.
if (p->flags & v3df_kill)
{
if (FX_SoundValidAndActive(p->handle))
if (chan->ChanFlags & EChanFlags::FromInt(CHANEXF_INTERMIT))
{
FX_StopSound(p->handle); // Make sure to stop active sounds
p->handle = 0;
DoTimedSound(chan);
}
//DSPRINTF(ds,"%d had v3df_kill.\n",p->num);
//MONO_PRINT(ds);
p->deleted = TRUE;
}
else
{
if (!FX_SoundValidAndActive(p->handle) && !looping)
if (chan->SoundID == DIGI_WHIPME && chan->SourceType == SOURCE_Actor)
{
if (p->flags & v3df_intermit)
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))
{
DoTimedSound(p);
soundEngine->EvictChannel(chan);
}
else
//if(p->owner == -1 && !TEST(p->flags,v3df_ambient))
else if (chan->ChanFlags & CHANF_EVICTED)
{
//DSPRINTF(ds,"%d is now inactive.\n",p->num);
//MONO_PRINT(ds);
p->deleted = TRUE;
soundEngine->RestartChannel(chan);
}
}
else if (FX_SoundValidAndActive(p->handle))
{
if (p->flags & v3df_follow)
{
dist = SoundDist(*p->x, *p->y, *p->z, p->vp->voc_distance);
angle = SoundAngle(*p->x, *p->y);
}
else
{
if (p->fx == 0 && p->fy == 0 && p->fz == 0)
dist = 0;
else
dist = SoundDist(p->fx, p->fy, p->fz, p->vp->voc_distance);
angle = SoundAngle(p->fx, p->fy);
}
// Can the ambient sound see the player? If not, tone it down some.
if ((p->vp->voc_flags & vf_loop) && p->owner != -1)
{
PLAYERp pp = Player + screenpeek;
SPRITEp sp = &sprite[p->owner];
//MONO_PRINT("Checking sound cansee");
if (!FAFcansee(sp->x, sp->y, sp->z, sp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum))
{
//MONO_PRINT("Reducing sound distance");
dist += ((dist / 2) + (dist / 4)); // Play more quietly
if (dist > 255) dist = 255;
// Special cases
if (p->num == 76 && TEST(p->flags, v3df_ambient))
{
dist = 255; // Cut off whipping sound, it's secret
}
}
}
if (dist >= 255 && p->vp->voc_distance == DIST_NORMAL)
{
FX_StopSound(p->handle); // Make sure to stop active
p->handle = 0;
// sounds
}
else
{
// Handle Panning Left and Right
if (!(p->flags & v3df_dontpan))
FX_Pan3D(p->handle, angle, dist);
else
FX_Pan3D(p->handle, 0, dist);
// Handle Doppler Effects
#define DOPPLERMAX 400
if (!(p->flags & v3df_doppler) && FX_SoundActive(p->handle))
{
pitch -= (dist - p->doplr_delta);
if (p->vp->pitch_lo != 0 && p->vp->pitch_hi != 0)
{
if (abs(p->vp->pitch_lo) > abs(p->vp->pitch_hi))
pitchmax = abs(p->vp->pitch_lo);
else
pitchmax = abs(p->vp->pitch_hi);
}
else
pitchmax = DOPPLERMAX;
if (pitch > pitchmax)
pitch = pitchmax;
if (pitch < -pitchmax)
pitch = -pitchmax;
p->doplr_delta = dist; // Save new distance to
// struct
FX_SetPitch(p->handle, pitch);
}
}
}
else if (!FX_SoundValidAndActive(p->handle) && looping)
{
if (p->flags & v3df_follow)
{
dist = SoundDist(*p->x, *p->y, *p->z, p->vp->voc_distance);
angle = SoundAngle(*p->x, *p->y);
}
else
{
dist = SoundDist(p->fx, p->fy, p->fz, p->vp->voc_distance);
angle = SoundAngle(p->fx, p->fy);
}
// Sound was bumped from active sounds list, try to play
// again.
// Don't bother if voices are already maxed out.
// Sort looping vocs in order of priority and distance
//if (FX_SoundsPlaying() < snd_numvoices && dist <= 255)
if (dist <= 255)
{
for (i = 0; i < min((int)SIZ(TmpVocArray), *snd_numvoices); i++)
{
if (p->priority >= TmpVocArray[i].priority)
{
if (!TmpVocArray[i].p || dist < TmpVocArray[i].dist)
{
ASSERT(p->num >= 0 && p->num < DIGI_MAX);
TmpVocArray[i].p = p;
TmpVocArray[i].dist = dist;
TmpVocArray[i].priority = p->priority;
break;
}
}
}
}
} // !FX_SoundActive
} // if(p->flags & v3df_kill)
p = p->next;
} // while(p)
// Process all the looping sounds that said they wanted to get back in
// Only update these sounds 5x per second! Woo hoo!, aren't we optimized now?
//if(MoveSkip8==0)
// {
for (i = 0; i < min((int)SIZ(TmpVocArray), *snd_numvoices); i++)
{
int handle;
p = TmpVocArray[i].p;
//if (FX_SoundsPlaying() >= snd_numvoices || !p) break;
if (!p) break;
ASSERT(p->num >= 0 && p->num < DIGI_MAX);
if (p->flags & v3df_follow)
{
if (p->owner == -1)
{
// Terminate the sound without aborting.
continue;
}
Use_SoundSpriteNum = TRUE;
SoundSpriteNum = p->owner;
//handle = PlaySound(p->num, p->x, p->y, p->z, p->flags); todo
//if(handle >= 0 || TEST(p->flags,v3df_ambient)) // After a valid PlaySound, it's ok to use voc3dend
//voc3dend->owner = p->owner; // todo Transfer the owner
p->deleted = TRUE;
Use_SoundSpriteNum = FALSE;
SoundSpriteNum = -1;
//MONO_PRINT("TmpVocArray playing a follow sound");
}
else
{
if (p->owner == -1)
{
// Terminate the sound without aborting.
continue;
}
Use_SoundSpriteNum = TRUE;
SoundSpriteNum = p->owner;
//handle = PlaySound(p->num, &p->fx, &p->fy, &p->fz, p->flags); todo
//if(handle >= 0 || TEST(p->flags,v3df_ambient))
//voc3dend->owner = p->owner; // todo Transfer the owner
p->deleted = TRUE;
Use_SoundSpriteNum = FALSE;
SoundSpriteNum = -1;
}
}
return false;
});
soundEngine->UpdateSounds((int)totalclock);
}
/*
// Can the ambient sound see the player? If not, tone it down some.
if ((vp->voc_flags & vf_loop) && Use_SoundSpriteNum && SoundSpriteNum >= 0)
{
PLAYERp pp = Player+screenpeek;
//MONO_PRINT("PlaySound:Checking sound cansee");
if (!FAFcansee(tx, ty, tz, sp->sectnum,pp->posx, pp->posy, pp->posz, pp->cursectnum))
{
//MONO_PRINT("PlaySound:Reducing sound distance");
sound_dist += ((sound_dist/2)+(sound_dist/4)); // Play more quietly
if (sound_dist > 255) sound_dist = 255;
// Special Cases
if (num == DIGI_WHIPME) sound_dist = 255;
}
}
*/
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//==========================================================================
//
//
//
//==========================================================================
float S_ConvertPitch(int lpitch)
{
@ -592,7 +433,6 @@ float S_ConvertPitch(int lpitch)
int _PlaySound(int num, SPRITEp sp, PLAYERp pp, vec3_t *pos, Voc3D_Flags flags, int channel)
{
VOC_INFOp vp;
VOC3D_INFOp v3p;
int pitch = 0;
short angle, sound_dist;
int tx, ty, tz;
@ -682,10 +522,12 @@ int _PlaySound(int num, SPRITEp sp, PLAYERp pp, vec3_t *pos, Voc3D_Flags flags,
{
cflags |= EChanFlags::FromInt(CHANEXF_DONTPAN); // beware of hackery to emulate this.
}
/*
if (flags & v3df_init)
{
cflags |= CHANF_VIRTUAL; // don't start right away but keep the channel around until explicitly deleted.
// 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.
@ -707,7 +549,8 @@ int _PlaySound(int num, SPRITEp sp, PLAYERp pp, vec3_t *pos, Voc3D_Flags flags,
{
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 = maxtics; // counter for intermittent delay.
chan->UserData[0] = maxtics; // counter for intermittent delay.
chan->UserData[1] = 0;
}
}
@ -766,14 +609,14 @@ void DeleteNoSoundOwner(short spritenum)
//==========================================================================
//
// This is called from KillSprite to kill a follow sound with no valid sprite owner
// Stops and active sound with the follow bit set, even play once sounds.
// Stops any active sound with the follow bit set, even play once sounds.
//
//==========================================================================
void DeleteNoFollowSoundOwner(short spritenum)
{
SPRITEp sp = &sprite[spritenum];
soundEngine->StopSound(SOURCE_Actor, sp, -1);
soundEngine->StopSound(SOURCE_Actor, sp, -1); // all non-follow sounds are SOURCE_Unattached
}
//==========================================================================
@ -803,7 +646,6 @@ void StopAmbientSound(void)
void StartAmbientSound(void)
{
VOC3D_INFOp p;
short i, nexti;
extern SWBOOL InMenuLevel;

View file

@ -58,21 +58,19 @@ enum
v3df_init = 64, // 1 = First pass of sound, don't play it.
// This is mainly used for intermittent sounds
v3df_nolookup = 128, // don't use ambient table lookup
v3df_listenerz = 256 // ignore height when positioning sound (was mostly hacked by providing bad cooordinates to the code before.)
};
typedef int Voc3D_Flags;
struct VOCstruct;
typedef struct VOCstruct VOC_INFO, *VOC_INFOp;
#if 0
struct VOC3Dstruct;
typedef struct VOC3Dstruct VOC3D_INFO, *VOC3D_INFOp;
#endif
struct ambientstruct;
typedef struct ambientstruct AMB_INFO, *AMB_INFOp;
extern VOC3D_INFOp voc3dstart;
extern VOC3D_INFOp voc3dend;
void DoUpdateSounds(void);
void Terminate3DSounds(void);
@ -151,6 +149,7 @@ struct VOCstruct
// JIMSOUND3D(tm) variables section //////////////////////////////////////////
#if 0
struct VOC3Dstruct
{
VOC_INFOp vp; // Pointer to the sound
@ -175,6 +174,7 @@ struct VOC3Dstruct
SWBOOL deleted; // Has sound been marked for deletion?
SWBOOL FX_Ok; // Did this sound play ok?
};
#endif
extern VOC_INFO voc[];