group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
SfxSystem Class Reference

Client-side sound effects system. More...

#include <SfxSystem.hpp>

Collaboration diagram for SfxSystem:
[legend]

Classes

struct  Source

Public Types

using SourceHandle = std::uint32_t

Public Member Functions

bool init ()
 Initialise SDL audio subsystem, open default playback device, load all clips.
void update (float dt)
 Per-frame update: decrement cooldowns and retire finished voices.
void update (float dt, const Registry &registry)
 Per-frame update: decrement cooldowns, retire finished voices, detect state changes.
void quit ()
 Destroy active sources, close the audio device, quit audio subsystem.
void handleEvent (const SDL_Event &event)
 Forward SDL audio-device events so the system can handle hot-swap.
void play (SfxId id, float gain=1.0f)
 Play a sound immediately.
SourceHandle play2D (SfxId id, float gain=1.0f, float priority=1.0f)
SourceHandle playUi (UiSoundAction action, float gain=1.0f)
SourceHandle play3D (SfxId id, const glm::vec3 &position, const glm::vec3 &velocity=glm::vec3{0.0f}, float gain=1.0f, float priority=1.0f)
SourceHandle startLoop (SfxId id, bool positional=false, const glm::vec3 &position=glm::vec3{0.0f}, float gain=1.0f, float priority=1.0f)
void updateSource (SourceHandle handle, const glm::vec3 &position, const glm::vec3 &velocity=glm::vec3{0.0f}, float gain=1.0f)
void stopSource (SourceHandle handle)
void playMusic (SfxId id, float gain=1.0f)
void stopMusic ()
void setListener (const audio::ListenerState &listener)
void setAudioObjectTransform (audio::AudioObjectId object, const glm::vec3 &position, const glm::vec3 &velocity=glm::vec3{0.0f})
void removeAudioObject (audio::AudioObjectId object)
void setAudioRtpc (audio::AudioObjectId object, audio::RtpcId rtpc, float value)
void setAudioSwitch (audio::AudioObjectId object, audio::SwitchGroupId group, audio::SwitchValueId value)
void setAudioState (audio::StateGroupId group, audio::StateValueId value)
void setAudioBusVolume (audio::AudioBusId bus, float volume)
bool reloadAudioManifest ()
SourceHandle postAudioEvent (std::string_view eventName, audio::AudioObjectId object=audio::kGlobalObject, float gain=1.0f)
SourceHandle postLocalAudioEvent (std::string_view eventName, audio::AudioObjectId object=audio::kGlobalObject, float gain=1.0f)
void submitVoiceFrame (ClientId speaker, std::uint16_t sequence, std::span< const float > monoPcm48k, const glm::vec3 &position, const glm::vec3 &velocity=glm::vec3{0.0f})
void stop (SfxId id)
 Stop all active voices playing the given sound.
void setMasterVolume (float v)
void setCategoryVolume (SfxCategory cat, float v)
void setPlaybackDeviceName (std::string_view name)
float masterVolume () const
float categoryVolume (SfxCategory cat) const
void onWeaponFired (const WeaponFiredEvent &e)
void onExplosion (const ExplosionEvent &e)
bool isInitialized () const
 True after a successful init().
const audio::AudioRuntimeaudioRuntime () const noexcept
const audio::AudioRuntimeStatsaudioStats () const noexcept
const SfxRuntimeStatssfxStats () const noexcept
float clipDuration (SfxId id) const noexcept
std::uint32_t activeSourceCount () const noexcept
std::uint32_t activeVoiceSourceCount () const noexcept

Static Public Attributes

static constexpr SourceHandle kInvalidSource = 0

Private Member Functions

bool loadClip (SfxId id, const char *filename, SfxCategory cat, float gain, float cooldownSecs)
 Decode a single MP3 from assets/sounds/ and store it as clip[id].
SourceacquireSource (float priority, SfxId id=SfxId::_Count, audio::AudioBusId bus=audio::kInvalidBus, std::uint16_t maxInstances=0, std::uint16_t maxBusInstances=0)
SourcefindSource (SourceHandle handle)
SourcefindVoiceSource (ClientId speaker)
SourceHandle startSource (SfxId id, bool positional, bool loop, const glm::vec3 &position, const glm::vec3 &velocity, float gain, float priority, audio::AudioBusId bus, float busGain, std::uint16_t maxInstances, std::uint16_t maxBusInstances, float cooldownOverrideSeconds=-1.0f, float fullGainDistance=audio::k_fullGainDistance, float silentDistance=audio::k_silentDistance)
SourceHandle playCommand (const audio::AudioCommand &command)
float effectiveGain (SfxId id, float extraGain) const
 master × category × clip × extraGain.
