#include "core/audio/audio.hpp" #include // Para SDL_GetError, SDL_Init #include // Para clamp #include // Para std::cout // Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp). // clang-format off #undef STB_VORBIS_HEADER_ONLY #include "external/stb_vorbis.c" // stb_vorbis.c filtra les macros L, C i R (i PLAYBACK_*) al TU. Les netegem // perquè xocarien amb noms de paràmetres de plantilla en altres headers. #undef L #undef C #undef R #undef PLAYBACK_MONO #undef PLAYBACK_LEFT #undef PLAYBACK_RIGHT // clang-format on #include "core/audio/audio_adapter.hpp" // Para AudioResource::getMusic/getSound #include "core/audio/jail_audio.hpp" // Para JA_* #include "game/options.hpp" // Para Options::audio // Singleton Audio* Audio::instance = nullptr; // Inicializa la instancia única del singleton void Audio::init() { Audio::instance = new Audio(); } // Libera la instancia void Audio::destroy() { delete Audio::instance; Audio::instance = nullptr; } // Obtiene la instancia auto Audio::get() -> Audio* { return Audio::instance; } // Constructor Audio::Audio() { initSDLAudio(); } // Destructor Audio::~Audio() { JA_Quit(); } // Método principal void Audio::update() { JA_Update(); // Sincronizar estado: detectar cuando la música se para (ej. fade-out completado) if (instance && instance->music_.state == MusicState::PLAYING && JA_GetMusicState() != JA_MUSIC_PLAYING) { instance->music_.state = MusicState::STOPPED; } } // Reproduce la música por nombre (con crossfade opcional) void Audio::playMusic(const std::string& name, const int loop, const int crossfade_ms) { bool new_loop = (loop != 0); // Si ya está sonando exactamente la misma pista y mismo modo loop, no hacemos nada if (music_.state == MusicState::PLAYING && music_.name == name && music_.loop == new_loop) { return; } if (!music_enabled_) return; auto* resource = AudioResource::getMusic(name); if (resource == nullptr) return; if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { JA_CrossfadeMusic(resource, crossfade_ms, loop); } else { if (music_.state == MusicState::PLAYING) { JA_StopMusic(); } JA_PlayMusic(resource, loop); } music_.name = name; music_.loop = new_loop; music_.state = MusicState::PLAYING; } // Reproduce la música por puntero (con crossfade opcional) void Audio::playMusic(JA_Music_t* music, const int loop, const int crossfade_ms) { if (!music_enabled_ || music == nullptr) return; if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { JA_CrossfadeMusic(music, crossfade_ms, loop); } else { if (music_.state == MusicState::PLAYING) { JA_StopMusic(); } JA_PlayMusic(music, loop); } music_.name.clear(); // nom desconegut quan es passa per punter music_.loop = (loop != 0); music_.state = MusicState::PLAYING; } // Pausa la música void Audio::pauseMusic() { if (music_enabled_ && music_.state == MusicState::PLAYING) { JA_PauseMusic(); music_.state = MusicState::PAUSED; } } // Continua la música pausada void Audio::resumeMusic() { if (music_enabled_ && music_.state == MusicState::PAUSED) { JA_ResumeMusic(); music_.state = MusicState::PLAYING; } } // Detiene la música void Audio::stopMusic() { if (music_enabled_) { JA_StopMusic(); music_.state = MusicState::STOPPED; } } // Reproduce un sonido por nombre void Audio::playSound(const std::string& name, Group group) const { if (sound_enabled_) { JA_PlaySound(AudioResource::getSound(name), 0, static_cast(group)); } } // Reproduce un sonido por puntero directo void Audio::playSound(JA_Sound_t* sound, Group group) const { if (sound_enabled_ && sound != nullptr) { JA_PlaySound(sound, 0, static_cast(group)); } } // Detiene todos los sonidos void Audio::stopAllSounds() const { if (sound_enabled_) { JA_StopChannel(-1); } } // Realiza un fundido de salida de la música void Audio::fadeOutMusic(int milliseconds) const { if (music_enabled_ && getRealMusicState() == MusicState::PLAYING) { JA_FadeOutMusic(milliseconds); } } // Consulta directamente el estado real de la música en jailaudio auto Audio::getRealMusicState() -> MusicState { JA_Music_state ja_state = JA_GetMusicState(); switch (ja_state) { case JA_MUSIC_PLAYING: return MusicState::PLAYING; case JA_MUSIC_PAUSED: return MusicState::PAUSED; case JA_MUSIC_STOPPED: case JA_MUSIC_INVALID: case JA_MUSIC_DISABLED: default: return MusicState::STOPPED; } } // Establece el volumen de los sonidos (float 0.0..1.0) void Audio::setSoundVolume(float sound_volume, Group group) const { if (sound_enabled_) { sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME); const float CONVERTED_VOLUME = sound_volume * Options::audio.volume; JA_SetSoundVolume(CONVERTED_VOLUME, static_cast(group)); } } // Establece el volumen de la música (float 0.0..1.0) void Audio::setMusicVolume(float music_volume) const { if (music_enabled_) { music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME); const float CONVERTED_VOLUME = music_volume * Options::audio.volume; JA_SetMusicVolume(CONVERTED_VOLUME); } } // Aplica la configuración void Audio::applySettings() { enable(Options::audio.enabled); } // Establecer estado general void Audio::enable(bool value) { enabled_ = value; setSoundVolume(enabled_ ? Options::audio.sound.volume : MIN_VOLUME); setMusicVolume(enabled_ ? Options::audio.music.volume : MIN_VOLUME); } // Inicializa SDL Audio void Audio::initSDLAudio() { if (!SDL_Init(SDL_INIT_AUDIO)) { std::cout << "SDL_AUDIO could not initialize! SDL Error: " << SDL_GetError() << '\n'; } else { JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2); enable(Options::audio.enabled); } }