#include "AudioManager.h" #include "src/Core/Logger.h" #include "src/FileSystem/FileSystem.h" #include #include "soloud_speech.h" #include "soloud_thread.h" #include #include namespace Nuake { AudioManager::AudioManager() { } AudioManager::~AudioManager() { Deinitialize(); m_Soloud->deinit(); } void AudioManager::Initialize() { m_Soloud = CreateRef(); // TODO: Sample rate, back end, buffer size, flags. #ifdef NK_WIN m_Soloud->init(SoLoud::Soloud::LEFT_HANDED_3D); #else m_Soloud->init(SoLoud::Soloud::CLIP_ROUNDOFF, SoLoud::Soloud::ALSA); #endif Logger::Log("Audio manager initialized", "audio", VERBOSE); } void AudioManager::Deinitialize() { m_WavSamples.clear(); m_ActiveClips.clear(); m_Soloud->deinit(); } void AudioManager::SetListenerPosition(const Vector3& position, const Vector3& direction, const Vector3& up) { if (position != m_ListenerPosition || direction != m_ListenerDirection || up != m_ListenerUp) { m_ListenerPosition = position; m_ListenerDirection = direction; m_ListenerUp = up; m_Soloud->set3dListenerParameters(m_ListenerPosition.x, m_ListenerPosition.y, m_ListenerPosition.z, m_ListenerDirection.x, m_ListenerDirection.y, m_ListenerDirection.z, m_ListenerUp.x, m_ListenerUp.y, m_ListenerUp.z ); m_Soloud->update3dAudio(); } } void AudioManager::PlayTTS(const std::string& text) { } void AudioManager::QueueWavAudio(const AudioRequest& request) { // Check if file exists and load const bool fileExists = FileSystem::FileExists(request.audioFile, true); if (fileExists && !IsWavLoaded(request.audioFile)) { LoadWavAudio(request.audioFile); } m_AudioQueue.push(request); } void AudioManager::UpdateVoice(const AudioRequest& request) { auto& audioClip = m_ActiveClips[request.audioFile]; if (IsVoiceActive(request.audioFile)) { if (!m_Soloud->isValidVoiceHandle(audioClip)) { m_ActiveClips.erase(request.audioFile); return; } } if (request.spatialized) { m_Soloud->set3dSourcePosition(audioClip, request.position.x, request.position.y, request.position.z); m_Soloud->set3dSourceMinMaxDistance(audioClip, request.MinDistance, request.MaxDistance); m_Soloud->set3dSourceAttenuation(audioClip, 1, request.AttenuationFactor); m_Soloud->update3dAudio(); } else { m_Soloud->setPan(audioClip, request.pan); } m_Soloud->setLooping(audioClip, request.Loop); m_Soloud->setRelativePlaySpeed(audioClip, request.speed); } void AudioManager::StopVoice(const std::string& filePath) { if (!IsVoiceActive(filePath)) { return; // We can't stop a voice that isn't active. } SoLoud::handle& voice = m_ActiveClips[filePath]; m_Soloud->stop(voice); } void AudioManager::StopAll() const { m_Soloud->stopAll(); } bool AudioManager::IsWavLoaded(const std::string& filePath) const { return m_WavSamples.find(filePath) != m_WavSamples.end(); } bool AudioManager::IsVoiceActive(const std::string& voice) const { return m_ActiveClips.find(voice) != m_ActiveClips.end(); } void AudioManager::LoadWavAudio(const std::string& filePath) { const bool STREAMING = false; if (STREAMING) { Ref wavStream = CreateRef(); wavStream->load(filePath.c_str()); m_WavSamples[filePath] = wavStream; } else { auto wav = CreateRef(); wav->load(filePath.c_str()); m_WavSamples[filePath] = wav; } } void AudioManager::AudioUpdate() { // Check if we have audio queued if (m_Soloud->getGlobalVolume() != m_GlobalVolume) { m_Soloud->setGlobalVolume(m_GlobalVolume); } while (!m_AudioQueue.empty()) { AudioRequest& audioRequest = m_AudioQueue.front(); Ref audio = m_WavSamples[audioRequest.audioFile]; SoLoud::handle handle; if (!audioRequest.spatialized) { handle = m_Soloud->play(*audio); m_Soloud->setVolume(handle, audioRequest.volume); m_Soloud->setPan(handle, audioRequest.pan); } else { const Vector3& position = audioRequest.position; handle = m_Soloud->play3d(*audio, position.x, position.y, position.z); m_Soloud->set3dSourceMinMaxDistance(handle, audioRequest.MinDistance, audioRequest.MaxDistance); m_Soloud->set3dSourceAttenuation(handle, SoLoud::AudioSource::ATTENUATION_MODELS::EXPONENTIAL_DISTANCE, audioRequest.AttenuationFactor); } m_Soloud->setRelativePlaySpeed(handle, audioRequest.speed); m_Soloud->setLooping(handle, audioRequest.Loop); m_ActiveClips[audioRequest.audioFile] = handle; m_AudioQueue.pop(); } } void AudioManager::CleanupInactiveVoices() { if (m_ActiveClips.empty()) { return; } std::erase_if(m_ActiveClips, [this](const auto& item) { return !m_Soloud->isValidVoiceHandle(item.second); }); } }