refactor(core/audio): Ja::* és l'única font de veritat de MusicState (INCONS-02)

music_.state era una cache d'estat que podia divergir del real —
precisament per això existia getRealMusicState() com a "consulta honesta".
Es descarta la cache:

  - music_.state deixa d'existir; el struct privat Music només manté
    name i loop.
  - getMusicState() passa a ser static i consulta Ja::getMusicState(),
    projectant INVALID/DISABLED al STOPPED que exposa Audio::MusicState.
  - getRealMusicState() desapareix (getMusicState ja fa la consulta real).
  - update() ja no necessita el bloc de sincronització.
  - playMusicInternal/pauseMusic/resumeMusic/stopMusic/fadeOutMusic
    llegeixen getMusicState() en comptes de music_.state i deixen de
    reassignar-lo — Ja ja ho fa.

Externament: cap caller usa getMusicState/getRealMusicState, així que
canviar a static és segur (Audio::get()->getMusicState() continua
funcionant).

pauseMusic/resumeMusic/stopMusic es marquen amb NOLINT per
readability-make-member-function-const pel mateix motiu que INCONS-01
(muten globals de Ja::* que clang-tidy no veu).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
JailDesignerandClaude Opus 4.7 committed 2026-04-20 12:15:49 +02:00
1 parent 964ff104ce
commit 66c5801725
3 files changed
+28 -32

No files matched your search

