diff --git a/CMakeLists.txt b/CMakeLists.txt index ea98e8e..d346a26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,9 @@ configure_file(${CMAKE_SOURCE_DIR}/source/version.h.in ${CMAKE_BINARY_DIR}/versi # --- 1. LISTA EXPLÍCITA DE FUENTES --- set(APP_SOURCES + # Core - Audio + source/core/audio/audio.cpp + # Core - Input source/core/input/global_inputs.cpp source/core/input/input.cpp diff --git a/source/core/audio/audio.cpp b/source/core/audio/audio.cpp new file mode 100644 index 0000000..ab2d86e --- /dev/null +++ b/source/core/audio/audio.cpp @@ -0,0 +1,170 @@ +#include "audio.hpp" + +#include // Para SDL_LogInfo, SDL_LogCategory, SDL_G... + +#include // Para clamp +#include // Para std::cout + +#include "core/resources/resource.hpp" // Para Resource +#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM... +#include "game/options.hpp" // Para AudioOptions, audio, MusicOptions + +// 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; } + +// 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(); +} + +// Reproduce la música +void Audio::playMusic(const std::string& name, const int loop) { + bool newLoop = (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 == newLoop) { + return; + } + + // Intentar obtener recurso; si falla, no tocar estado + auto* resource = Resource::get()->getMusic(name); + if (!resource) { + // manejo de error opcional + return; + } + + // Si hay algo reproduciéndose, detenerlo primero (si el backend lo requiere) + if (music_.state == MusicState::PLAYING) { + JA_StopMusic(); // sustituir por la función de stop real del API si tiene otro nombre + } + + // Llamada al motor para reproducir la nueva pista + JA_PlayMusic(resource, loop); + + // Actualizar estado y metadatos después de iniciar con éxito + music_.name = name; + music_.loop = newLoop; + 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 +void Audio::playSound(const std::string& name, Group group) const { + if (sound_enabled_) { + JA_PlaySound(Resource::get()->getSound(name), 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 +void Audio::setSoundVolume(int sound_volume, Group group) const { + if (sound_enabled_) { + sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME); + const float CONVERTED_VOLUME = (sound_volume / 100.0F) * (Options::audio.volume / 100.0F); + JA_SetSoundVolume(CONVERTED_VOLUME, static_cast(group)); + } +} + +// Establece el volumen de la música +void Audio::setMusicVolume(int music_volume) const { + if (music_enabled_) { + music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME); + const float CONVERTED_VOLUME = (music_volume / 100.0F) * (Options::audio.volume / 100.0F); + 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)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError()); + } else { + JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2); + enable(Options::audio.enabled); + + std::cout << "Audio system initialized successfully\n"; + } +} \ No newline at end of file diff --git a/source/core/audio/audio.hpp b/source/core/audio/audio.hpp new file mode 100644 index 0000000..adaef96 --- /dev/null +++ b/source/core/audio/audio.hpp @@ -0,0 +1,111 @@ +#pragma once + +#include // Para string +#include // Para move + +// --- Clase Audio: gestor de audio (singleton) --- +class Audio { + public: + // --- Enums --- + enum class Group : int { + ALL = -1, // Todos los grupos + GAME = 0, // Sonidos del juego + INTERFACE = 1 // Sonidos de la interfaz + }; + + enum class MusicState { + PLAYING, // Reproduciendo música + PAUSED, // Música pausada + STOPPED, // Música detenida + }; + + // --- Constantes --- + static constexpr int MAX_VOLUME = 100; // Volumen máximo + static constexpr int MIN_VOLUME = 0; // Volumen mínimo + static constexpr int FREQUENCY = 48000; // Frecuencia de audio + + // --- Métodos de singleton --- + static void init(); // Inicializa el objeto Audio + static void destroy(); // Libera el objeto Audio + static auto get() -> Audio*; // Obtiene el puntero al objeto Audio + Audio(const Audio&) = delete; // Evitar copia + auto operator=(const Audio&) -> Audio& = delete; // Evitar asignación + + // --- Método principal --- + static void update(); + + // --- Control de Música --- + void playMusic(const std::string& name, int loop = -1); // Reproducir música en bucle + void pauseMusic(); // Pausar reproducción de música + void resumeMusic(); // Continua la música pausada + void stopMusic(); // Detener completamente la música + void fadeOutMusic(int milliseconds) const; // Fundido de salida de la música + + // --- Control de Sonidos --- + void playSound(const std::string& name, Group group = Group::GAME) const; // Reproducir sonido puntual + void stopAllSounds() const; // Detener todos los sonidos + + // --- Configuración General --- + void enable(bool value); // Establecer estado general + void toggleEnabled() { enabled_ = !enabled_; } // Alternar estado general + void applySettings(); // Aplica la configuración + + // --- Configuración de Sonidos --- + void enableSound() { sound_enabled_ = true; } // Habilitar sonidos + void disableSound() { sound_enabled_ = false; } // Deshabilitar sonidos + void enableSound(bool value) { sound_enabled_ = value; } // Establecer estado de sonidos + void toggleSound() { sound_enabled_ = !sound_enabled_; } // Alternar estado de sonidos + + // --- Configuración de Música --- + void enableMusic() { music_enabled_ = true; } // Habilitar música + void disableMusic() { music_enabled_ = false; } // Deshabilitar música + void enableMusic(bool value) { music_enabled_ = value; } // Establecer estado de música + void toggleMusic() { music_enabled_ = !music_enabled_; } // Alternar estado de música + + // --- Control de Volumen --- + void setSoundVolume(int volume, Group group = Group::ALL) const; // Ajustar volumen de efectos + void setMusicVolume(int volume) const; // Ajustar volumen de música + + // --- Getters para debug --- + [[nodiscard]] auto isEnabled() const -> bool { return enabled_; } + [[nodiscard]] auto isSoundEnabled() const -> bool { return sound_enabled_; } + [[nodiscard]] auto isMusicEnabled() const -> bool { return music_enabled_; } + [[nodiscard]] auto getMusicState() const -> MusicState { return music_.state; } + [[nodiscard]] static auto getRealMusicState() -> MusicState; // Consulta directamente a jailaudio + [[nodiscard]] auto getCurrentMusicName() const -> const std::string& { return music_.name; } + + private: + // --- Estructuras privadas --- + struct Music { + MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa) + std::string name; // Última pista de música reproducida + bool loop; // Indica si la última pista de música se debe reproducir en bucle + + // Constructor para inicializar la música con valores predeterminados + Music() + : state(MusicState::STOPPED), + loop(false) {} + + // Constructor para inicializar con valores específicos + Music(MusicState init_state, std::string init_name, bool init_loop) + : state(init_state), + name(std::move(init_name)), + loop(init_loop) {} + }; + + // --- Variables de estado --- + Music music_; // Estado de la música + bool enabled_ = true; // Estado general del audio + bool sound_enabled_ = true; // Estado de los efectos de sonido + bool music_enabled_ = true; // Estado de la música + + // --- Métodos internos --- + void initSDLAudio(); // Inicializa SDL Audio + + // --- Constructores y destructor privados (singleton) --- + Audio(); // Constructor privado + ~Audio(); // Destructor privado + + // --- Instancia singleton --- + static Audio* instance; // Instancia única de Audio +}; \ No newline at end of file