329ae7a38e
Pas 7 final del hallazgo #28. La capa de game/options havia esdevingut exclusivament una capa de persistència YAML que llegia i escrivia Config::EngineConfig. El nom "Options" no reflectia bé aquest rol. Canvis: - Renomenats fitxers: game/options.{hpp,cpp} → game/config_yaml.{hpp,cpp} (preservant la història de git via mv). - Renomenat namespace: Options → ConfigYaml. - Esborrades del .hpp les referències-alias inline (window, rendering, player1, player2, keyboard_controls, gamepad_controls, console) que ja no tenien call-sites externs (només existien per a la transició). El .hpp ara només exposa engine_config + version + path + funcions. - A config_yaml.cpp s'introdueixen aliases internes (anonymous namespace) per mantenir llegible el codi de la implementació, sense exposar-les. - Actualitzat main.cpp per a usar ConfigYaml::*. - Actualitzats els comentaris stale a sdl_manager.hpp, director.hpp, engine_config.hpp i audio.hpp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
163 lines
8.9 KiB
C++
163 lines
8.9 KiB
C++
#pragma once
|
||
|
||
#include <cmath> // Para std::lround
|
||
#include <cstdint> // Para int8_t, uint8_t
|
||
#include <functional> // Para std::function
|
||
#include <memory> // Para std::unique_ptr
|
||
#include <string> // Para string
|
||
|
||
// Forward-declares per no incloure core/audio/jail_audio.hpp al header. Els
|
||
// tres símbols (Music/Sound para el punter que exposa la API i Engine per al
|
||
// std::unique_ptr<Engine> membre) s'usen solo per punter al header, así que
|
||
// el forward-decl basta. El ~Audio() en .cpp veu la definició completa i
|
||
// instancia correctament el dtor de l'unique_ptr.
|
||
namespace Ja {
|
||
class Engine;
|
||
struct Music;
|
||
struct Sound;
|
||
} // namespace Ja
|
||
|
||
// --- Clase Audio: gestor d'àudio (singleton) ---
|
||
// Port del subsistema d'àudio del projecte ../aee, desacoblat d'Options:
|
||
// la configuración entra per la struct Audio::Config a init()/applySettings(),
|
||
// en lloc de llegir directament ConfigYaml::*. Això deixa audio.cpp independent
|
||
// del layout d'Options i permet substituir la font de configuración.
|
||
//
|
||
// Els volums es manegen internament como a float 0.0–1.0; la capa de
|
||
// presentació (menús, notificacions) usa las helpers toPercent/fromPercent
|
||
// per mostrar 0–100 a l'usuari.
|
||
class Audio {
|
||
public:
|
||
// --- Configuración injectada (Options la construeix via buildAudioConfig) ---
|
||
struct Config {
|
||
bool enabled{true};
|
||
float volume{1.0F}; // Master 0..1
|
||
bool music_enabled{true};
|
||
float music_volume{0.8F};
|
||
bool sound_enabled{true};
|
||
float sound_volume{1.0F};
|
||
};
|
||
|
||
// --- Enums ---
|
||
enum class Group : std::int8_t {
|
||
ALL = -1, // Tots los grups
|
||
GAME = 0, // Sons del joc
|
||
INTERFACE = 1 // Sons de la interfície
|
||
};
|
||
|
||
enum class MusicState : std::uint8_t {
|
||
PLAYING, // Reproduint música
|
||
PAUSED, // Música pausada
|
||
STOPPED, // Música aturada
|
||
};
|
||
|
||
// --- Constants ---
|
||
static constexpr float MAX_VOLUME = 1.0F; // Volum màxim (float 0..1)
|
||
static constexpr float MIN_VOLUME = 0.0F; // Volum mínim (float 0..1)
|
||
|
||
// --- Singleton ---
|
||
static void init(const Config& config); // Inicialitza con la configuración rebuda
|
||
static void destroy(); // Allibera l'objecte Audio
|
||
static auto get() -> Audio*; // Obté el punter a l'objecte Audio
|
||
~Audio(); // Destructor (públic para std::unique_ptr)
|
||
Audio(const Audio&) = delete; // Evitar còpia
|
||
Audio(Audio&&) = delete;
|
||
auto operator=(const Audio&) -> Audio& = delete; // Evitar assignació
|
||
auto operator=(Audio&&) -> Audio& = delete;
|
||
|
||
static void update(); // Actualització del sistema d'àudio
|
||
|
||
// --- Control de música ---
|
||
void playMusic(const std::string& name, int loop = -1, int crossfade_ms = 0); // Reproduir música per nom (amb crossfade opcional)
|
||
void playMusic(Ja::Music* music, int loop = -1, int crossfade_ms = 0); // Reproduir música per punter (amb crossfade opcional)
|
||
void pauseMusic(); // Pausar la reproducció de música
|
||
void resumeMusic(); // Continua la música pausada
|
||
void stopMusic(); // Aturar completament la música
|
||
void fadeOutMusic(int milliseconds); // Fosa de sortida de la música (muta globals de Ja)
|
||
void setOnMusicEnded(std::function<void()> callback); // Callback disparat cuando la pista actual acaba de drenar (CONV-03)
|
||
// Multiplicador de velocitat de la música actual. 1.0 = normal,
|
||
// 1.5 = un 50% més ràpid (efecte "chipmunk" — también puja el to).
|
||
// Es reseteja a 1.0 implícitament a cada `playMusic`. No-op si no
|
||
// hay música activa.
|
||
void setMusicSpeed(float ratio);
|
||
|
||
// --- Control de sons ---
|
||
void playSound(const std::string& name, Group group = Group::GAME); // Reproduir so puntual per nom (muta globals de Ja)
|
||
void playSound(Ja::Sound* sound, Group group = Group::GAME); // Reproduir so puntual per punter (muta globals de Ja)
|
||
// Reprodueix un so con la velocitat (i to) escalats per `speed`:
|
||
// 1.0 = normal, 0.95 ≈ -5% (més greu i lent), 1.05 ≈ +5% (més
|
||
// agut i ràpid). Mateixa semàntica que `setMusicSpeed`. Útil per a
|
||
// variacions subtils que eviten la fatiga d'escoltar el mismo
|
||
// sample idèntic (p.ex. obertures de sarcòfag, picks d'ítems).
|
||
void playSound(const std::string& name, Group group, float speed);
|
||
// Reprodueix un so processat per un efecte definit a data/config/sounds.yaml
|
||
// (preset_name busca a SoundEffectsConfig). Si el preset no existeix
|
||
// o el motor está al sin de canals con efecte, fa fallback a playSound
|
||
// sec — l'usuari sent el so igualment, sin la cua.
|
||
void playSoundWithEcho(const std::string& name, const std::string& preset_name, Group group = Group::GAME);
|
||
void playSoundWithReverb(const std::string& name, const std::string& preset_name, Group group = Group::GAME);
|
||
void stopAllSounds(); // Aturar tots los sons (muta globals de Ja)
|
||
|
||
// --- Control de volum (API interna: float 0.0..1.0) ---
|
||
void setSoundVolume(float volume, Group group = Group::ALL); // Ajusta el volum dels efectes
|
||
void setMusicVolume(float volume); // Ajusta el volum de la música
|
||
|
||
// --- Helpers de conversió para la capa de presentació ---
|
||
// UI (menús, notificacions) manega enters 0..100; internament viu float 0..1.
|
||
// No són constexpr porque std::lround no ho es en C++20; s'usen en runtime.
|
||
static auto toPercent(float volume) -> int {
|
||
return static_cast<int>(std::lround(volume * 100.0F));
|
||
}
|
||
static auto fromPercent(int percent) -> float {
|
||
return static_cast<float>(percent) / 100.0F;
|
||
}
|
||
|
||
// --- Configuración general ---
|
||
void enable(bool value); // Estableix l'estat general (reaplica volums)
|
||
void toggleEnabled() { enable(!enabled_); } // Alterna l'estat general (reaplica volums)
|
||
void applySettings(const Config& config); // Aplica una nueva configuración
|
||
|
||
// --- Configuración de sons ---
|
||
void enableSound(bool value); // Estableix l'estat dels sons (reaplica volum)
|
||
void toggleSound() { enableSound(!sound_enabled_); } // Alterna l'estat dels sons (reaplica volum)
|
||
|
||
// --- Configuración de música ---
|
||
void enableMusic(bool value); // Estableix l'estat de la música (reaplica volum)
|
||
void toggleMusic() { enableMusic(!music_enabled_); } // Alterna l'estat de la música (reaplica volum)
|
||
|
||
// --- Consultes d'estat ---
|
||
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
|
||
[[nodiscard]] auto isSoundEnabled() const -> bool { return sound_enabled_; }
|
||
[[nodiscard]] auto isMusicEnabled() const -> bool { return music_enabled_; }
|
||
[[nodiscard]] static auto getMusicState() -> MusicState; // Estat real consultat a Ja::
|
||
[[nodiscard]] auto getCurrentMusicName() const -> const std::string& { return music_.name; }
|
||
// Duración de la pista resolta per nom (mil·lisegons). 0 si la pista no
|
||
// existeix al cache de recursos o si el seu header OGG no permet
|
||
// calcular-la. Pensat para clients que necessiten un timeline
|
||
// determinista (p. ex. RoomFsm) sin dependre de callbacks de fi.
|
||
[[nodiscard]] static auto getMusicDurationMs(const std::string& name) -> int;
|
||
|
||
private:
|
||
// --- Tipus anidats ---
|
||
struct Music {
|
||
std::string name; // Última pista de música reproduïda (buida si es va passar per punter sin filename)
|
||
bool loop{false}; // Si el play actual es en bucle
|
||
};
|
||
|
||
// --- Mètodes ---
|
||
explicit Audio(const Config& config); // Constructor privat: rep la config
|
||
void initSDLAudio(); // Inicialitza SDL Audio
|
||
void playMusicInternal(Ja::Music* music, int loop, int crossfade_ms); // Camí comú dels dos overloads de playMusic
|
||
[[nodiscard]] auto effectiveVolume(float volume, bool channel_enabled) const -> float; // Gate master+channel: 0 si algun está off, clamp 0..1 altrament
|
||
|
||
// --- Variables membre ---
|
||
static std::unique_ptr<Audio> instance; // Instància única d'Audio
|
||
|
||
std::unique_ptr<Ja::Engine> engine_; // Motor de baix nivell (owned); viu mentre Audio viu.
|
||
Config config_{}; // Configuración injectada (volums, enables)
|
||
Music music_; // Estat de la música (nom + loop cachejats)
|
||
bool enabled_{true}; // Estat general de l'àudio
|
||
bool sound_enabled_{true}; // Estat dels efectes de so
|
||
bool music_enabled_{true}; // Estat de la música
|
||
};
|