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>
playSound (x2), stopAllSounds, fadeOutMusic, setSoundVolume i
setMusicVolume es declaraven const però mutaven l'estat global de Ja::*
(channels[], outgoing_music, music_volume, sound_volume[]). El const no
protegia res: el compilador no detectava les mutacions perquè anaven a
globals, no a membres, i podia induir a error a qui llegia el header.
Els callers externs van via Audio* (no Audio const*), així que el canvi
és source-compatible.
Clang-tidy falla amb readability-make-member-function-const perquè
tampoc veu la mutació — és exactament el símptoma que motiva el
hallazgo. S'afegeix NOLINT localitzat a cada mètode amb la raó. Quan
s'executi COUP-01 (encapsular Ja::* en classe membre) els NOLINT
cauran sols perquè la mutació serà visible al compilador.
effectiveVolume es queda const — és un càlcul pur sobre els membres.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setSoundVolume i setMusicVolume repetien la mateixa lògica de clamp +
gate (enabled_ && channel_enabled_) + multiplicació pel master
config_.volume. S'extreu effectiveVolume(volume, channel_enabled) privat
i els dos setters es limiten a la crida Ja::*.
Any canvi futur a la política (p.ex. una corba logarítmica) queda en un
sol lloc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Els dos playMusic compartien el dispatch crossfade-vs-stop+play i
l'actualització de music_.state/loop. S'extreu playMusicInternal privat
que rep el Ja::Music* ja resolt i fa el camí comú.
Cada overload es queda només amb el seu gating propi: la variant per
nom fa early-return si ja sona la mateixa pista i posa music_.name; la
variant per punter buida el nom (PATCH-02 recuperarà el filename del
propi Ja::Music).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Abans, toggleEnabled/toggleSound/toggleMusic i les variants sense bool
només flipaven el flag intern; els canals actius no notaven el canvi
fins a la pròxima crida explícita a setSoundVolume/setMusicVolume —
silenciós i fàcil de perdre.
Ara:
- toggleEnabled passa per enable(bool), que ja reaplicava els volums.
- enableSound(bool) i enableMusic(bool) es mouen a audio.cpp i
reapliquen el volum gated cap a Ja::*.
- Les variants enableSound()/disableSound()/enableMusic()/disableMusic()
i els toggles passen per aquestes dues funcions, així que qualsevol
camí d'activació produeix el mateix efecte sobre l'estat sonor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VOLUME_STEP, FREQUENCY i DEFAULT_CROSSFADE_MS vivien com a membres
constexpr d'Audio. Eren valors de configuració — no invariants de la
classe — i la convenció del projecte és que tots els paràmetres
ajustables visquin a utils/defaults.hpp.
Passen a Defaults::Audio::{VOLUME_STEP, FREQUENCY, CROSSFADE_MS}. L'únic
ús intern (Ja::init al constructor d'Audio) es reapunta al Defaults. Cap
ús extern dels tres símbols.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>