diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc60d5619..668a5b0ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -879,6 +879,7 @@ set (PCH_SOURCES sound/s_advsound.cpp sound/s_environment.cpp sound/s_sndseq.cpp + sound/s_doomsound.cpp sound/s_sound.cpp sound/s_music.cpp serializer.cpp diff --git a/src/playsim/p_acs.cpp b/src/playsim/p_acs.cpp index eec8e752e..e28e8d72b 100644 --- a/src/playsim/p_acs.cpp +++ b/src/playsim/p_acs.cpp @@ -5953,7 +5953,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) if (args[0] == 0) { - S_ChangeSoundVolume(activator, chan, volume); + S_ChangeActorSoundVolume(activator, chan, volume); } else { @@ -5962,7 +5962,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) while ((spot = it.Next()) != NULL) { - S_ChangeSoundVolume(spot, chan, volume); + S_ChangeActorSoundVolume(spot, chan, volume); } } } diff --git a/src/scripting/vmthunks_actors.cpp b/src/scripting/vmthunks_actors.cpp index e2548489f..7ad73c7e8 100644 --- a/src/scripting/vmthunks_actors.cpp +++ b/src/scripting/vmthunks_actors.cpp @@ -142,21 +142,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_StopSound, NativeStopSound) return 0; } -DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundPitch, S_ChangeSoundPitch) +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundPitch, S_ChangeActorSoundPitch) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(channel); PARAM_FLOAT(pitch); - S_ChangeSoundPitch(self, channel, pitch); + S_ChangeActorSoundPitch(self, channel, pitch); return 0; } -DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundVolume, S_ChangeSoundVolume) +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_SoundVolume, S_ChangeActorSoundVolume) { PARAM_SELF_PROLOGUE(AActor); PARAM_INT(channel); PARAM_FLOAT(volume); - S_ChangeSoundVolume(self, channel, volume); + S_ChangeActorSoundVolume(self, channel, volume); return 0; } diff --git a/src/sound/backend/i_soundinternal.h b/src/sound/backend/i_soundinternal.h index 75874b6c3..ef9e24db3 100644 --- a/src/sound/backend/i_soundinternal.h +++ b/src/sound/backend/i_soundinternal.h @@ -77,6 +77,7 @@ struct SoundListener bool underwater; bool valid; ReverbContainer *Environment; + void* ListenerObject; }; // Default rolloff information. diff --git a/src/sound/s_doomsound.cpp b/src/sound/s_doomsound.cpp new file mode 100644 index 000000000..865ac15bc --- /dev/null +++ b/src/sound/s_doomsound.cpp @@ -0,0 +1,710 @@ +/* +** doomspund.cpp +** +** Game dependent part of the sound engine. +** +**--------------------------------------------------------------------------- +** +** Copyright 1999-2016 Randy Heit +** Copyright 2002-2019 Christoph Oelckers +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#include +#include +#ifdef _WIN32 +#include +#endif + +#include "i_system.h" +#include "i_sound.h" +#include "i_music.h" +#include "s_sound.h" +#include "s_sndseq.h" +#include "s_playlist.h" +#include "c_dispatch.h" +#include "m_random.h" +#include "w_wad.h" +#include "p_local.h" +#include "doomstat.h" +#include "cmdlib.h" +#include "v_video.h" +#include "v_text.h" +#include "a_sharedglobal.h" +#include "gstrings.h" +#include "gi.h" +#include "po_man.h" +#include "serializer.h" +#include "d_player.h" +#include "g_levellocals.h" +#include "vm.h" +#include "g_game.h" +#include "s_music.h" + + +extern SoundListener listener; +extern int RestartEvictionsAt; // do not restart evicted channels before this time +extern bool SoundPaused; // whether sound is paused +void CalcPosVel(FSoundChan* chan, FVector3* pos, FVector3* vel); + +//========================================================================== +// +// S_Sound - Unpositioned version +// +//========================================================================== + +void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float pitch) +{ + S_StartSound(SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); +} + +void S_Sound(int channel, FSoundID sound_id, float volume, float attenuation) +{ + S_StartSound (SOURCE_None, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, 0.f); +} + +DEFINE_ACTION_FUNCTION(DObject, S_Sound) +{ + PARAM_PROLOGUE; + PARAM_SOUND(id); + PARAM_INT(channel); + PARAM_FLOAT(volume); + PARAM_FLOAT(attn); + PARAM_FLOAT(pitch); + S_SoundPitch(channel, id, static_cast(volume), static_cast(attn), static_cast(pitch)); + return 0; +} + +//========================================================================== +// +// S_Sound - An actor is source +// +//========================================================================== +void S_SoundPitchActor(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation, float pitch) +{ + if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) + return; + + if (S_sfx[sound_id].bPlayerReserve) + { + sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); + if (sound_id <= 0) return; + } + + S_StartSound (SOURCE_Actor, ent, nullptr, channel, sound_id, volume, attenuation, 0, pitch); +} + +void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation) +{ + S_SoundPitchActor(ent, channel, sound_id, volume, attenuation, 0.f); +} + +//========================================================================== +// +// S_SoundMinMaxDist - An actor is source +// +// Attenuation is specified as min and max distances, rather than a scalar. +// +//========================================================================== + +void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume, float mindist, float maxdist) +{ + if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) + return; + + if (S_sfx[sound_id].bPlayerReserve) + { + sound_id = FSoundID(S_FindSkinnedSound(ent, sound_id)); + if (sound_id <= 0) return; + } + + + FRolloffInfo rolloff; + + rolloff.RolloffType = ROLLOFF_Linear; + rolloff.MinDistance = mindist; + rolloff.MaxDistance = maxdist; + S_StartSound(SOURCE_Actor, ent, nullptr, channel, sound_id, volume, 1, &rolloff); +} + +//========================================================================== +// +// S_Sound - A polyobject is source +// +//========================================================================== + +void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation) +{ + if (poly->Level != primaryLevel) return; + S_StartSound (SOURCE_Polyobj, poly, nullptr, channel, sound_id, volume, attenuation); +} + +//========================================================================== +// +// S_Sound - A point is source +// +//========================================================================== + +void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sound_id, float volume, float attenuation) +{ + if (Level != primaryLevel) return; + // The sound system switches Y and Z around. + FVector3 p((float)pos.X, (float)pos.Z, (float)pos.Y); + S_StartSound (SOURCE_Unattached, nullptr, &p, channel, sound_id, volume, attenuation); +} + +//========================================================================== +// +// S_Sound - An entire sector is source +// +//========================================================================== + +void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation) +{ + if (sec->Level != primaryLevel) return; + S_StartSound (SOURCE_Sector, sec, nullptr, channel, sfxid, volume, attenuation); +} + +//========================================================================== +// +// S_PlaySound - Subfunction used by ACS and DECORATE +// +// Has a local parameter to make the sound audible only to the source +// +//========================================================================== + +void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch) +{ + if (a == nullptr || a->Sector->Flags & SECF_SILENT || a->Level != primaryLevel) + return; + + if (!local) + { + S_SoundPitchActor(a, chan, sid, vol, atten, pitch); + } + else + { + if (a->CheckLocalView()) + { + S_SoundPitch(chan, sid, vol, ATTN_NONE, pitch); + } + } +} + +void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local) +{ + S_PlaySoundPitch(a, chan, sid, vol, atten, local, 0.f); +} + +void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch) +{ + if (!looping) + { + if (!(channel & CHAN_NOSTOP) || !S_IsActorPlayingSomething(self, channel & 7, soundid)) + { + S_PlaySoundPitch(self, channel, soundid, (float)volume, (float)attenuation, local, (float)pitch); + } + } + else + { + if (!S_IsActorPlayingSomething(self, channel & 7, soundid)) + { + S_PlaySoundPitch(self, channel | CHAN_LOOP, soundid, (float)volume, (float)attenuation, local, (float)pitch); + } + } +} + + +//========================================================================== +// +// S_StopSound +// +// Stops a sound from a single actor from playing on a specific channel. +// +//========================================================================== + +void S_StopSound (AActor *actor, int channel) +{ + S_StopSound(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + + +//========================================================================== +// +// S_StopSound +// +// Stops a sound from a single sector from playing on a specific channel. +// +//========================================================================== + +void S_StopSound (const sector_t *sec, int channel) +{ + S_StopSound(SOURCE_Sector, sec, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + +//========================================================================== +// +// S_StopSound +// +// Stops a sound from a single polyobject from playing on a specific channel. +// +//========================================================================== + +void S_StopSound (const FPolyObj *poly, int channel) +{ + S_StopSound(SOURCE_Polyobj, poly, (compatflags & COMPATF_MAGICSILENCE) ? -1 : channel); +} + +//========================================================================== +// +// S_RelinkSound +// +// Moves all the sounds from one thing to another. If the destination is +// NULL, then the sound becomes a positioned sound. +//========================================================================== + +void S_RelinkSound (AActor *from, AActor *to) +{ + + FVector3 p = from->SoundPos(); + S_RelinkSound(SOURCE_Actor, from, to, !(compatflags2 & COMPATF2_SOUNDCUTOFF)? &p : nullptr); +} + +//========================================================================== +// +// S_ChangeSoundVolume +// +//========================================================================== + +void S_ChangeActorSoundVolume(AActor *actor, int channel, double dvolume) +{ + S_ChangeSoundVolume(SOURCE_Actor, actor, (compatflags & COMPATF_MAGICSILENCE)? -1 : channel, dvolume); +} + +//========================================================================== +// +// S_ChangeSoundPitch +// +//========================================================================== + +void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch) +{ + for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) + { + if (chan->SourceType == SOURCE_Actor && + chan->Source == actor && + chan->EntChannel == channel) + { + S_SetPitch(chan, (float)pitch); + return; + } + } + return; +} + +//========================================================================== +// +// S_GetSoundPlayingInfo +// +// Is a sound being played by a specific emitter? +//========================================================================== + +bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) +{ + return S_GetSoundPlayingInfo(SOURCE_Actor, actor, sound_id); +} + +bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) +{ + return S_GetSoundPlayingInfo(SOURCE_Sector, sec, sound_id); +} + +bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) +{ + return S_GetSoundPlayingInfo(SOURCE_Polyobj, poly, sound_id); +} + + //========================================================================== +// +// S_IsActorPlayingSomething +// +//========================================================================== + +bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) +{ + if (compatflags & COMPATF_MAGICSILENCE) + { + channel = CHAN_AUTO; + } + return S_IsSourcePlayingSomething(SOURCE_Actor, actor, channel, sound_id); +} + +//========================================================================== +// +// Sets the internal listener structure +// +//========================================================================== + +static void S_SetListener(AActor *listenactor) +{ + if (listenactor != NULL) + { + listener.angle = (float)listenactor->Angles.Yaw.Radians(); + /* + listener.velocity.X = listenactor->vel.x * (TICRATE/65536.f); + listener.velocity.Y = listenactor->vel.z * (TICRATE/65536.f); + listener.velocity.Z = listenactor->vel.y * (TICRATE/65536.f); + */ + listener.velocity.Zero(); + listener.position = listenactor->SoundPos(); + listener.underwater = listenactor->waterlevel == 3; + assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber); + listener.Environment = primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment; + listener.valid = true; + } + else + { + listener.angle = 0; + listener.position.Zero(); + listener.velocity.Zero(); + listener.underwater = false; + listener.Environment = NULL; + listener.valid = false; + } + listener.ListenerObject = listenactor; +} + +//========================================================================== +// +// S_UpdateSounds +// +// Updates music & sounds +//========================================================================== + +void S_UpdateSounds (AActor *listenactor) +{ + // should never happen + S_SetListener(listenactor); + + for (auto Level : AllLevels()) + { + SN_UpdateActiveSequences(Level); + } + + S_UpdateSounds(primaryLevel->time); +} + +//========================================================================== +// +// Although saving the sound system's state is supposed to be an engine +// feature, the specifics cannot be set up there, this needs to be on the client side. +// +//========================================================================== + +static FSerializer &Serialize(FSerializer &arc, const char *key, FSoundChan &chan, FSoundChan *def) +{ + if (arc.BeginObject(key)) + { + arc("sourcetype", chan.SourceType) + ("soundid", chan.SoundID) + ("orgid", chan.OrgID) + ("volume", chan.Volume) + ("distancescale", chan.DistanceScale) + ("pitch", chan.Pitch) + ("chanflags", chan.ChanFlags) + ("entchannel", chan.EntChannel) + ("priority", chan.Priority) + ("nearlimit", chan.NearLimit) + ("starttime", chan.StartTime) + ("rolloftype", chan.Rolloff.RolloffType) + ("rolloffmin", chan.Rolloff.MinDistance) + ("rolloffmax", chan.Rolloff.MaxDistance) + ("limitrange", chan.LimitRange); + + switch (chan.SourceType) + { + case SOURCE_None: break; + case SOURCE_Actor: { AActor* s = (AActor*)chan.Source; arc("actor", s); chan.Source = s; break; } + case SOURCE_Sector: { auto s = (sector_t*)chan.Source; arc("sector", s); chan.Source = s; break; } + case SOURCE_Polyobj: { auto s = (FPolyObj*)chan.Source; arc("poly", s); chan.Source = s; break; } + case SOURCE_Unattached: arc.Array("point", chan.Point, 3); break; + default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; + } + arc.EndObject(); + } + return arc; +} + +//========================================================================== +// +// S_SerializeSounds +// +//========================================================================== + +void S_SerializeSounds(FSerializer &arc) +{ + FSoundChan *chan; + + GSnd->Sync(true); + + if (arc.isWriting()) + { + TArray chans; + + // Count channels and accumulate them so we can store them in + // reverse order. That way, they will be in the same order when + // reloaded later as they are now. + for (chan = Channels; chan != NULL; chan = chan->NextChan) + { + // If the sound is forgettable, this is as good a time as + // any to forget about it. And if it's a UI sound, it shouldn't + // be stored in the savegame. + if (!(chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_UI))) + { + chans.Push(chan); + } + } + if (chans.Size() > 0 && arc.BeginArray("sounds")) + { + for (unsigned int i = chans.Size(); i-- != 0; ) + { + // Replace start time with sample position. + uint64_t start = chans[i]->StartTime; + chans[i]->StartTime = GSnd ? GSnd->GetPosition(chans[i]) : 0; + arc(nullptr, *chans[i]); + chans[i]->StartTime = start; + } + arc.EndArray(); + } + } + else + { + unsigned int count; + + S_StopAllChannels(); + if (arc.BeginArray("sounds")) + { + count = arc.ArraySize(); + for (unsigned int i = 0; i < count; ++i) + { + chan = (FSoundChan*)S_GetChannel(NULL); + arc(nullptr, *chan); + // Sounds always start out evicted when restored from a save. + chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; + } + arc.EndArray(); + } + // The two tic delay is to make sure any screenwipes have finished. + // This needs to be two because the game is run for one tic before + // the wipe so that it can produce a screen to wipe to. So if we + // only waited one tic to restart the sounds, they would start + // playing before the wipe, and depending on the synchronization + // between the main thread and the mixer thread at the time, the + // sounds might be heard briefly before pausing for the wipe. + RestartEvictionsAt = primaryLevel->time + 2; + } + GSnd->Sync(false); + GSnd->UpdateSounds(); +} + + +//========================================================================== +// +// CCMD playsound +// +//========================================================================== + +CCMD (playsound) +{ + if (argv.argc() > 1) + { + FSoundID id = argv[1]; + if (id == 0) + { + Printf("'%s' is not a sound\n", argv[1]); + } + else + { + S_Sound (CHAN_AUTO | CHAN_UI, id, 1.f, ATTN_NONE); + } + } +} + +//========================================================================== +// +// CCMD loopsound +// +//========================================================================== + +CCMD (loopsound) +{ + if (players[consoleplayer].mo != NULL && !netgame && argv.argc() > 1) + { + FSoundID id = argv[1]; + if (id == 0) + { + Printf("'%s' is not a sound\n", argv[1]); + } + else + { + AActor *icon = Spawn(primaryLevel, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE); + if (icon != NULL) + { + S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE); + } + } + } +} + +//========================================================================== +// +// CCMD cachesound +// +//========================================================================== + +CCMD (cachesound) +{ + if (argv.argc() < 2) + { + Printf ("Usage: cachesound ...\n"); + return; + } + for (int i = 1; i < argv.argc(); ++i) + { + FSoundID sfxnum = argv[i]; + if (sfxnum != FSoundID(0)) + { + S_CacheSound (&S_sfx[sfxnum]); + } + } +} + + +CCMD(listsoundchannels) +{ + FSoundChan *chan; + int count = 0; + for (chan = Channels; chan != NULL; chan = chan->NextChan) + { + if (!(chan->ChanFlags & CHAN_EVICTED)) + { + FVector3 chanorigin; + + CalcPosVel(chan, &chanorigin, NULL); + + Printf("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)chan->SoundID, chanorigin.X, chanorigin.Y, chanorigin.Z); + count++; + } + } + Printf("%d sounds playing\n", count); +} + +// intentionally moved here to keep the s_music include out of the rest of the file. + +//========================================================================== +// +// S_PauseSound +// +// Stop music and sound effects, during game PAUSE. +//========================================================================== +#include "s_music.h" + +void S_PauseSound (bool notmusic, bool notsfx) +{ + if (!notmusic) + { + S_PauseMusic(); + } + if (!notsfx) + { + SoundPaused = true; + GSnd->SetSfxPaused (true, 0); + } +} + +DEFINE_ACTION_FUNCTION(DObject, S_PauseSound) +{ + PARAM_PROLOGUE; + PARAM_BOOL(notmusic); + PARAM_BOOL(notsfx); + S_PauseSound(notmusic, notsfx); + return 0; +} + +//========================================================================== +// +// S_ResumeSound +// +// Resume music and sound effects, after game PAUSE. +//========================================================================== + +void S_ResumeSound (bool notsfx) +{ + S_ResumeMusic(); + if (!notsfx) + { + SoundPaused = false; + GSnd->SetSfxPaused (false, 0); + } +} + +DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound) +{ + PARAM_PROLOGUE; + PARAM_BOOL(notsfx); + S_ResumeSound(notsfx); + return 0; +} + + +CCMD (snd_status) +{ + GSnd->PrintStatus (); +} + +CCMD (snd_reset) +{ + S_SoundReset(); +} + +void S_SoundReset() +{ + S_StopMusic(true); + S_Reset(); + S_RestartMusic(); +} + +CCMD (snd_listdrivers) +{ + GSnd->PrintDriversList (); +} + +ADD_STAT (sound) +{ + return GSnd->GatherStats (); +} diff --git a/src/sound/s_doomsound.h b/src/sound/s_doomsound.h new file mode 100644 index 000000000..49f002a8f --- /dev/null +++ b/src/sound/s_doomsound.h @@ -0,0 +1,47 @@ +#pragma once + +// Information about one playing sound. +struct sector_t; +struct FPolyObj; + +void S_UpdateSounds(AActor* listenactor); + +void S_Sound (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation); +void S_SoundMinMaxDist (AActor *ent, int channel, FSoundID sfxid, float volume, float mindist, float maxdist); +void S_Sound (const FPolyObj *poly, int channel, FSoundID sfxid, float volume, float attenuation); +void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation); +void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sfxid, float volume, float attenuation); + +void S_SoundPitchActor (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation, float pitch); + +// [Nash] Used by ACS and DECORATE +void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local); +void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch); + +// Stops a sound emanating from one of an emitter's channels. +void S_StopSound (AActor *ent, int channel); +void S_StopSound (const sector_t *sec, int channel); +void S_StopSound (const FPolyObj *poly, int channel); + +// Moves all sounds from one mobj to another +void S_RelinkSound (AActor *from, AActor *to); + +// Is the sound playing on one of the emitter's channels? +bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id); +bool S_GetSoundPlayingInfo (const sector_t *sector, int sound_id); +bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id); + +bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id); + +// Change a playing sound's volume +void S_ChangeActorSoundVolume(AActor *actor, int channel, double volume); + +// Change a playing sound's pitch +void S_ChangeActorSoundPitch(AActor *actor, int channel, double pitch); + +// Stores/retrieves playing channel information in an archive. +void S_SerializeSounds(FSerializer &arc); + +void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch); +static void S_SetListener(AActor *listenactor); +void S_SoundReset(); \ No newline at end of file diff --git a/src/sound/s_sound.cpp b/src/sound/s_sound.cpp index b5bb73234..a06be742c 100644 --- a/src/sound/s_sound.cpp +++ b/src/sound/s_sound.cpp @@ -1,31 +1,11 @@ -//----------------------------------------------------------------------------- -// -// Copyright 1993-1996 id Software -// Copyright 1999-2016 Randy Heit -// Copyright 2002-2016 Christoph Oelckers -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see http://www.gnu.org/licenses/ -// -//----------------------------------------------------------------------------- -// -// DESCRIPTION: none -// -//----------------------------------------------------------------------------- - -/* For code that originates from ZDoom the following applies: +/* +** s_sound.cpp +** Main sound engine ** **--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2002-2019 Christoph Oelckers +** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions @@ -95,15 +75,6 @@ // TYPES ------------------------------------------------------------------- -enum -{ - SOURCE_None, // Sound is always on top of the listener. - SOURCE_Actor, // Sound is coming from an actor. - SOURCE_Sector, // Sound is coming from a sector. - SOURCE_Polyobj, // Sound is coming from a polyobject. - SOURCE_Unattached, // Sound is not attached to any particular emitter. -}; - // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- extern float S_GetMusicVolume (const char *music); @@ -111,25 +82,24 @@ extern float S_GetMusicVolume (const char *music); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- void I_CloseSound(); + // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- static void S_LoadSound3D(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer); -static bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, AActor *actor, int channel); -static bool S_IsChannelUsed(AActor *actor, int channel, int *seen); +static bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, int sourcetype, const void *actor, int channel); +static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen); static void S_ActivatePlayList(bool goBack); -static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); -static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, const FPolyObj *poly, +void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel); +static void CalcPosVel(int type, const void *source, const float pt[3], int channel, int chanflags, FVector3 *pos, FVector3 *vel); static void CalcSectorSoundOrg(const DVector3 &listenpos, const sector_t *sec, int channum, FVector3 &res); static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, FVector3 &res); -static FSoundChan *S_StartSound(AActor *mover, const sector_t *sec, const FPolyObj *poly, - const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo *rolloff, float spitch); -static void S_SetListener(SoundListener &listener, AActor *listenactor); // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static bool SoundPaused; // whether sound is paused -static int RestartEvictionsAt; // do not restart evicted channels before this time +bool SoundPaused; // whether sound is paused +int RestartEvictionsAt; // do not restart evicted channels before this time +SoundListener listener; // PUBLIC DATA DEFINITIONS ------------------------------------------------- @@ -628,9 +598,9 @@ void S_LinkChannel(FSoundChan *chan, FSoundChan **head) // //========================================================================= -static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) +void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) { - CalcPosVel(chan->SourceType, chan->Actor, chan->Sector, chan->Poly, chan->Point, + CalcPosVel(chan->SourceType, chan->Source, chan->Point, chan->EntChannel, chan->ChanFlags, pos, vel); } @@ -642,8 +612,8 @@ static void CalcPosVel(FSoundChan *chan, FVector3 *pos, FVector3 *vel) // //========================================================================= -static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, - const FPolyObj *poly, const float pt[3], int channum, int chanflags, FVector3 *pos, FVector3 *vel) +static void CalcPosVel(int type, const void *source, + const float pt[3], int channum, int chanflags, FVector3 *pos, FVector3 *vel) { if (pos != NULL) { @@ -663,6 +633,7 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, pos->Zero(); pgroup = 0; } + if (vel) vel->Zero(); // [BL] Moved this case out of the switch statement to make code easier // on static analysis. @@ -683,16 +654,27 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, break; case SOURCE_Actor: + { + auto actor = (AActor*)source; //assert(actor != NULL); if (actor != NULL) { DVector2 disp = primaryLevel->Displacements.getOffset(pgroup, actor->Sector->PortalGroup); DVector3 posi = actor->Pos() - disp; *pos = { (float)posi.X, (float)posi.Z, (float)posi.Y }; + if (vel) + { + vel->X = float(actor->Vel.X * TICRATE); + vel->Y = float(actor->Vel.Z * TICRATE); + vel->Z = float(actor->Vel.Y * TICRATE); + } } break; + } case SOURCE_Sector: + { + auto sector = (sector_t*)source; assert(sector != NULL); if (sector != NULL) { @@ -706,15 +688,18 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, } else { - + pos->X = (float)(sector->centerspot.X - disp.X); pos->Z = (float)(sector->centerspot.Y - disp.Y); chanflags |= CHAN_LISTENERZ; } } break; + } case SOURCE_Polyobj: + { + auto poly = (FPolyObj*)source; assert(poly != NULL); if (poly != NULL) { @@ -725,6 +710,7 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, } break; } + } if ((chanflags & CHAN_LISTENERZ) && players[consoleplayer].camera != NULL) { @@ -732,20 +718,6 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, } } } - if (vel != NULL) - { - // Only actors maintain velocity information. - if (type == SOURCE_Actor && actor != NULL) - { - vel->X = float(actor->Vel.X * TICRATE); - vel->Y = float(actor->Vel.Z * TICRATE); - vel->Z = float(actor->Vel.Y * TICRATE); - } - else - { - vel->Zero(); - } - } } //========================================================================== @@ -797,7 +769,7 @@ static bool ValidatePosVel(const AActor *actor, const FVector3 &pos, const FVect static bool ValidatePosVel(const FSoundChan *const chan, const FVector3 &pos, const FVector3 &vel) { - return ValidatePosVel(chan->SourceType == SOURCE_Actor ? chan->Actor : nullptr, pos, vel); + return ValidatePosVel(chan->SourceType == SOURCE_Actor ? (AActor*)chan->Source : nullptr, pos, vel); } //========================================================================== @@ -885,9 +857,9 @@ static void CalcPolyobjSoundOrg(const DVector3 &listenpos, const FPolyObj *poly, // //========================================================================== -static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyObj *poly, +FSoundChan *S_StartSound(int type, const void *source, const FVector3 *pt, int channel, FSoundID sound_id, float volume, float attenuation, - FRolloffInfo *forcedrolloff=NULL, float spitch = 0.0f) + FRolloffInfo *forcedrolloff, float spitch) { sfxinfo_t *sfx; int chanflags; @@ -902,36 +874,15 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO if (sound_id <= 0 || volume <= 0 || nosfx || nosound ) return NULL; - int type; - - if (actor != NULL) - { - type = SOURCE_Actor; - } - else if (sec != NULL) - { - type = SOURCE_Sector; - } - else if (poly != NULL) - { - type = SOURCE_Polyobj; - } - else if (pt != NULL) - { - type = SOURCE_Unattached; - } - else - { - type = SOURCE_None; - } + if (type == SOURCE_Unattached && pt == nullptr) type = SOURCE_None; org_id = sound_id; chanflags = channel & ~7; channel &= 7; - CalcPosVel(type, actor, sec, poly, &pt->X, channel, chanflags, &pos, &vel); + CalcPosVel(type, source, &pt->X, channel, chanflags, &pos, &vel); - if (!ValidatePosVel(type == SOURCE_Actor ? actor : nullptr, pos, vel)) + if (!ValidatePosVel(type == SOURCE_Actor ? (AActor*)source : nullptr, pos, vel)) { return nullptr; } @@ -942,7 +893,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } else if ((chanflags & CHAN_MAYBE_LOCAL) && (compatflags & COMPATF_SILENTPICKUP)) { - if (actor != nullptr && actor != players[consoleplayer].camera) + if (source != nullptr && source == listener.ListenerObject) { return nullptr; } @@ -965,14 +916,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // Resolve player sounds, random sounds, and aliases while (sfx->link != sfxinfo_t::NO_LINK) { - if (sfx->bPlayerReserve) - { - sound_id = FSoundID(S_FindSkinnedSound (actor, sound_id)); - near_limit = S_sfx[sound_id].NearLimit; - limit_range = S_sfx[sound_id].LimitRange; - rolloff = &S_sfx[sound_id].Rolloff; - } - else if (sfx->bRandomHeader) + if (sfx->bRandomHeader) { // Random sounds attenuate based on the original (random) sound as well as the chosen one. attenuation *= sfx->Attenuation; @@ -1026,14 +970,14 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // If the sound is unpositioned or comes from the listener, it is // never limited. - if (type == SOURCE_None || actor == players[consoleplayer].camera) + if (type == SOURCE_None || source == listener.ListenerObject) { near_limit = 0; } // If this sound doesn't like playing near itself, don't play it if // that's what would happen. - if (near_limit > 0 && S_CheckSoundLimit(sfx, pos, near_limit, limit_range, actor, channel)) + if (near_limit > 0 && S_CheckSoundLimit(sfx, pos, near_limit, limit_range, type, type == SOURCE_Actor? source : nullptr, channel)) { chanflags |= CHAN_EVICTED; } @@ -1056,7 +1000,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } // Select priority. - if (type == SOURCE_None || actor == players[consoleplayer].camera) + if (type == SOURCE_None || source == listener.ListenerObject) { basepriority = 80; } @@ -1066,11 +1010,11 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } int seen = 0; - if (actor != NULL && channel == CHAN_AUTO) + if (source != NULL && channel == CHAN_AUTO) { // Select a channel that isn't already playing something. // Try channel 0 first, then travel from channel 7 down. - if (!S_IsChannelUsed(actor, 0, &seen)) + if (!S_IsChannelUsed(type, source, 0, &seen)) { channel = 0; } @@ -1078,7 +1022,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO { for (channel = 7; channel > 0; --channel) { - if (!S_IsChannelUsed(actor, channel, &seen)) + if (!S_IsChannelUsed(type, source, channel, &seen)) { break; } @@ -1091,26 +1035,16 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO } // If this actor is already playing something on the selected channel, stop it. - if (type != SOURCE_None && ((actor == NULL && channel != CHAN_AUTO) || (actor != NULL && S_IsChannelUsed(actor, channel, &seen)))) + if (type != SOURCE_None && ((source == NULL && channel != CHAN_AUTO) || (source != NULL && S_IsChannelUsed(type, source, channel, &seen)))) { for (chan = Channels; chan != NULL; chan = chan->NextChan) { if (chan->SourceType == type && chan->EntChannel == channel) { - bool foundit; - - switch (type) + if (type != SOURCE_Unattached) chan->Source = source; + else { - case SOURCE_Actor: foundit = (chan->Actor == actor); break; - case SOURCE_Sector: foundit = (chan->Sector == sec); break; - case SOURCE_Polyobj: foundit = (chan->Poly == poly); break; - case SOURCE_Unattached: foundit = (chan->Point[0] == pt->X && chan->Point[2] == pt->Z && chan->Point[1] == pt->Y); break; - default: foundit = false; break; - } - if (foundit) - { - S_StopChannel(chan); - break; + chan->Point[0] == pt->X && chan->Point[2] == pt->Z && chan->Point[1] == pt->Y; } } } @@ -1148,8 +1082,6 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO if (attenuation > 0) { S_LoadSound3D(sfx, &SoundBuffer); - SoundListener listener; - S_SetListener(listener, players[consoleplayer].camera); chan = (FSoundChan*)GSnd->StartSound3D (sfx->data3d, &listener, float(volume), rolloff, float(attenuation), pitch, basepriority, pos, vel, channel, startflags, NULL); } else @@ -1184,13 +1116,13 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO chan->Priority = basepriority; chan->DistanceScale = float(attenuation); chan->SourceType = type; - switch (type) + if (type == SOURCE_Unattached) { - case SOURCE_Actor: chan->Actor = actor; break; - case SOURCE_Sector: chan->Sector = sec; break; - case SOURCE_Polyobj: chan->Poly = poly; break; - case SOURCE_Unattached: chan->Point[0] = pt->X; chan->Point[1] = pt->Y; chan->Point[2] = pt->Z; break; - default: break; + chan->Point[0] = pt->X; chan->Point[1] = pt->Y; chan->Point[2] = pt->Z; + } + else if (type != SOURCE_None) + { + chan->Source = source; } if (spitch > 0.0) @@ -1249,15 +1181,12 @@ void S_RestartSound(FSoundChan *chan) // If this sound doesn't like playing near itself, don't play it if // that's what would happen. - if (chan->NearLimit > 0 && S_CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, NULL, 0)) + if (chan->NearLimit > 0 && S_CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0)) { return; } S_LoadSound3D(sfx, &SoundBuffer); - SoundListener listener; - S_SetListener(listener, players[consoleplayer].camera); - chan->ChanFlags &= ~(CHAN_EVICTED|CHAN_ABSTIME); ochan = (FSoundChan*)GSnd->StartSound3D(sfx->data3d, &listener, chan->Volume, &chan->Rolloff, chan->DistanceScale, chan->Pitch, chan->Priority, pos, vel, chan->EntChannel, startflags, chan); @@ -1274,158 +1203,6 @@ void S_RestartSound(FSoundChan *chan) } } -//========================================================================== -// -// S_Sound - Unpositioned version -// -//========================================================================== - -void S_SoundPitch(int channel, FSoundID sound_id, float volume, float attenuation, float pitch) -{ - S_StartSound(NULL, NULL, NULL, NULL, channel, sound_id, volume, attenuation, 0, pitch); -} - -void S_Sound(int channel, FSoundID sound_id, float volume, float attenuation) -{ - S_StartSound (NULL, NULL, NULL, NULL, channel, sound_id, volume, attenuation, 0, 0.f); -} - -DEFINE_ACTION_FUNCTION(DObject, S_Sound) -{ - PARAM_PROLOGUE; - PARAM_SOUND(id); - PARAM_INT(channel); - PARAM_FLOAT(volume); - PARAM_FLOAT(attn); - PARAM_FLOAT(pitch); - S_SoundPitch(channel, id, static_cast(volume), static_cast(attn), static_cast(pitch)); - return 0; -} - -//========================================================================== -// -// S_Sound - An actor is source -// -//========================================================================== -void S_SoundPitchActor(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation, float pitch) -{ - if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) - return; - S_StartSound (ent, nullptr, nullptr, nullptr, channel, sound_id, volume, attenuation, 0, pitch); -} - -void S_Sound(AActor *ent, int channel, FSoundID sound_id, float volume, float attenuation) -{ - S_SoundPitchActor(ent, channel, sound_id, volume, attenuation, 0.f); -} -//========================================================================== -// -// S_SoundMinMaxDist - An actor is source -// -// Attenuation is specified as min and max distances, rather than a scalar. -// -//========================================================================== - -void S_SoundMinMaxDist(AActor *ent, int channel, FSoundID sound_id, float volume, float mindist, float maxdist) -{ - if (ent == nullptr || ent->Sector->Flags & SECF_SILENT || ent->Level != primaryLevel) - return; - - FRolloffInfo rolloff; - - rolloff.RolloffType = ROLLOFF_Linear; - rolloff.MinDistance = mindist; - rolloff.MaxDistance = maxdist; - S_StartSound(ent, nullptr, nullptr, nullptr, channel, sound_id, volume, 1, &rolloff); -} - -//========================================================================== -// -// S_Sound - A polyobject is source -// -//========================================================================== - -void S_Sound (const FPolyObj *poly, int channel, FSoundID sound_id, float volume, float attenuation) -{ - if (poly->Level != primaryLevel) return; - S_StartSound (nullptr, nullptr, poly, nullptr, channel, sound_id, volume, attenuation); -} - -//========================================================================== -// -// S_Sound - A point is source -// -//========================================================================== - -void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sound_id, float volume, float attenuation) -{ - if (Level != primaryLevel) return; - // The sound system switches Y and Z around. - FVector3 p((float)pos.X, (float)pos.Z, (float)pos.Y); - S_StartSound (nullptr, nullptr, nullptr, &p, channel, sound_id, volume, attenuation); -} - -//========================================================================== -// -// S_Sound - An entire sector is source -// -//========================================================================== - -void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation) -{ - if (sec->Level != primaryLevel) return; - S_StartSound (nullptr, sec, nullptr, nullptr, channel, sfxid, volume, attenuation); -} - -//========================================================================== -// -// S_PlaySound - Subfunction used by ACS and DECORATE -// -// Has a local parameter to make the sound audible only to the source -// -//========================================================================== - -void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch) -{ - if (a == nullptr || a->Sector->Flags & SECF_SILENT || a->Level != primaryLevel) - return; - - if (!local) - { - S_SoundPitchActor(a, chan, sid, vol, atten, pitch); - } - else - { - if (a->CheckLocalView()) - { - S_SoundPitch(chan, sid, vol, ATTN_NONE, pitch); - } - } -} - -void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local) -{ - S_PlaySoundPitch(a, chan, sid, vol, atten, local, 0.f); -} - -void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch) -{ - if (!looping) - { - if (!(channel & CHAN_NOSTOP) || !S_IsActorPlayingSomething(self, channel & 7, soundid)) - { - S_PlaySoundPitch(self, channel, soundid, (float)volume, (float)attenuation, local, (float)pitch); - } - } - else - { - if (!S_IsActorPlayingSomething(self, channel & 7, soundid)) - { - S_PlaySoundPitch(self, channel | CHAN_LOOP, soundid, (float)volume, (float)attenuation, local, (float)pitch); - } - } -} - //========================================================================== // // S_LoadSound @@ -1603,7 +1380,7 @@ bool S_CheckSingular(int sound_id) //========================================================================== bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, float limit_range, - AActor *actor, int channel) + int sourcetype, const void *actor, int channel) { FSoundChan *chan; int count; @@ -1615,7 +1392,7 @@ bool S_CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_limit, floa FVector3 chanorigin; if (actor != NULL && chan->EntChannel == channel && - chan->SourceType == SOURCE_Actor && chan->Actor == actor) + chan->SourceType == sourcetype && chan->Source == actor) { // We are restarting a playing sound. Always let it play. return false; } @@ -1644,8 +1421,7 @@ void S_StopSound (int channel) while (chan != NULL) { FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_None && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) + if (chan->SourceType == SOURCE_None) { S_StopChannel(chan); } @@ -1661,63 +1437,15 @@ void S_StopSound (int channel) // //========================================================================== -void S_StopSound (AActor *actor, int channel) +void S_StopSound(int sourcetype, const void* actor, int channel) { - FSoundChan *chan = Channels; + FSoundChan* chan = Channels; while (chan != NULL) { - FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Actor && - chan->Actor == actor && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) - { - S_StopChannel(chan); - } - chan = next; - } -} - -//========================================================================== -// -// S_StopSound -// -// Stops a sound from a single sector from playing on a specific channel. -// -//========================================================================== - -void S_StopSound (const sector_t *sec, int channel) -{ - FSoundChan *chan = Channels; - while (chan != NULL) - { - FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Sector && - chan->Sector == sec && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) - { - S_StopChannel(chan); - } - chan = next; - } -} - -//========================================================================== -// -// S_StopSound -// -// Stops a sound from a single polyobject from playing on a specific channel. -// -//========================================================================== - -void S_StopSound (const FPolyObj *poly, int channel) -{ - FSoundChan *chan = Channels; - while (chan != NULL) - { - FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Polyobj && - chan->Poly == poly && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) + FSoundChan* next = chan->NextChan; + if (chan->SourceType == sourcetype && + chan->Source == actor && + (chan->EntChannel == channel || channel < 0)) { S_StopChannel(chan); } @@ -1758,7 +1486,7 @@ void S_StopAllChannels () // NULL, then the sound becomes a positioned sound. //========================================================================== -void S_RelinkSound (AActor *from, AActor *to) +void S_RelinkSound (int sourcetype, const void *from, const void *to, const FVector3 *optpos) { if (from == NULL) return; @@ -1767,20 +1495,19 @@ void S_RelinkSound (AActor *from, AActor *to) while (chan != NULL) { FSoundChan *next = chan->NextChan; - if (chan->SourceType == SOURCE_Actor && chan->Actor == from) + if (chan->SourceType == sourcetype && chan->Source == from) { if (to != NULL) { - chan->Actor = to; + chan->Source = to; } - else if (!(chan->ChanFlags & CHAN_LOOP) && !(compatflags2 & COMPATF2_SOUNDCUTOFF)) + else if (!(chan->ChanFlags & CHAN_LOOP) && optpos) { - chan->Actor = NULL; + chan->Source = NULL; chan->SourceType = SOURCE_Unattached; - FVector3 p = from->SoundPos(); - chan->Point[0] = p.X; - chan->Point[1] = p.Y; - chan->Point[2] = p.Z; + chan->Point[0] = optpos->X; + chan->Point[1] = optpos->Y; + chan->Point[2] = optpos->Z; } else { @@ -1798,7 +1525,7 @@ void S_RelinkSound (AActor *from, AActor *to) // //========================================================================== -void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) +void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume) { float volume = float(dvolume); // don't let volume get out of bounds @@ -1809,9 +1536,9 @@ void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && - chan->Actor == actor && - (chan->EntChannel == channel || (compatflags & COMPATF_MAGICSILENCE))) + if (chan->SourceType == sourcetype && + chan->Source == source && + (chan->EntChannel == channel || channel == -1)) { GSnd->ChannelVolume(chan, volume); chan->Volume = volume; @@ -1827,12 +1554,12 @@ void S_ChangeSoundVolume(AActor *actor, int channel, double dvolume) // //========================================================================== -void S_ChangeSoundPitch(AActor *actor, int channel, double pitch) +void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && - chan->Actor == actor && + if (chan->SourceType == sourcetype && + chan->Source == source && chan->EntChannel == channel) { S_SetPitch(chan, (float)pitch); @@ -1856,49 +1583,15 @@ void S_SetPitch(FSoundChan *chan, float pitch) // Is a sound being played by a specific emitter? //========================================================================== -bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id) +bool S_GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id) { if (sound_id > 0) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { if (chan->OrgID == sound_id && - chan->SourceType == SOURCE_Actor && - chan->Actor == actor) - { - return true; - } - } - } - return false; -} - -bool S_GetSoundPlayingInfo (const sector_t *sec, int sound_id) -{ - if (sound_id > 0) - { - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (chan->OrgID == sound_id && - chan->SourceType == SOURCE_Sector && - chan->Sector == sec) - { - return true; - } - } - } - return false; -} - -bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) -{ - if (sound_id > 0) - { - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (chan->OrgID == sound_id && - chan->SourceType == SOURCE_Polyobj && - chan->Poly == poly) + chan->SourceType == sourcetype && + chan->Source == source) { return true; } @@ -1917,7 +1610,7 @@ bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id) // //========================================================================== -static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) +static bool S_IsChannelUsed(int sourcetype, const void *actor, int channel, int *seen) { if (*seen & (1 << channel)) { @@ -1925,7 +1618,7 @@ static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) } for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && chan->Actor == actor) + if (chan->SourceType == sourcetype && chan->Source == actor) { *seen |= 1 << chan->EntChannel; if (chan->EntChannel == channel) @@ -1943,16 +1636,11 @@ static bool S_IsChannelUsed(AActor *actor, int channel, int *seen) // //========================================================================== -bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id) +bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) { - if (compatflags & COMPATF_MAGICSILENCE) - { - channel = 0; - } - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { - if (chan->SourceType == SOURCE_Actor && chan->Actor == actor) + if (chan->SourceType == sourcetype && chan->Source == actor) { if (channel == 0 || chan->EntChannel == channel) { @@ -2099,15 +1787,11 @@ void S_RestoreEvictedChannels() // Updates music & sounds //========================================================================== -void S_UpdateSounds (AActor *listenactor) +void S_UpdateSounds(int time) { FVector3 pos, vel; - SoundListener listener; - // should never happen - S_SetListener(listener, listenactor); - - for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) + for (FSoundChan* chan = Channels; chan != NULL; chan = chan->NextChan) { if ((chan->ChanFlags & (CHAN_EVICTED | CHAN_IS3D)) == CHAN_IS3D) { @@ -2121,57 +1805,16 @@ void S_UpdateSounds (AActor *listenactor) chan->ChanFlags &= ~CHAN_JUSTSTARTED; } - for (auto Level : AllLevels()) - { - SN_UpdateActiveSequences(Level); - } - GSnd->UpdateListener(&listener); GSnd->UpdateSounds(); - if (primaryLevel->time >= RestartEvictionsAt) + if (time >= RestartEvictionsAt) { RestartEvictionsAt = 0; S_RestoreEvictedChannels(); } } -//========================================================================== -// -// Sets the internal listener structure -// -//========================================================================== - -static void S_SetListener(SoundListener &listener, AActor *listenactor) -{ - if (listenactor != NULL) - { - listener.angle = (float)listenactor->Angles.Yaw.Radians(); - /* - listener.velocity.X = listenactor->vel.x * (TICRATE/65536.f); - listener.velocity.Y = listenactor->vel.z * (TICRATE/65536.f); - listener.velocity.Z = listenactor->vel.y * (TICRATE/65536.f); - */ - listener.velocity.Zero(); - listener.position = listenactor->SoundPos(); - listener.underwater = listenactor->waterlevel == 3; - assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber); - listener.Environment = primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment; - listener.valid = true; - } - else - { - listener.angle = 0; - listener.position.Zero(); - listener.velocity.Zero(); - listener.underwater = false; - listener.Environment = NULL; - listener.valid = false; - } -} - - - //========================================================================== // // S_GetRolloff @@ -2317,7 +1960,7 @@ void S_StopChannel(FSoundChan *chan) chan->ChanFlags |= CHAN_FORGETTABLE; if (chan->SourceType == SOURCE_Actor) { - chan->Actor = NULL; + chan->Source = NULL; } } GSnd->StopChannel(chan); @@ -2328,268 +1971,6 @@ void S_StopChannel(FSoundChan *chan) } } -//========================================================================== -// -// -// -//========================================================================== - -static FSerializer &Serialize(FSerializer &arc, const char *key, FSoundChan &chan, FSoundChan *def) -{ - if (arc.BeginObject(key)) - { - arc("sourcetype", chan.SourceType) - ("soundid", chan.SoundID) - ("orgid", chan.OrgID) - ("volume", chan.Volume) - ("distancescale", chan.DistanceScale) - ("pitch", chan.Pitch) - ("chanflags", chan.ChanFlags) - ("entchannel", chan.EntChannel) - ("priority", chan.Priority) - ("nearlimit", chan.NearLimit) - ("starttime", chan.StartTime) - ("rolloftype", chan.Rolloff.RolloffType) - ("rolloffmin", chan.Rolloff.MinDistance) - ("rolloffmax", chan.Rolloff.MaxDistance) - ("limitrange", chan.LimitRange); - - switch (chan.SourceType) - { - case SOURCE_None: break; - case SOURCE_Actor: arc("actor", chan.Actor); break; - case SOURCE_Sector: arc("sector", chan.Sector); break; - case SOURCE_Polyobj: arc("poly", chan.Poly); break; - case SOURCE_Unattached: arc.Array("point", chan.Point, 3); break; - default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; - } - arc.EndObject(); - } - return arc; -} - -//========================================================================== -// -// S_SerializeSounds -// -//========================================================================== - -void S_SerializeSounds(FSerializer &arc) -{ - FSoundChan *chan; - - GSnd->Sync(true); - - if (arc.isWriting()) - { - TArray chans; - - // Count channels and accumulate them so we can store them in - // reverse order. That way, they will be in the same order when - // reloaded later as they are now. - for (chan = Channels; chan != NULL; chan = chan->NextChan) - { - // If the sound is forgettable, this is as good a time as - // any to forget about it. And if it's a UI sound, it shouldn't - // be stored in the savegame. - if (!(chan->ChanFlags & (CHAN_FORGETTABLE | CHAN_UI))) - { - chans.Push(chan); - } - } - if (chans.Size() > 0 && arc.BeginArray("sounds")) - { - for (unsigned int i = chans.Size(); i-- != 0; ) - { - // Replace start time with sample position. - uint64_t start = chans[i]->StartTime; - chans[i]->StartTime = GSnd ? GSnd->GetPosition(chans[i]) : 0; - arc(nullptr, *chans[i]); - chans[i]->StartTime = start; - } - arc.EndArray(); - } - } - else - { - unsigned int count; - - S_StopAllChannels(); - if (arc.BeginArray("sounds")) - { - count = arc.ArraySize(); - for (unsigned int i = 0; i < count; ++i) - { - chan = (FSoundChan*)S_GetChannel(NULL); - arc(nullptr, *chan); - // Sounds always start out evicted when restored from a save. - chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; - } - arc.EndArray(); - } - // The two tic delay is to make sure any screenwipes have finished. - // This needs to be two because the game is run for one tic before - // the wipe so that it can produce a screen to wipe to. So if we - // only waited one tic to restart the sounds, they would start - // playing before the wipe, and depending on the synchronization - // between the main thread and the mixer thread at the time, the - // sounds might be heard briefly before pausing for the wipe. - RestartEvictionsAt = primaryLevel->time + 2; - } - GSnd->Sync(false); - GSnd->UpdateSounds(); -} - - -//========================================================================== -// -// CCMD playsound -// -//========================================================================== - -CCMD (playsound) -{ - if (argv.argc() > 1) - { - FSoundID id = argv[1]; - if (id == 0) - { - Printf("'%s' is not a sound\n", argv[1]); - } - else - { - S_Sound (CHAN_AUTO | CHAN_UI, id, 1.f, ATTN_NONE); - } - } -} - -//========================================================================== -// -// CCMD loopsound -// -//========================================================================== - -CCMD (loopsound) -{ - if (players[consoleplayer].mo != NULL && !netgame && argv.argc() > 1) - { - FSoundID id = argv[1]; - if (id == 0) - { - Printf("'%s' is not a sound\n", argv[1]); - } - else - { - AActor *icon = Spawn(primaryLevel, "SpeakerIcon", players[consoleplayer].mo->PosPlusZ(32.), ALLOW_REPLACE); - if (icon != NULL) - { - S_Sound(icon, CHAN_BODY | CHAN_LOOP, id, 1.f, ATTN_IDLE); - } - } - } -} - -//========================================================================== -// -// CCMD cachesound -// -//========================================================================== - -CCMD (cachesound) -{ - if (argv.argc() < 2) - { - Printf ("Usage: cachesound ...\n"); - return; - } - for (int i = 1; i < argv.argc(); ++i) - { - FSoundID sfxnum = argv[i]; - if (sfxnum != FSoundID(0)) - { - S_CacheSound (&S_sfx[sfxnum]); - } - } -} - - -CCMD(listsoundchannels) -{ - FSoundChan *chan; - int count = 0; - for (chan = Channels; chan != NULL; chan = chan->NextChan) - { - if (!(chan->ChanFlags & CHAN_EVICTED)) - { - FVector3 chanorigin; - - CalcPosVel(chan, &chanorigin, NULL); - - Printf("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)chan->SoundID, chanorigin.X, chanorigin.Y, chanorigin.Z); - count++; - } - } - Printf("%d sounds playing\n", count); -} - -// intentionally moved here to keep the s_music include out of the rest of the file. - -//========================================================================== -// -// S_PauseSound -// -// Stop music and sound effects, during game PAUSE. -//========================================================================== -#include "s_music.h" - -void S_PauseSound (bool notmusic, bool notsfx) -{ - if (!notmusic) - { - S_PauseMusic(); - } - if (!notsfx) - { - SoundPaused = true; - GSnd->SetSfxPaused (true, 0); - } -} - -DEFINE_ACTION_FUNCTION(DObject, S_PauseSound) -{ - PARAM_PROLOGUE; - PARAM_BOOL(notmusic); - PARAM_BOOL(notsfx); - S_PauseSound(notmusic, notsfx); - return 0; -} - -//========================================================================== -// -// S_ResumeSound -// -// Resume music and sound effects, after game PAUSE. -//========================================================================== - -void S_ResumeSound (bool notsfx) -{ - S_ResumeMusic(); - if (!notsfx) - { - SoundPaused = false; - GSnd->SetSfxPaused (false, 0); - } -} - -DEFINE_ACTION_FUNCTION(DObject, S_ResumeSound) -{ - PARAM_PROLOGUE; - PARAM_BOOL(notsfx); - S_ResumeSound(notsfx); - return 0; -} - - void S_UnloadAllSounds() { for (unsigned i = 0; i < S_sfx.Size(); i++) @@ -2598,32 +1979,11 @@ void S_UnloadAllSounds() } } -CCMD (snd_status) +void S_Reset() { - GSnd->PrintStatus (); -} - -CCMD (snd_reset) -{ - S_SoundReset(); -} - -void S_SoundReset() -{ - S_StopMusic(true); S_EvictAllChannels(); I_CloseSound(); I_InitSound(); - S_RestartMusic(); S_RestoreEvictedChannels(); } -CCMD (snd_listdrivers) -{ - GSnd->PrintDriversList (); -} - -ADD_STAT (sound) -{ - return GSnd->GatherStats (); -} diff --git a/src/sound/s_sound.h b/src/sound/s_sound.h index cf7b52900..18ab9c279 100644 --- a/src/sound/s_sound.h +++ b/src/sound/s_sound.h @@ -38,10 +38,6 @@ struct FLevelLocals; #include "s_soundinternal.h" -// Information about one playing sound. -struct sector_t; -struct FPolyObj; - // Per level startup code. // Kills playing sounds at start of level and starts new music. // @@ -52,59 +48,17 @@ void S_PrecacheLevel (FLevelLocals *l); // Start sound for thing at void S_Sound (int channel, FSoundID sfxid, float volume, float attenuation); -void S_Sound (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation); -void S_SoundMinMaxDist (AActor *ent, int channel, FSoundID sfxid, float volume, float mindist, float maxdist); -void S_Sound (const FPolyObj *poly, int channel, FSoundID sfxid, float volume, float attenuation); -void S_Sound (const sector_t *sec, int channel, FSoundID sfxid, float volume, float attenuation); -void S_Sound(FLevelLocals *Level, const DVector3 &pos, int channel, FSoundID sfxid, float volume, float attenuation); void S_SoundPitch (int channel, FSoundID sfxid, float volume, float attenuation, float pitch); -void S_SoundPitchActor (AActor *ent, int channel, FSoundID sfxid, float volume, float attenuation, float pitch); - -// [Nash] Used by ACS and DECORATE -void S_PlaySound(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local); -void S_PlaySoundPitch(AActor *a, int chan, FSoundID sid, float vol, float atten, bool local, float pitch); struct FSoundLoadBuffer; int S_PickReplacement (int refid); void S_CacheRandomSound (sfxinfo_t *sfx); -// Stops a sound emanating from one of an emitter's channels. -void S_StopSound (AActor *ent, int channel); -void S_StopSound (const sector_t *sec, int channel); -void S_StopSound (const FPolyObj *poly, int channel); - -// Stops an origin-less sound from playing from this channel. -void S_StopSound (int channel); - // Stop sound for all channels void S_StopAllChannels (void); - -// Is the sound playing on one of the emitter's channels? -bool S_GetSoundPlayingInfo (const AActor *actor, int sound_id); -bool S_GetSoundPlayingInfo (const sector_t *sector, int sound_id); -bool S_GetSoundPlayingInfo (const FPolyObj *poly, int sound_id); - -bool S_IsActorPlayingSomething (AActor *actor, int channel, int sound_id); - -// Change a playing sound's volume -void S_ChangeSoundVolume(AActor *actor, int channel, double volume); - -// Change a playing sound's pitch -void S_ChangeSoundPitch(AActor *actor, int channel, double pitch); void S_SetPitch(FSoundChan *chan, float dpitch); -// Moves all sounds from one mobj to another -void S_RelinkSound (AActor *from, AActor *to); - -// Stores/retrieves playing channel information in an archive. -void S_SerializeSounds(FSerializer &arc); - -// -// Updates music & sounds -// -void S_UpdateSounds (AActor *listener); - void S_RestoreEvictedChannels(); // [RH] S_sfx "maintenance" routines @@ -130,10 +84,11 @@ void S_ShrinkPlayerSoundLists (); void S_UnloadSound (sfxinfo_t *sfx); sfxinfo_t *S_LoadSound(sfxinfo_t *sfx, FSoundLoadBuffer *pBuffer = nullptr); unsigned int S_GetMSLength(FSoundID sound); -void A_PlaySound(AActor *self, int soundid, int channel, double volume, int looping, double attenuation, int local, double pitch); // [RH] Prints sound debug info to the screen. // Modelled after Hexen's noise cheat. void S_NoiseDebug (); +#include "s_doomsound.h" + #endif diff --git a/src/sound/s_soundinternal.h b/src/sound/s_soundinternal.h index 0c2dd6801..b8af56ef7 100644 --- a/src/sound/s_soundinternal.h +++ b/src/sound/s_soundinternal.h @@ -140,8 +140,6 @@ extern FRolloffInfo S_Rolloff; extern TArray S_SoundCurve; -typedef FVector3(*GetSourceOrigin)(const float *listenerpos, const void *source); - class AActor; struct sector_t; struct FPolyObj; @@ -160,14 +158,7 @@ struct FSoundChan : public FISoundChannel float LimitRange; union { - AActor *Actor; // Used for position and velocity. - const sector_t *Sector; // Sector for area sounds. - const FPolyObj *Poly; // Polyobject sound source. - struct - { - void *Source; - GetSourceOrigin getOrigin; - }; + const void *Source; float Point[3]; // Sound is not attached to any source. }; }; @@ -249,10 +240,40 @@ enum // Checks if a copy of this sound is already playing. bool S_CheckSingular (int sound_id); +enum // This cannot be remain as this, but for now it has to suffice. +{ + SOURCE_None, // Sound is always on top of the listener. + SOURCE_Actor, // Sound is coming from an actor. + SOURCE_Sector, // Sound is coming from a sector. + SOURCE_Polyobj, // Sound is coming from a polyobject. + SOURCE_Unattached, // Sound is not attached to any particular emitter. +}; + + +// +// Updates music & sounds +// +void S_UpdateSounds (int time); + +FSoundChan* S_StartSound(int sourcetype, const void* source, + const FVector3* pt, int channel, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f); + +// Stops an origin-less sound from playing from this channel. +void S_StopSound(int channel); +void S_StopSound(int sourcetype, const void* actor, int channel); + +void S_RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); +void S_ChangeSoundVolume(int sourcetype, const void *source, int channel, double dvolume); +void S_ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch); +bool S_IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id); + // Stop and resume music, during game PAUSE. void S_PauseSound (bool notmusic, bool notsfx); void S_ResumeSound (bool notsfx); void S_SetSoundPaused (int state); +bool S_GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id); +void S_UnloadAllSounds(); +void S_Reset(); extern ReverbContainer *Environments; extern ReverbContainer *DefaultEnvironments[26]; @@ -263,6 +284,4 @@ void S_SetEnvironment (const ReverbContainer *settings); ReverbContainer *S_FindEnvironment (const char *name); ReverbContainer *S_FindEnvironment (int id); void S_AddEnvironment (ReverbContainer *settings); -void S_UnloadAllSounds(); -void S_SoundReset();