void convertClipToMixer (SoundClip &clip, const char *debugName)
void synthesizeClip (SfxId id, SfxCategory cat, float gain, float cooldownSecs)
bool isOccluded (const glm::vec3 &position) const
void mixIntoStream (SDL_AudioStream *stream, int additionalAmount)
bool openDevice ()
 Open a specific physical playback device (macOS) or the default (other platforms).
void reopenDevice ()
 Tear down all active voices/streams, close the device, then reopen.
void warmUpDevice ()
 Push a tiny silent buffer to force AudioQueue buffer pre-allocation.
void convertClipsToMixer ()

Static Private Member Functions

static void mixCallback (void *userdata, SDL_AudioStream *stream, int additionalAmount, int totalAmount)

Private Attributes

SDL_AudioDeviceID device_ = 0
 Logical playback device (0 = not initialised).
SDL_AudioDeviceID physicalDeviceId_ = 0
 Logical device ID we opened (for event matching).
SDL_AudioStream * mixStream_ = nullptr
 Single SDL stream that receives our mixed stereo output.
SDL_AudioSpec mixerSpec_ {}
std::array< SoundClip, static_cast< size_t >(SfxId::_Count)> clips_
std::array< Source, kMaxSourcessources_ {}
SourceHandle nextSourceHandle_ = 1
std::unordered_map< int, SourceHandlevoiceSources_
SourceHandle musicHandle_ = kInvalidSource
SfxId currentMusic_ = SfxId::_Count
float masterVolume_ = 0.8f
std::array< float, static_cast< size_t >(SfxCategory::_Count)> categoryVolumes_ {}
audio::AudioRuntime audioRuntime_
SfxRuntimeStats sfxStats_ {}
std::string manifestPath_
std::string playbackDeviceName_
audio::ListenerState listener_ {}
std::array< float, 48000 > reverbDelayL_ {}
std::array< float, 48000 > reverbDelayR_ {}
std::size_t reverbWrite_ = 0
float deathMuffleTarget_ = 0.0f
float deathMuffleAmount_ = 0.0f
float deathMuffleStateL_ = 0.0f
float deathMuffleStateR_ = 0.0f
std::mutex mixerMutex_
std::array< float, static_cast< size_t >(SfxId::_Count)> cooldowns_ {}
 Per-SfxId countdown to next allowed play (seconds remaining).
std::array< float, static_cast< size_t >(UiSoundAction::_Count)> uiActionCooldowns_ {}
std::array< std::size_t, static_cast< size_t >(UiSoundAction::_Count)> uiActionVariantCursors_ {}
float prevHealth_ = 100.0f
float prevArmor_ = 100.0f
int prevDeaths_ = 0
int prevKills_ = 0
bool prevLocalReloading_ = false
bool prevLocalRailgunCharging_ = false
std::unordered_map< entt::entity, float > prevGrenadeCooldowns_
std::unordered_map< entt::entity, bool > knownFireFields_
float healingSoundCooldown_ = 0.0f
 Throttle the looping heal tick sound.
bool stateInitialized_ = false
 Skip sounds on the very first update().
bool pendingReopen_ = false
 Set by handleEvent(), processed at the start of update().

Static Private Attributes

static constexpr int kMaxSources = 64
static constexpr std::size_t kMaxVoiceQueuedFrames = 48000 / 2

Detailed Description

Client-side sound effects system.

All audio is local to the client — the server never drives sounds directly. Weapon-fire SFX hook into the existing WeaponFiredEvent on the dispatcher. Health/death/kill changes are detected by comparing previous-frame state in update(), because the server's handleDeath() immediately calls handleRespawn() in the same tick so IsDead never survives to a registry sync.

On macOS, device hot-swap (e.g. plugging/unplugging headphones) is handled via SDL_EVENT_AUDIO_DEVICE_REMOVED / ADDED events which trigger a graceful reopen of the default playback device.

Member Typedef Documentation

◆ SourceHandle

using SfxSystem::SourceHandle = std::uint32_t

Member Function Documentation

◆ acquireSource()

SfxSystem::Source * SfxSystem::acquireSource ( float priority,
SfxId id = SfxId::_Count,
audio::AudioBusId bus = audio::kInvalidBus,
std::uint16_t maxInstances = 0,
std::uint16_t maxBusInstances = 0 )
private
Here is the caller graph for this function:

◆ activeSourceCount()

std::uint32_t SfxSystem::activeSourceCount ( ) const
nodiscardnoexcept

◆ activeVoiceSourceCount()

