Files
orni-attack/source/core/audio/audio.hpp
T

177 lines
9.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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.01.0; la capa de
// presentació (menús, notificacions) usa las helpers toPercent/fromPercent
// per mostrar 0100 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
void setMasterVolume(float volume); // Ajusta el master (re-aplica sound + music)
// Getters dels volums actuals (lectura de la config_ cachejada). Reflexen
// el valor que l'usuari ha triat l'última vegada, independent del gating
// d'enabled/channel.
[[nodiscard]] auto getMasterVolume() const -> float { return config_.volume; }
[[nodiscard]] auto getSoundVolume() const -> float { return config_.sound_volume; }
[[nodiscard]] auto getMusicVolume() const -> float { return config_.music_volume; }
// --- 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)
// Silencia (o restaura) un únic grup de sons sense tocar el volum cachejat
// de l'usuari ni la resta de grups. Pensat per a l'attract/demo: vol callar
// els SFX de joc (Group::GAME) pero mantenir els del menu de servei
// (Group::INTERFACE) i la música. En restaurar, reaplica el volum efectiu
// normal del canal (que ja respecta els gates master/sound).
void silenceGroup(Group group, bool silenced);
// --- 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
};