From 87e8de27f38e793f9feaea4a3b0d8d98e4c9d011 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 4 Jan 2026 15:06:13 +0000 Subject: [PATCH] refactor: Implement SDL audio initialization, playback, and cleanup in SdlAudioService --- src/services/impl/sdl_audio_service.cpp | 165 +++++++++++++++++++++--- 1 file changed, 145 insertions(+), 20 deletions(-) diff --git a/src/services/impl/sdl_audio_service.cpp b/src/services/impl/sdl_audio_service.cpp index e5a8116..b60f838 100644 --- a/src/services/impl/sdl_audio_service.cpp +++ b/src/services/impl/sdl_audio_service.cpp @@ -1,5 +1,7 @@ #include "sdl_audio_service.hpp" #include +#include +#include namespace sdl3cpp::services::impl { @@ -19,9 +21,33 @@ void SdlAudioService::Initialize() { return; } - // TODO: Initialize SDL audio subsystem - logger_->Info("Audio service initialized (stub implementation)"); + // Initialize SDL audio + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) { + throw std::runtime_error("Failed to initialize SDL audio: " + std::string(SDL_GetError())); + } + + // Set up desired audio spec + SDL_AudioSpec desiredSpec; + SDL_zero(desiredSpec); + desiredSpec.freq = 44100; + desiredSpec.format = SDL_AUDIO_S16; + desiredSpec.channels = 2; + desiredSpec.samples = 4096; + desiredSpec.callback = AudioCallback; + desiredSpec.userdata = this; + + // Open audio device + audioDevice_ = SDL_OpenAudioDevice(nullptr, 0, &desiredSpec, &audioSpec_, 0); + if (audioDevice_ == 0) { + SDL_QuitSubSystem(SDL_INIT_AUDIO); + throw std::runtime_error("Failed to open audio device: " + std::string(SDL_GetError())); + } + + // Start audio playback + SDL_PlayAudioDevice(audioDevice_); + initialized_ = true; + logger_->Info("SDL audio service initialized successfully"); } void SdlAudioService::Shutdown() noexcept { @@ -31,9 +57,24 @@ void SdlAudioService::Shutdown() noexcept { return; } - // TODO: Shutdown SDL audio subsystem + // Stop and cleanup audio + if (audioDevice_ != 0) { + SDL_PauseAudioDevice(audioDevice_, 1); // Pause + SDL_CloseAudioDevice(audioDevice_); + audioDevice_ = 0; + } + + { + std::lock_guard lock(audioMutex_); + if (backgroundAudio_) { + CleanupAudioData(*backgroundAudio_); + backgroundAudio_.reset(); + } + } + + SDL_QuitSubSystem(SDL_INIT_AUDIO); initialized_ = false; - logger_->Info("Audio service shutdown (stub implementation)"); + logger_->Info("SDL audio service shutdown"); } void SdlAudioService::PlayBackground(const std::filesystem::path& path, bool loop) { @@ -43,8 +84,25 @@ void SdlAudioService::PlayBackground(const std::filesystem::path& path, bool loo throw std::runtime_error("Audio service not initialized"); } - // TODO: Implement background music playback using SDL_mixer or similar - logger_->Info("Playing background audio: " + path.string() + " (loop: " + std::to_string(loop) + ") - STUB"); + std::lock_guard lock(audioMutex_); + + // Stop current background audio + if (backgroundAudio_) { + CleanupAudioData(*backgroundAudio_); + backgroundAudio_.reset(); + } + + // Load new audio file + backgroundAudio_ = std::make_unique(); + if (!LoadAudioFile(path, *backgroundAudio_)) { + backgroundAudio_.reset(); + throw std::runtime_error("Failed to load audio file: " + path.string()); + } + + backgroundAudio_->loop = loop; + backgroundAudio_->position = 0; + + logger_->Info("Playing background audio: " + path.string() + " (loop: " + std::to_string(loop) + ")"); } void SdlAudioService::PlayEffect(const std::filesystem::path& path, bool loop) { @@ -54,8 +112,8 @@ void SdlAudioService::PlayEffect(const std::filesystem::path& path, bool loop) { throw std::runtime_error("Audio service not initialized"); } - // TODO: Implement sound effect playback - logger_->Info("Playing effect audio: " + path.string() + " (loop: " + std::to_string(loop) + ") - STUB"); + // For now, effects are not implemented - could be added later + logger_->Info("Playing effect audio: " + path.string() + " (loop: " + std::to_string(loop) + ") - NOT IMPLEMENTED"); } void SdlAudioService::StopBackground() { @@ -65,25 +123,26 @@ void SdlAudioService::StopBackground() { return; } - // TODO: Stop background music - logger_->Info("Stopping background audio - STUB"); + std::lock_guard lock(audioMutex_); + if (backgroundAudio_) { + CleanupAudioData(*backgroundAudio_); + backgroundAudio_.reset(); + } + + logger_->Info("Stopped background audio"); } void SdlAudioService::StopAll() { logger_->TraceFunction(__func__); - if (!initialized_) { - return; - } - - // TODO: Stop all audio - logger_->Info("Stopping all audio - STUB"); + StopBackground(); + // Effects would be stopped here too + logger_->Info("Stopped all audio"); } void SdlAudioService::SetVolume(float volume) { volume_ = std::clamp(volume, 0.0f, 1.0f); - // Note: AudioPlayer doesn't expose volume control, - // this would need to be added to AudioPlayer implementation + logger_->TraceVariable("volume", volume_); } float SdlAudioService::GetVolume() const { @@ -91,8 +150,74 @@ float SdlAudioService::GetVolume() const { } bool SdlAudioService::IsBackgroundPlaying() const { - // TODO: Check if background music is currently playing - return false; // Stub implementation + std::lock_guard lock(audioMutex_); + return backgroundAudio_ != nullptr; +} + +void SdlAudioService::AudioCallback(void* userdata, Uint8* stream, int len) { + auto* service = static_cast(userdata); + service->MixAudio(stream, len); +} + +void SdlAudioService::MixAudio(Uint8* stream, int len) { + std::lock_guard lock(audioMutex_); + + // Clear the stream + SDL_memset(stream, 0, len); + + if (!backgroundAudio_ || !backgroundAudio_->isOpen) { + return; + } + + // Read audio data from vorbis file + char buffer[4096]; + int bytesRead = 0; + int totalBytesRead = 0; + + while (totalBytesRead < len) { + bytesRead = ov_read(&backgroundAudio_->vorbisFile, buffer, std::min(4096, len - totalBytesRead), 0, 2, 1, nullptr); + if (bytesRead <= 0) { + // End of file + if (backgroundAudio_->loop) { + // Loop back to beginning + ov_pcm_seek(&backgroundAudio_->vorbisFile, 0); + continue; + } else { + // Stop playback + CleanupAudioData(*backgroundAudio_); + backgroundAudio_.reset(); + break; + } + } + + // Mix audio data (simple copy for now, could add volume mixing) + SDL_MixAudio(stream + totalBytesRead, reinterpret_cast(buffer), bytesRead, SDL_MIX_MAXVOLUME); + totalBytesRead += bytesRead; + } +} + +bool SdlAudioService::LoadAudioFile(const std::filesystem::path& path, AudioData& audioData) { + FILE* file = fopen(path.c_str(), "rb"); + if (!file) { + logger_->Error("Failed to open audio file: " + path.string()); + return false; + } + + if (ov_open(file, &audioData.vorbisFile, nullptr, 0) < 0) { + fclose(file); + logger_->Error("Failed to open vorbis file: " + path.string()); + return false; + } + + audioData.isOpen = true; + return true; +} + +void SdlAudioService::CleanupAudioData(AudioData& audioData) { + if (audioData.isOpen) { + ov_clear(&audioData.vorbisFile); + audioData.isOpen = false; + } } } // namespace sdl3cpp::services::impl