std::uint32_t SfxSystem::activeVoiceSourceCount ( ) const
nodiscardnoexcept

◆ audioRuntime()

const audio::AudioRuntime & SfxSystem::audioRuntime ( ) const
inlinenodiscardnoexcept

◆ audioStats()

const audio::AudioRuntimeStats & SfxSystem::audioStats ( ) const
inlinenodiscardnoexcept

◆ categoryVolume()

float SfxSystem::categoryVolume ( SfxCategory cat) const

◆ clipDuration()

float SfxSystem::clipDuration ( SfxId id) const
nodiscardnoexcept

◆ convertClipsToMixer()

void SfxSystem::convertClipsToMixer ( )
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ convertClipToMixer()

void SfxSystem::convertClipToMixer ( SoundClip & clip,
const char * debugName )
private
Here is the caller graph for this function:

◆ effectiveGain()

float SfxSystem::effectiveGain ( SfxId id,
float extraGain ) const
private

master × category × clip × extraGain.

Here is the caller graph for this function:

◆ findSource()

SfxSystem::Source * SfxSystem::findSource ( SourceHandle handle)
private
Here is the caller graph for this function:

◆ findVoiceSource()

SfxSystem::Source * SfxSystem::findVoiceSource ( ClientId speaker)
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ handleEvent()

void SfxSystem::handleEvent ( const SDL_Event & event)

Forward SDL audio-device events so the system can handle hot-swap.

Call this from Game::event() for SDL_EVENT_AUDIO_DEVICE_ADDED, SDL_EVENT_AUDIO_DEVICE_REMOVED, and SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED.

◆ init()

bool SfxSystem::init ( )

Initialise SDL audio subsystem, open default playback device, load all clips.

Returns
True on success; false on any audio-init failure (non-fatal — game continues mute).
Here is the call graph for this function:

◆ isInitialized()

bool SfxSystem::isInitialized ( ) const
inline

True after a successful init().

Here is the caller graph for this function:

◆ isOccluded()

bool SfxSystem::isOccluded ( const glm::vec3 & position) const
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ loadClip()

bool SfxSystem::loadClip ( SfxId id,
const char * filename,
SfxCategory cat,
float gain,
float cooldownSecs )
private

Decode a single MP3 from assets/sounds/ and store it as clip[id].

Here is the caller graph for this function:

◆ masterVolume()

float SfxSystem::masterVolume ( ) const
inline

◆ mixCallback()

void SfxSystem::mixCallback ( void * userdata,
SDL_AudioStream * stream,
int additionalAmount,
int totalAmount )
staticprivate
Here is the call graph for this function:
Here is the caller graph for this function:

◆ mixIntoStream()

void SfxSystem::mixIntoStream ( SDL_AudioStream * stream,
int additionalAmount )
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ onExplosion()

void SfxSystem::onExplosion ( const ExplosionEvent & e)
Here is the call graph for this function:

◆ onWeaponFired()

void SfxSystem::onWeaponFired ( const WeaponFiredEvent & e)
Here is the call graph for this function:

◆ openDevice()

bool SfxSystem::openDevice ( )
private

Open a specific physical playback device (macOS) or the default (other platforms).

Returns
True on success.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ play()

void SfxSystem::play ( SfxId id,
float gain = 1.0f )

Play a sound immediately.

Parameters
idWhich clip to play.
gainExtra volume multiplier (stacks on top of master × category × clip gain).
Here is the call graph for this function:

◆ play2D()

SfxSystem::SourceHandle SfxSystem::play2D ( SfxId id,
float gain = 1.0f,
float priority = 1.0f )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ play3D()

SfxSystem::SourceHandle SfxSystem::play3D ( SfxId id,
const glm::vec3 & position,
const glm::vec3 & velocity = glm::vec3{0.0f},
float gain = 1.0f,
float priority = 1.0f )
Here is the call graph for this function:

◆ playCommand()

SfxSystem::SourceHandle SfxSystem::playCommand ( const audio::AudioCommand & command)
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ playMusic()

void SfxSystem::playMusic ( SfxId id,
float gain = 1.0f )
Here is the call graph for this function:

◆ playUi()

SfxSystem::SourceHandle SfxSystem::playUi ( UiSoundAction action,
float gain = 1.0f )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ postAudioEvent()

SfxSystem::SourceHandle SfxSystem::postAudioEvent ( std::string_view eventName,
audio::AudioObjectId object = audio::kGlobalObject,
float gain = 1.0f )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ postLocalAudioEvent()

SfxSystem::SourceHandle SfxSystem::postLocalAudioEvent ( std::string_view eventName,
audio::AudioObjectId object = audio::kGlobalObject,
float gain = 1.0f )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ quit()

