| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- #include "AudioSystem.h"
- #include "MiniaudioBackend.h"
- #include "MusicPlayer.h"
- #include "Sound.h"
- #include <QDebug>
- #include <algorithm>
- #include <chrono>
- #include <cmath>
- #include <cstddef>
- #include <memory>
- #include <mutex>
- #include <qglobal.h>
- #include <string>
- #include <utility>
- #include <vector>
- AudioSystem::AudioSystem()
- : isRunning(false), masterVolume(AudioConstants::DEFAULT_VOLUME),
- soundVolume(AudioConstants::DEFAULT_VOLUME),
- musicVolume(AudioConstants::DEFAULT_VOLUME),
- voiceVolume(AudioConstants::DEFAULT_VOLUME) {}
- AudioSystem::~AudioSystem() { shutdown(); }
- auto AudioSystem::getInstance() -> AudioSystem & {
- static AudioSystem instance;
- return instance;
- }
- auto AudioSystem::initialize() -> bool {
- if (isRunning) {
- return true;
- }
- m_musicPlayer = &Game::Audio::MusicPlayer::getInstance();
- if (!m_musicPlayer->initialize()) {
- qWarning() << "Failed to initialize MusicPlayer";
- return false;
- }
- isRunning = true;
- audioThread = std::thread(&AudioSystem::audioThreadFunc, this);
- return true;
- }
- void AudioSystem::shutdown() {
- if (!isRunning) {
- return;
- }
- {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::SHUTDOWN);
- }
- queueCondition.notify_one();
- if (audioThread.joinable()) {
- audioThread.join();
- }
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->shutdown();
- m_musicPlayer = nullptr;
- }
- sounds.clear();
- soundCategories.clear();
- activeResources.clear();
- {
- std::lock_guard<std::mutex> const lock(activeSoundsMutex);
- activeSounds.clear();
- }
- }
- void AudioSystem::playSound(const std::string &soundId, float volume, bool loop,
- int priority, AudioCategory category) {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::PLAY_SOUND, soundId, volume, loop,
- priority, category);
- queueCondition.notify_one();
- }
- void AudioSystem::playMusic(const std::string &musicId, float volume, bool) {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::PLAY_MUSIC, musicId, volume);
- queueCondition.notify_one();
- }
- void AudioSystem::stopSound(const std::string &soundId) {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::STOP_SOUND, soundId);
- queueCondition.notify_one();
- }
- void AudioSystem::stopMusic() {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::STOP_MUSIC);
- queueCondition.notify_one();
- }
- void AudioSystem::setMasterVolume(float volume) {
- masterVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
- AudioConstants::MAX_VOLUME);
- std::lock_guard<std::mutex> const lock(resourceMutex);
- for (auto &sound : sounds) {
- auto it = soundCategories.find(sound.first);
- AudioCategory const category =
- (it != soundCategories.end()) ? it->second : AudioCategory::SFX;
- sound.second->set_volume(
- getEffectiveVolume(category, AudioConstants::DEFAULT_VOLUME));
- }
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->setVolume(masterVolume * musicVolume);
- }
- }
- void AudioSystem::setSoundVolume(float volume) {
- soundVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
- AudioConstants::MAX_VOLUME);
- std::lock_guard<std::mutex> const lock(resourceMutex);
- for (auto &sound : sounds) {
- auto it = soundCategories.find(sound.first);
- if (it != soundCategories.end() && it->second == AudioCategory::SFX) {
- sound.second->set_volume(getEffectiveVolume(
- AudioCategory::SFX, AudioConstants::DEFAULT_VOLUME));
- }
- }
- }
- void AudioSystem::setMusicVolume(float volume) {
- musicVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
- AudioConstants::MAX_VOLUME);
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->setVolume(masterVolume * musicVolume);
- }
- }
- void AudioSystem::setVoiceVolume(float volume) {
- voiceVolume = std::clamp(volume, AudioConstants::MIN_VOLUME,
- AudioConstants::MAX_VOLUME);
- std::lock_guard<std::mutex> const lock(resourceMutex);
- for (auto &sound : sounds) {
- auto it = soundCategories.find(sound.first);
- if (it != soundCategories.end() && it->second == AudioCategory::VOICE) {
- sound.second->set_volume(getEffectiveVolume(
- AudioCategory::VOICE, AudioConstants::DEFAULT_VOLUME));
- }
- }
- }
- void AudioSystem::pauseAll() {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::PAUSE);
- queueCondition.notify_one();
- }
- void AudioSystem::resumeAll() {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::RESUME);
- queueCondition.notify_one();
- }
- auto AudioSystem::loadSound(const std::string &soundId,
- const std::string &filePath,
- AudioCategory category) -> bool {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (sounds.find(soundId) != sounds.end()) {
- return true;
- }
- MiniaudioBackend *backend =
- (m_musicPlayer != nullptr) ? m_musicPlayer->getBackend() : nullptr;
- auto sound = std::make_unique<Sound>(filePath, backend);
- if (!sound || !sound->is_loaded()) {
- return false;
- }
- sounds[soundId] = std::move(sound);
- soundCategories[soundId] = category;
- activeResources.insert(soundId);
- return true;
- }
- auto AudioSystem::loadMusic(const std::string &musicId,
- const std::string &filePath) -> bool {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer == nullptr) {
- qWarning() << "MusicPlayer not initialized";
- return false;
- }
- m_musicPlayer->registerTrack(musicId, filePath);
- activeResources.insert(musicId);
- return true;
- }
- void AudioSystem::unloadSound(const std::string &soundId) {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::UNLOAD_RESOURCE, soundId);
- queueCondition.notify_one();
- }
- void AudioSystem::unloadMusic(const std::string &musicId) {
- std::lock_guard<std::mutex> const lock(queueMutex);
- eventQueue.emplace(AudioEventType::UNLOAD_RESOURCE, musicId);
- queueCondition.notify_one();
- }
- void AudioSystem::unloadAllSounds() {
- std::lock_guard<std::mutex> const lock(queueMutex);
- for (const auto &sound : sounds) {
- eventQueue.emplace(AudioEventType::UNLOAD_RESOURCE, sound.first);
- }
- queueCondition.notify_one();
- }
- void AudioSystem::unloadAllMusic() {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->stop();
- }
- std::vector<std::string> music_resources;
- for (const auto &res : activeResources) {
- if (sounds.find(res) == sounds.end()) {
- music_resources.push_back(res);
- }
- }
- for (const auto &res : music_resources) {
- activeResources.erase(res);
- }
- }
- void AudioSystem::setMaxChannels(size_t channels) {
- maxChannels = std::max(AudioConstants::MIN_CHANNELS, channels);
- }
- auto AudioSystem::getActiveChannelCount() const -> size_t {
- std::lock_guard<std::mutex> const lock(activeSoundsMutex);
- return activeSounds.size();
- }
- void AudioSystem::audioThreadFunc() {
- while (isRunning) {
- std::unique_lock<std::mutex> lock(queueMutex);
- queueCondition.wait(lock, [this] { return !eventQueue.empty(); });
- while (!eventQueue.empty()) {
- AudioEvent const event = eventQueue.front();
- eventQueue.pop();
- lock.unlock();
- processEvent(event);
- if (event.type == AudioEventType::SHUTDOWN) {
- isRunning = false;
- return;
- }
- lock.lock();
- }
- }
- }
- void AudioSystem::processEvent(const AudioEvent &event) {
- switch (event.type) {
- case AudioEventType::PLAY_SOUND: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- auto it = sounds.find(event.resourceId);
- if (it != sounds.end()) {
- if (!canPlaySound(event.priority)) {
- evictLowestPrioritySoundLocked();
- }
- float const effective_vol =
- getEffectiveVolume(event.category, event.volume);
- it->second->play(effective_vol, event.loop);
- {
- std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
- activeSounds.push_back({event.resourceId, event.priority, event.loop,
- event.category,
- std::chrono::steady_clock::now()});
- }
- }
- break;
- }
- case AudioEventType::PLAY_MUSIC: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer != nullptr) {
- float const effective_volume = masterVolume * musicVolume * event.volume;
- m_musicPlayer->play(event.resourceId, effective_volume, event.loop);
- }
- break;
- }
- case AudioEventType::STOP_SOUND: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- auto it = sounds.find(event.resourceId);
- if (it != sounds.end()) {
- it->second->stop();
- std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
- activeSounds.erase(std::remove_if(activeSounds.begin(),
- activeSounds.end(),
- [&](const ActiveSound &as) {
- return as.id == event.resourceId;
- }),
- activeSounds.end());
- }
- break;
- }
- case AudioEventType::STOP_MUSIC: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->stop();
- }
- break;
- }
- case AudioEventType::PAUSE: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->pause();
- }
- break;
- }
- case AudioEventType::RESUME: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- if (m_musicPlayer != nullptr) {
- m_musicPlayer->resume();
- }
- break;
- }
- case AudioEventType::UNLOAD_RESOURCE: {
- std::lock_guard<std::mutex> const lock(resourceMutex);
- auto sound_it = sounds.find(event.resourceId);
- if (sound_it != sounds.end()) {
- sound_it->second->stop();
- std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
- activeSounds.erase(std::remove_if(activeSounds.begin(),
- activeSounds.end(),
- [&](const ActiveSound &as) {
- return as.id == event.resourceId;
- }),
- activeSounds.end());
- sounds.erase(sound_it);
- soundCategories.erase(event.resourceId);
- activeResources.erase(event.resourceId);
- }
- activeResources.erase(event.resourceId);
- break;
- }
- case AudioEventType::CLEANUP_INACTIVE: {
- cleanupInactiveSounds();
- break;
- }
- case AudioEventType::SET_VOLUME:
- case AudioEventType::SHUTDOWN:
- break;
- }
- }
- auto AudioSystem::canPlaySound(int) -> bool {
- std::lock_guard<std::mutex> const lock(activeSoundsMutex);
- return activeSounds.size() < maxChannels;
- }
- void AudioSystem::evictLowestPrioritySound() {
- std::string sound_id_to_stop;
- {
- std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
- if (activeSounds.empty()) {
- return;
- }
- auto lowest_it =
- std::min_element(activeSounds.begin(), activeSounds.end(),
- [](const ActiveSound &a, const ActiveSound &b) {
- if (a.priority != b.priority) {
- return a.priority < b.priority;
- }
- return a.startTime < b.startTime;
- });
- if (lowest_it != activeSounds.end()) {
- sound_id_to_stop = lowest_it->id;
- activeSounds.erase(lowest_it);
- }
- }
- if (!sound_id_to_stop.empty()) {
- std::lock_guard<std::mutex> const resource_lock(resourceMutex);
- auto it = sounds.find(sound_id_to_stop);
- if (it != sounds.end()) {
- it->second->stop();
- }
- }
- }
- void AudioSystem::evictLowestPrioritySoundLocked() {
- std::string sound_id_to_stop;
- {
- std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
- if (activeSounds.empty()) {
- return;
- }
- auto lowest_it =
- std::min_element(activeSounds.begin(), activeSounds.end(),
- [](const ActiveSound &a, const ActiveSound &b) {
- if (a.priority != b.priority) {
- return a.priority < b.priority;
- }
- return a.startTime < b.startTime;
- });
- if (lowest_it != activeSounds.end()) {
- sound_id_to_stop = lowest_it->id;
- activeSounds.erase(lowest_it);
- }
- }
- if (!sound_id_to_stop.empty()) {
- auto it = sounds.find(sound_id_to_stop);
- if (it != sounds.end()) {
- it->second->stop();
- }
- }
- }
- void AudioSystem::cleanupInactiveSounds() {
- std::lock_guard<std::mutex> const resource_lock(resourceMutex);
- std::lock_guard<std::mutex> const active_lock(activeSoundsMutex);
- activeSounds.erase(std::remove_if(activeSounds.begin(), activeSounds.end(),
- [this](const ActiveSound &as) {
- if (as.loop) {
- return false;
- }
- auto it = sounds.find(as.id);
- return it == sounds.end();
- }),
- activeSounds.end());
- }
- auto AudioSystem::getEffectiveVolume(AudioCategory category,
- float eventVolume) const -> float {
- float category_volume = NAN;
- switch (category) {
- case AudioCategory::SFX:
- category_volume = soundVolume;
- break;
- case AudioCategory::VOICE:
- category_volume = voiceVolume;
- break;
- case AudioCategory::MUSIC:
- category_volume = musicVolume;
- break;
- default:
- category_volume = soundVolume;
- break;
- }
- return masterVolume * category_volume * eventVolume;
- }
|