Air-Trap 1.0.0
A multiplayer R-Type clone game engine built with C++23 and ECS architecture
Loading...
Searching...
No Matches
AudioSystem.cpp
Go to the documentation of this file.
1
9#include "RType/Logger.hpp"
10
11namespace rtp::client {
12
14 : _registry(registry), _masterVolume(100.0f), _musicVolume(100.0f), _sfxVolume(100.0f), _nextSoundId(0) {}
15
19
20void AudioSystem::update(float dt) {
23}
24
25void AudioSystem::setMasterVolume(float volume) {
26 _masterVolume = std::clamp(volume, 0.0f, 100.0f);
27
28 // Update volume of all currently playing sounds
29 for (auto& [id, sound] : _activeSounds) {
30 float baseVolume = _soundBaseVolumes[id];
31 bool isMusic = _soundIsMusic[id];
32 float categoryVolume = isMusic ? _musicVolume : _sfxVolume;
33 sound->setVolume(baseVolume * _masterVolume * categoryVolume / 100.0f);
34 }
35}
36
38 return _masterVolume;
39}
40
41void AudioSystem::setMusicVolume(float volume) {
42 _musicVolume = std::clamp(volume, 0.0f, 100.0f);
43
44 // Update volume of all currently playing music
45 for (auto& [id, sound] : _activeSounds) {
46 if (_soundIsMusic[id]) {
47 float baseVolume = _soundBaseVolumes[id];
48 sound->setVolume(baseVolume * _masterVolume * _musicVolume / 100.0f);
49 }
50 }
51}
52
53void AudioSystem::setSfxVolume(float volume) {
54 _sfxVolume = std::clamp(volume, 0.0f, 100.0f);
55
56 // Update volume of all currently playing SFX
57 for (auto& [id, sound] : _activeSounds) {
58 if (!_soundIsMusic[id]) {
59 float baseVolume = _soundBaseVolumes[id];
60 sound->setVolume(baseVolume * _masterVolume * _sfxVolume / 100.0f);
61 }
62 }
63}
64
66 for (auto& [id, sound] : _activeSounds) {
67 sound->stop();
68 }
69 _activeSounds.clear();
70 _loopingSounds.clear();
71 _soundBaseVolumes.clear();
72 _soundIsMusic.clear();
73}
74
75sf::SoundBuffer* AudioSystem::loadSoundBuffer(const std::string& path) {
76 if (_soundBuffers.find(path) != _soundBuffers.end()) {
77 return _soundBuffers[path].get();
78 }
79
80 auto buffer = std::make_unique<sf::SoundBuffer>();
81 if (!buffer->loadFromFile(path)) {
82 log::error("Failed to load sound: {}", path);
83 return nullptr;
84 }
85
86 sf::SoundBuffer* result = buffer.get();
87 _soundBuffers[path] = std::move(buffer);
88 return result;
89}
90
92 try {
94
95 for (auto&& [audioSource] : view) {
96 if (audioSource.dirty) {
97 // Handle state changes
98 if (audioSource.isPlaying) {
99 playAudioSource(audioSource);
100 }
101 audioSource.dirty = false;
102 }
103 if (audioSource.loop) {
104 auto it = _activeSounds.find(audioSource.sourceId);
105 if (it != _activeSounds.end() && it->second->getStatus() == sf::Sound::Status::Stopped) {
106 it->second->play();
107 }
108 }
110 }
111 } catch (const std::exception& e) {
112 log::error("Error updating AudioSources: {}", e.what());
113 }
114}
115
117 try {
119
120 for (auto&& [soundEvent] : view) {
121 if (!soundEvent.played) {
122 log::info("Playing sound: {}", soundEvent.soundPath);
123 playSoundEffect(soundEvent);
124 soundEvent.played = true;
125 }
126 }
127 } catch (const std::exception& e) {
128 log::error("Error updating SoundEvents: {}", e.what());
129 }
130}
131
133 sf::SoundBuffer* buffer = loadSoundBuffer(audioSource.audioPath);
134 if (!buffer) return;
135
136 auto sound = std::make_unique<sf::Sound>(*buffer);
137 sound->setVolume(audioSource.volume * _masterVolume * _musicVolume / 100.0f);
138 sound->setPitch(audioSource.pitch);
139 sound->play();
140
141 audioSource.sourceId = _nextSoundId;
142 _loopingSounds[_nextSoundId] = audioSource.loop;
144 _soundIsMusic[_nextSoundId] = true; // AudioSource = Music
145 _activeSounds[_nextSoundId++] = std::move(sound);
146}
147
149 sf::SoundBuffer* buffer = loadSoundBuffer(soundEvent.soundPath);
150 if (!buffer) return;
151
152 auto sound = std::make_unique<sf::Sound>(*buffer);
153 sound->setVolume(soundEvent.volume * _masterVolume * _sfxVolume / 100.0f);
154 sound->setPitch(soundEvent.pitch);
155 sound->play();
156
158 _soundIsMusic[_nextSoundId] = false; // SoundEvent = SFX
159 _activeSounds[_nextSoundId++] = std::move(sound);
160}
161
163 for (auto it = _activeSounds.begin(); it != _activeSounds.end();) {
164 const uint32_t id = it->first;
165 const bool isLooping = (_loopingSounds.find(id) != _loopingSounds.end() && _loopingSounds[id]);
166 if (it->second->getStatus() == sf::Sound::Status::Stopped && !isLooping) {
167 it = _activeSounds.erase(it);
168 _loopingSounds.erase(id);
169 _soundBaseVolumes.erase(id);
170 _soundIsMusic.erase(id);
171 } else {
172 ++it;
173 }
174 }
175}
176
177} // namespace rtp::client
Logger declaration with support for multiple log levels.
void setSfxVolume(float volume)
Set SFX volume (affects SoundEvent components)
void updateAudioSources(float dt)
Update all AudioSource components.
AudioSystem(ecs::Registry &registry)
ecs::Registry & _registry
void update(float dt) override
Update audio system (called every frame)
void stopAllSounds()
Stop all currently playing sounds.
std::unordered_map< uint32_t, bool > _soundIsMusic
Track if sound is music (true) or SFX (false)
std::unordered_map< uint32_t, float > _soundBaseVolumes
Track base volume (0.0-1.0) for each sound.
void cleanupFinishedSounds()
Remove finished non-looping sounds from tracking.
float getMasterVolume() const
Get current master volume.
std::unordered_map< uint32_t, std::unique_ptr< sf::Sound > > _activeSounds
void setMusicVolume(float volume)
Set music volume (affects AudioSource components)
void updateSoundEvents(float dt)
Update all SoundEvent components.
void playSoundEffect(ecs::components::audio::SoundEvent &soundEvent)
Play a sound effect (one-time)
std::unordered_map< std::string, std::unique_ptr< sf::SoundBuffer > > _soundBuffers
std::unordered_map< uint32_t, bool > _loopingSounds
Track which sounds should loop.
void playAudioSource(ecs::components::audio::AudioSource &audioSource)
Play an AudioSource.
void setMasterVolume(float volume)
Set master volume for all audio.
sf::SoundBuffer * loadSoundBuffer(const std::string &path)
Load or get cached sound buffer.
auto zipView(this Self &self)
R-Type client namespace.
void error(LogFmt< std::type_identity_t< Args >... > fmt, Args &&...args) noexcept
Log an error message.
void info(LogFmt< std::type_identity_t< Args >... > fmt, Args &&...args) noexcept
Log an informational message.
Component for entities that emit continuous sound (music, loops, ambient)
std::string audioPath
Path to the audio file.
uint32_t sourceId
Internal audio source identifier.
float volume
Volume level (0.0 - 1.0)
bool loop
Whether the audio should loop.
Component for triggering one-time sound effects (SFX)
float volume
Volume level (0.0 - 1.0)
std::string soundPath
Path to the sound file.