void SfxSystem::quit ( )

Destroy active sources, close the audio device, quit audio subsystem.

◆ reloadAudioManifest()

bool SfxSystem::reloadAudioManifest ( )

◆ removeAudioObject()

void SfxSystem::removeAudioObject ( audio::AudioObjectId object)

◆ reopenDevice()

void SfxSystem::reopenDevice ( )
private

Tear down all active voices/streams, close the device, then reopen.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setAudioBusVolume()

void SfxSystem::setAudioBusVolume ( audio::AudioBusId bus,
float volume )

◆ setAudioObjectTransform()

void SfxSystem::setAudioObjectTransform ( audio::AudioObjectId object,
const glm::vec3 & position,
const glm::vec3 & velocity = glm::vec3{0.0f} )
Here is the caller graph for this function:

◆ setAudioRtpc()

void SfxSystem::setAudioRtpc ( audio::AudioObjectId object,
audio::RtpcId rtpc,
float value )

◆ setAudioState()

void SfxSystem::setAudioState ( audio::StateGroupId group,
audio::StateValueId value )

◆ setAudioSwitch()

void SfxSystem::setAudioSwitch ( audio::AudioObjectId object,
audio::SwitchGroupId group,
audio::SwitchValueId value )

◆ setCategoryVolume()

void SfxSystem::setCategoryVolume ( SfxCategory cat,
float v )

◆ setListener()

void SfxSystem::setListener ( const audio::ListenerState & listener)
Here is the call graph for this function:

◆ setMasterVolume()

void SfxSystem::setMasterVolume ( float v)
inline

◆ setPlaybackDeviceName()

void SfxSystem::setPlaybackDeviceName ( std::string_view name)

◆ sfxStats()

const SfxRuntimeStats & SfxSystem::sfxStats ( ) const
inlinenodiscardnoexcept

◆ startLoop()

SfxSystem::SourceHandle SfxSystem::startLoop ( SfxId id,
bool positional = false,
const glm::vec3 & position = glm::vec3{0.0f},
float gain = 1.0f,
float priority = 1.0f )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ startSource()

SfxSystem::SourceHandle SfxSystem::startSource ( SfxId id,
bool positional,
bool loop,
const glm::vec3 & position,
const glm::vec3 & velocity,
float gain,
float priority,
audio::AudioBusId bus,
float busGain,
std::uint16_t maxInstances,
std::uint16_t maxBusInstances,
float cooldownOverrideSeconds = -1.0f,
float fullGainDistance = audio::k_fullGainDistance,
float silentDistance = audio::k_silentDistance )
private
Here is the call graph for this function:
Here is the caller graph for this function:

◆ stop()

void SfxSystem::stop ( SfxId id)

Stop all active voices playing the given sound.

Here is the caller graph for this function:

◆ stopMusic()

void SfxSystem::stopMusic ( )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ stopSource()

void SfxSystem::stopSource ( SourceHandle handle)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ submitVoiceFrame()

void SfxSystem::submitVoiceFrame ( ClientId speaker,
std::uint16_t sequence,
std::span< const float > monoPcm48k,
const glm::vec3 & position,
const glm::vec3 & velocity = glm::vec3{0.0f} )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ synthesizeClip()

void SfxSystem::synthesizeClip ( SfxId id,
SfxCategory cat,
float gain,
float cooldownSecs )
private
Here is the caller graph for this function:

◆ update() [1/2]

void SfxSystem::update ( float dt)

Per-frame update: decrement cooldowns and retire finished voices.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update() [2/2]

void SfxSystem::update ( float dt,
const Registry & registry )

Per-frame update: decrement cooldowns, retire finished voices, detect state changes.

Parameters
dtFrame delta time in seconds.
registryECS registry (read-only — used to detect health/death/kill changes).
Here is the call graph for this function:

◆ updateSource()

void SfxSystem::updateSource ( SourceHandle handle,
const glm::vec3 & position,
const glm::vec3 & velocity = glm::vec3{0.0f},
float gain = 1.0f )
Here is the call graph for this function:

◆ warmUpDevice()

void SfxSystem::warmUpDevice ( )
private

Push a tiny silent buffer to force AudioQueue buffer pre-allocation.

Without this, the first real sound on macOS triggers lazy buffer allocation inside CoreAudio's AudioQueue, causing an audible latency spike / glitch.

Here is the caller graph for this function:

Member Data Documentation

◆ audioRuntime_

audio::AudioRuntime SfxSystem::audioRuntime_
private

◆ categoryVolumes_

std::array<float, static_cast<size_t>(SfxCategory::_Count)> SfxSystem::categoryVolumes_ {}
private