+2 -2
View File
@@ -20,7 +20,7 @@ Los IDs se mantienen estables una vez asignados. Cada entrada apunta al(los) fic
|----|--------|--------| |----|--------|--------|
| COUP-01 | Encapsular globals `Ja::` en clase (engine sin estado global) | PENDING | | COUP-01 | Encapsular globals `Ja::` en clase (engine sin estado global) | PENDING |
| HC-02 | Formato/canales/freq hardcodeado en 3+ sitios | DONE | | HC-02 | Formato/canales/freq hardcodeado en 3+ sitios | DONE |
| INCONS-02 | Doble `MusicState` + `getRealMusicState` como cache paralelo | PENDING | | INCONS-02 | Doble `MusicState` + `getRealMusicState` como cache paralelo | DONE |
| HC-01 | Parámetros de audio no configurables (viola convención del proyecto) | DONE | | HC-01 | Parámetros de audio no configurables (viola convención del proyecto) | DONE |
| PATCH-01 | `toggleEnabled/toggleSound/toggleMusic` no reaplican volumen | DONE | | PATCH-01 | `toggleEnabled/toggleSound/toggleMusic` no reaplican volumen | DONE |
@@ -71,7 +71,7 @@ Los IDs se mantienen estables una vez asignados. Cada entrada apunta al(los) fic
| ID | Fichero:línea | Hallazgo | Severidad | Estado | Notas | | ID | Fichero:línea | Hallazgo | Severidad | Estado | Notas |
|----|---------------|----------|-----------|--------|-------| |----|---------------|----------|-----------|--------|-------|
| INCONS-01 | `audio.cpp:128,135,142,149,172,180` | Métodos `const` que mutan globals de `Ja` (const miente) | Importante | DONE | `const` tret de 6 mètodes (`playSound` x2, `stopAllSounds`, `fadeOutMusic`, `setSoundVolume`, `setMusicVolume`). `effectiveVolume` es queda `const` (pur sobre membres). Cap caller extern via `const Audio*` | | INCONS-01 | `audio.cpp:128,135,142,149,172,180` | Métodos `const` que mutan globals de `Ja` (const miente) | Importante | DONE | `const` tret de 6 mètodes (`playSound` x2, `stopAllSounds`, `fadeOutMusic`, `setSoundVolume`, `setMusicVolume`). `effectiveVolume` es queda `const` (pur sobre membres). Cap caller extern via `const Audio*` |
| INCONS-02 | `audio.hpp:44-48` + `jail_audio.hpp:44-50` | Dos `MusicState` paralelos + `getRealMusicState` como puente | Importante | PENDING | Un enum único; eliminar `getRealMusicState` | | INCONS-02 | `audio.hpp:44-48` + `jail_audio.hpp:44-50` | Dos `MusicState` paralelos + `getRealMusicState` como puente | Importante | DONE | Eliminat `music_.state` (cache) i `getRealMusicState`. `getMusicState()` consulta Ja::* directament i projecta INVALID/DISABLED → STOPPED. La font de veritat passa a ser Ja. `update()` ja no necessita sincronització |
| INCONS-03 | `audio.cpp` varios | Comentarios mezclan catalán y español | Menor | PENDING | Unificar a catalán | | INCONS-03 | `audio.cpp` varios | Comentarios mezclan catalán y español | Menor | PENDING | Unificar a catalán |
| INCONS-04 | `audio.hpp:70,78` | `playMusic` `loop=-1` vs `playSound` `loop=0` | Menor | WONTFIX | Convención razonable (música loop / sonido one-shot); documentar | | INCONS-04 | `audio.hpp:70,78` | `playMusic` `loop=-1` vs `playSound` `loop=0` | Menor | WONTFIX | Convención razonable (música loop / sonido one-shot); documentar |
| INCONS-05 | `audio.hpp:102-105` | 4 formas de enable/disable sound (API redundante) + divergencia con `Ja::enableSound` | Importante | PENDING | Dejar `enableSound(bool)` + `toggleSound()`; delegar a `Ja` | | INCONS-05 | `audio.hpp:102-105` | 4 formas de enable/disable sound (API redundante) + divergencia con `Ja::enableSound` | Importante | PENDING | Dejar `enableSound(bool)` + `toggleSound()`; delegar a `Ja` |
+23 -25
View File
@@ -51,14 +51,10 @@ Audio::~Audio() {
Ja::quit(); Ja::quit();
} }
// Método principal // Método principal: l'estat de la música el manté Ja (única font de veritat),
// per tant no cal cap sincronització aquí.
void Audio::update() { void Audio::update() {
Ja::update(); Ja::update();
// Sincronitzar estat: detectar quan la música s'ha parat (p. ex. fade-out completat)
if (instance && instance->music_.state == MusicState::PLAYING && Ja::getMusicState() != Ja::MusicState::PLAYING) {
instance->music_.state = MusicState::STOPPED;
}
} }
// Reproduce la música por nombre (con crossfade opcional) // Reproduce la música por nombre (con crossfade opcional)
@@ -66,7 +62,7 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
const bool NEW_LOOP = (loop != 0); const bool NEW_LOOP = (loop != 0);
// Si ja sona exactament la mateixa pista i mateix mode loop, no fem res // Si ja sona exactament la mateixa pista i mateix mode loop, no fem res
if (music_.state == MusicState::PLAYING && music_.name == name && music_.loop == NEW_LOOP) { if (getMusicState() == MusicState::PLAYING && music_.name == name && music_.loop == NEW_LOOP) {
return; return;
} }
@@ -88,43 +84,44 @@ void Audio::playMusic(Ja::Music* music, const int loop, const int crossfade_ms)
} }
// Camí comú dels dos overloads: fa el dispatch crossfade vs stop+play i // Camí comú dels dos overloads: fa el dispatch crossfade vs stop+play i
// actualitza state/loop. Els callers s'encarreguen del gating (music_enabled_, // actualitza el loop cachejat. Els callers s'encarreguen del gating
// nullptr, same-track early return) i d'actualitzar music_.name. // (music_enabled_, nullptr, same-track early return) i del nom. L'estat el
// manté Ja (Ja::playMusic posa PLAYING al Ja::Music* corresponent).
void Audio::playMusicInternal(Ja::Music* music, const int loop, const int crossfade_ms) { void Audio::playMusicInternal(Ja::Music* music, const int loop, const int crossfade_ms) {
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { const bool CURRENTLY_PLAYING = (getMusicState() == MusicState::PLAYING);
if (crossfade_ms > 0 && CURRENTLY_PLAYING) {
Ja::crossfadeMusic(music, crossfade_ms, loop); Ja::crossfadeMusic(music, crossfade_ms, loop);
} else { } else {
if (music_.state == MusicState::PLAYING) { if (CURRENTLY_PLAYING) {
Ja::stopMusic(); Ja::stopMusic();
} }
Ja::playMusic(music, loop); Ja::playMusic(music, loop);
} }
music_.loop = (loop != 0); music_.loop = (loop != 0);
music_.state = MusicState::PLAYING;
} }
// Pausa la música // Pausa la música (l'estat el transiciona Ja::pauseMusic)
// NOLINTNEXTLINE(readability-make-member-function-const) — muta Ja::current_music->state
void Audio::pauseMusic() { void Audio::pauseMusic() {
if (music_enabled_ && music_.state == MusicState::PLAYING) { if (music_enabled_ && getMusicState() == MusicState::PLAYING) {
Ja::pauseMusic(); Ja::pauseMusic();
music_.state = MusicState::PAUSED;
} }
} }
// Continua la música pausada // Continua la música pausada (l'estat el transiciona Ja::resumeMusic)
// NOLINTNEXTLINE(readability-make-member-function-const) — muta Ja::current_music->state
void Audio::resumeMusic() { void Audio::resumeMusic() {
if (music_enabled_ && music_.state == MusicState::PAUSED) { if (music_enabled_ && getMusicState() == MusicState::PAUSED) {
Ja::resumeMusic(); Ja::resumeMusic();
music_.state = MusicState::PLAYING;
} }
} }
// Detiene la música // Detiene la música (l'estat el transiciona Ja::stopMusic)
// NOLINTNEXTLINE(readability-make-member-function-const) — muta Ja::current_music/outgoing_music
void Audio::stopMusic() { void Audio::stopMusic() {
if (music_enabled_) { if (music_enabled_) {
Ja::stopMusic(); Ja::stopMusic();
music_.state = MusicState::STOPPED;
} }
} }
@@ -155,15 +152,16 @@ void Audio::stopAllSounds() {
// Realiza un fundido de salida de la música // Realiza un fundido de salida de la música
// NOLINTNEXTLINE(readability-make-member-function-const) — muta Ja::outgoing_music/incoming_fade // NOLINTNEXTLINE(readability-make-member-function-const) — muta Ja::outgoing_music/incoming_fade
void Audio::fadeOutMusic(int milliseconds) { void Audio::fadeOutMusic(int milliseconds) {
if (music_enabled_ && getRealMusicState() == MusicState::PLAYING) { if (music_enabled_ && getMusicState() == MusicState::PLAYING) {
Ja::fadeOutMusic(milliseconds); Ja::fadeOutMusic(milliseconds);
} }
} }
// Consulta directament l'estat real de la música a jail_audio // Consulta directament l'estat a Ja i el projecta al subconjunt d'estats que
auto Audio::getRealMusicState() -> MusicState { // exposa Audio (INVALID/DISABLED de Ja col·lapsen a STOPPED — la capa d'usuari
const Ja::MusicState JA_STATE = Ja::getMusicState(); // només vol saber si està sonant, pausat o parat).
switch (JA_STATE) { auto Audio::getMusicState() -> MusicState {
switch (Ja::getMusicState()) {
case Ja::MusicState::PLAYING: case Ja::MusicState::PLAYING:
return MusicState::PLAYING; return MusicState::PLAYING;
case Ja::MusicState::PAUSED: case Ja::MusicState::PAUSED:
+3 -5
View File
@@ -111,16 +111,14 @@ class Audio {
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; } [[nodiscard]] auto isEnabled() const -> bool { return enabled_; }
[[nodiscard]] auto isSoundEnabled() const -> bool { return sound_enabled_; } [[nodiscard]] auto isSoundEnabled() const -> bool { return sound_enabled_; }
[[nodiscard]] auto isMusicEnabled() const -> bool { return music_enabled_; } [[nodiscard]] auto isMusicEnabled() const -> bool { return music_enabled_; }
[[nodiscard]] auto getMusicState() const -> MusicState { return music_.state; } [[nodiscard]] static auto getMusicState() -> MusicState; // Estat real consultat a Ja::
[[nodiscard]] static auto getRealMusicState() -> MusicState;
[[nodiscard]] auto getCurrentMusicName() const -> const std::string& { return music_.name; } [[nodiscard]] auto getCurrentMusicName() const -> const std::string& { return music_.name; }
private: private:
// --- Tipos anidados --- // --- Tipos anidados ---
struct Music { struct Music {
MusicState state{MusicState::STOPPED}; // Estado actual de la música std::string name; // Última pista de música reproducida (buida si es va passar per punter sense filename)
std::string name; // Última pista de música reproducida bool loop{false}; // Si el play actual és en bucle
bool loop{false}; // Indica si se reproduce en bucle
}; };
// --- Métodos --- // --- Métodos ---