Files
aee-arcade/source/core/audio/audio.hpp
T
JailDesignerandClaude Opus 4.7 961a59c460 refactor(core/audio): Audio owns the engine (migration)
Audio guanya un std::unique_ptr<Ja::Engine> engine_ com a membre privat.
Es construeix a initSDLAudio amb els defaults de Defaults::Audio; es
destrueix sol al ~Audio (cap Ja::quit() explícit).

Totes les crides Ja::funcióN(...) d'audio.cpp passen a engine_->funcióN:
  - playMusicInternal: crossfadeMusic/stopMusic/playMusic
  - pauseMusic, resumeMusic, stopMusic, fadeOutMusic
  - playSound x2, stopAllSounds
  - setSoundVolume, setMusicVolume
  - getMusicState (via instance->engine_ — és static però pot arribar-hi)

Les free functions forwarder del commit 2 (Ja::init/quit/update/playMusic/
pauseMusic/resumeMusic/stopMusic/fadeOutMusic/crossfadeMusic/
getMusicState/setMusicVolume/enableMusic i els equivalents de so) es
retiren del .hpp i del .cpp: ja no les crida ningú.

Queden com a free functions permanents (les usa Resource::Cache):
loadMusic (x2), loadSound, deleteMusic, deleteSound. Aquests darrers
consulten Engine::active() per notificar el motor abans d'alliberar.

audio.hpp forward-declara també `class Ja::Engine` perquè engine_ és
un std::unique_ptr i el dtor es defineix out-of-line al .cpp (= default)
— així no cal incloure jail_audio.hpp al header.

Els NOLINT(readability-make-member-function-const) d'audio.cpp es
mantenen amb raó actualitzada: std::unique_ptr::operator-> retorna T*
(no const T*), per tant clang-tidy veu un mètode d'Audio que podria ser
const encara que muti l'Engine via el punter. La mutació és real; el
NOLINT segueix sent honest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:25:03 +02:00

140 lines
7.2 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> // Per a std::lround
#include <cstdint> // Per a int8_t, uint8_t
#include <memory> // Per a std::unique_ptr
#include <string> // Per a string
#include <utility> // Per a move
// Forward-declares per no incloure core/audio/jail_audio.hpp al header. Els
// tres símbols (Music/Sound per al punter que exposa la API i Engine per al
// std::unique_ptr<Engine> membre) s'usen només per punter al header, així 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
// --- Classe Audio: gestor d'àudio (singleton) ---
// Port del subsistema d'àudio del projecte ../aee, desacoblat d'Options:
// la configuració entra per la struct Audio::Config a init()/applySettings(),
// en lloc de llegir directament Options::audio. Això deixa audio.cpp independent
// del layout d'Options i permet substituir la font de configuració.
//
// Els volums es manegen internament com a float 0.0–1.0; la capa de
// presentació (menús, notificacions) usa les helpers toPercent/fromPercent
// per mostrar 0–100 a l'usuari.
class Audio {
public:
// --- Configuració 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 els 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 amb la configuració rebuda
static void destroy(); // Allibera l'objecte Audio
static auto get() -> Audio*; // Obté el punter a l'objecte Audio
~Audio(); // Destructor (públic per a 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)
// --- 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)
void stopAllSounds(); // Aturar tots els 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ó per a la capa de presentació ---
// UI (menús, notificacions) manega enters 0..100; internament viu float 0..1.
// No són constexpr perquè std::lround no ho és 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ó 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 nova configuració
// --- Configuració 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ó 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; }
private:
// --- Tipus anidats ---
struct Music {
std::string name; // Última pista de música reproduïda (buida si es va passar per punter sense filename)
bool loop{false}; // Si el play actual és 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ó 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
};