◆ clips_

std::array<SoundClip, static_cast<size_t>(SfxId::_Count)> SfxSystem::clips_
private

◆ cooldowns_

std::array<float, static_cast<size_t>(SfxId::_Count)> SfxSystem::cooldowns_ {}
private

Per-SfxId countdown to next allowed play (seconds remaining).

◆ currentMusic_

SfxId SfxSystem::currentMusic_ = SfxId::_Count
private

◆ deathMuffleAmount_

float SfxSystem::deathMuffleAmount_ = 0.0f
private

◆ deathMuffleStateL_

float SfxSystem::deathMuffleStateL_ = 0.0f
private

◆ deathMuffleStateR_

float SfxSystem::deathMuffleStateR_ = 0.0f
private

◆ deathMuffleTarget_

float SfxSystem::deathMuffleTarget_ = 0.0f
private

◆ device_

SDL_AudioDeviceID SfxSystem::device_ = 0
private

Logical playback device (0 = not initialised).

◆ healingSoundCooldown_

float SfxSystem::healingSoundCooldown_ = 0.0f
private

Throttle the looping heal tick sound.

◆ kInvalidSource

SourceHandle SfxSystem::kInvalidSource = 0
staticconstexpr

◆ kMaxSources

int SfxSystem::kMaxSources = 64
staticconstexprprivate

◆ kMaxVoiceQueuedFrames

std::size_t SfxSystem::kMaxVoiceQueuedFrames = 48000 / 2
staticconstexprprivate

◆ knownFireFields_

std::unordered_map<entt::entity, bool> SfxSystem::knownFireFields_
private

◆ listener_

audio::ListenerState SfxSystem::listener_ {}
private

◆ manifestPath_

std::string SfxSystem::manifestPath_
private

◆ masterVolume_

float SfxSystem::masterVolume_ = 0.8f
private

◆ mixerMutex_

std::mutex SfxSystem::mixerMutex_
mutableprivate

◆ mixerSpec_

SDL_AudioSpec SfxSystem::mixerSpec_ {}
private

◆ mixStream_

SDL_AudioStream* SfxSystem::mixStream_ = nullptr
private

Single SDL stream that receives our mixed stereo output.

◆ musicHandle_

SourceHandle SfxSystem::musicHandle_ = kInvalidSource
private

◆ nextSourceHandle_

SourceHandle SfxSystem::nextSourceHandle_ = 1
private

◆ pendingReopen_

bool SfxSystem::pendingReopen_ = false
private

Set by handleEvent(), processed at the start of update().

◆ physicalDeviceId_

SDL_AudioDeviceID SfxSystem::physicalDeviceId_ = 0
private

Logical device ID we opened (for event matching).

◆ playbackDeviceName_

std::string SfxSystem::playbackDeviceName_
private

◆ prevArmor_

float SfxSystem::prevArmor_ = 100.0f
private

◆ prevDeaths_

int SfxSystem::prevDeaths_ = 0
private

◆ prevGrenadeCooldowns_

std::unordered_map<entt::entity, float> SfxSystem::prevGrenadeCooldowns_
private

◆ prevHealth_

float SfxSystem::prevHealth_ = 100.0f
private

◆ prevKills_

int SfxSystem::prevKills_ = 0
private

◆ prevLocalRailgunCharging_

bool SfxSystem::prevLocalRailgunCharging_ = false
private

◆ prevLocalReloading_

bool SfxSystem::prevLocalReloading_ = false
private

◆ reverbDelayL_

std::array<float, 48000> SfxSystem::reverbDelayL_ {}
private

◆ reverbDelayR_

std::array<float, 48000> SfxSystem::reverbDelayR_ {}
private

◆ reverbWrite_

std::size_t SfxSystem::reverbWrite_ = 0
private

◆ sfxStats_

SfxRuntimeStats SfxSystem::sfxStats_ {}
private

◆ sources_

std::array<Source, kMaxSources> SfxSystem::sources_ {}
private

◆ stateInitialized_

bool SfxSystem::stateInitialized_ = false
private

Skip sounds on the very first update().

◆ uiActionCooldowns_

std::array<float, static_cast<size_t>(UiSoundAction::_Count)> SfxSystem::uiActionCooldowns_ {}
private

◆ uiActionVariantCursors_

std::array<std::size_t, static_cast<size_t>(UiSoundAction::_Count)> SfxSystem::uiActionVariantCursors_ {}
private

◆ voiceSources_

std::unordered_map<int, SourceHandle> SfxSystem::voiceSources_
private

The documentation for this class was generated from the following files: