refactor(core/audio): introduce Ja::Engine class (COUP-01, CONV-02)
Encapsula l'estat del motor a Ja::Engine — classe amb RAII (ctor obre SDL_AudioDevice, dtor el tanca) i `assert` al ctor contra instàncies concurrents. Tots els globals del namespace anònim del commit 1 (current_music, channels[], audio_spec, music_volume, sound_volume[], music_enabled, sound_enabled, sdl_audio_device, outgoing_music, incoming_fade) es converteixen en membres privats amb sufix `_`. Els helpers que toquen estat (stealCurrentIntoOutgoing, updateOutgoing/IncomingFade, updateCurrent*, forEachTargetChannel) passen a mètodes privats. Els stateless (feedMusicChunk, pumpMusic, preFillOutgoing, fadeProgress) es queden al namespace anònim del .cpp. Engine::active() static accessor: registrat al ctor, desregistrat al dtor. Els deleters Ja::deleteMusic/deleteSound consulten Engine::active() per parar canals/música abans d'alliberar recursos; si active() és nullptr (shutdown-order invertit), fan cleanup local silenciós. Això resol CONV-02 (engine ja és classe) i tanca COUP-01. Compat amb els callers: les free functions Ja::init/quit/update, Ja::playMusic, Ja::pauseMusic, Ja::resumeMusic, Ja::stopMusic, Ja::fadeOutMusic, Ja::crossfadeMusic, Ja::getMusicState, Ja::setMusicVolume, Ja::enableMusic, Ja::playSound, Ja::playSoundOnChannel, Ja::pauseChannel, Ja::resumeChannel, Ja::stopChannel, Ja::setSoundVolume i Ja::enableSound es mantenen com a thin forwarders contra Engine::active(). audio.cpp no canvia en aquest commit; les seves crides Ja:: continuen funcionant. Al commit 3 Audio agafarà ownership directe via std::unique_ptr<Engine> i aquests forwarders desapareixeran. Ja::init() crea un std::unique_ptr<Engine> propietari dins d'un namespace anònim del .cpp (encara no és Audio qui posseeix — això és el commit 3). Ja::loadMusic/loadSound/deleteMusic/deleteSound es queden com a free functions permanents (no depenen del motor per construir). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bc53174f2b
commit
1d124e967d
2 files changed
+552
-395
No files matched your search
@@ -24,10 +24,10 @@ struct SdlFreeDeleter {
|
||||
}
|
||||
};
|
||||
|
||||
// Engine de baix nivell d'àudio del projecte jailgames: streaming OGG (stb_vorbis)
|
||||
// + N canals d'efectes (SDL3 audio). No depèn d'Options ni de cap singleton del
|
||||
// joc; només de SDL3 i stb_vorbis. La capa superior (Audio) li passa recursos
|
||||
// pel punter i fa el bookkeeping d'usuari.
|
||||
// Motor de baix nivell d'àudio del projecte jailgames: streaming OGG
|
||||
// (stb_vorbis) + N canals d'efectes (SDL3 audio). No depèn d'Options ni de cap
|
||||
// singleton del joc; només de SDL3 i stb_vorbis. La capa superior (Audio) li
|
||||
// passa recursos pel punter i fa el bookkeeping d'usuari.
|
||||
namespace Ja {
|
||||
|
||||
// --- Public Enums ---
|
||||
@@ -51,9 +51,9 @@ namespace Ja {
|
||||
inline constexpr int MAX_SIMULTANEOUS_CHANNELS = 20;
|
||||
inline constexpr int MAX_GROUPS = 2;
|
||||
|
||||
// Spec de fallback del dispositiu. S'aplica abans de Ja::init() i com a valor
|
||||
// inicial de Sound/Music. L'spec real d'ús l'imposa Ja::init(), que la capa
|
||||
// Audio alimenta des de Defaults::Audio — única font de veritat del projecte.
|
||||
// Spec de fallback del dispositiu. S'aplica abans que l'Engine s'iniciï i
|
||||
// com a valor inicial de Sound/Music. L'spec real d'ús l'imposa el ctor
|
||||
// d'Engine, alimentat des de Defaults::Audio via Audio.
|
||||
inline constexpr SDL_AudioSpec DEFAULT_SPEC{SDL_AUDIO_S16, 2, 48000};
|
||||
|
||||
// --- Struct Definitions ---
|
||||
@@ -106,14 +106,88 @@ namespace Ja {
|
||||
FadeState fade;
|
||||
};
|
||||
|
||||
// --- Lifecycle ---
|
||||
// --- Engine ---
|
||||
// Encapsula tot l'estat que abans vivia com a globals inline. Un sol Engine
|
||||
// viu per procés (enforceat via assert al ctor contra `active_`). El ctor
|
||||
// obre el device SDL; el dtor el tanca (RAII). Els deleters
|
||||
// `Ja::deleteMusic`/`Ja::deleteSound` accedeixen al motor actiu via
|
||||
// `Engine::active()` per parar canals abans d'alliberar.
|
||||
class Engine {
|
||||
public:
|
||||
Engine(int freq, SDL_AudioFormat format, int num_channels);
|
||||
~Engine();
|
||||
Engine(const Engine&) = delete;
|
||||
auto operator=(const Engine&) -> Engine& = delete;
|
||||
Engine(Engine&&) = delete;
|
||||
auto operator=(Engine&&) -> Engine& = delete;
|
||||
|
||||
// Retorna el motor actiu o nullptr si cap ha estat construït. L'usen
|
||||
// els deleters de recursos perquè no els arriba cap referència directa.
|
||||
[[nodiscard]] static auto active() noexcept -> Engine*;
|
||||
|
||||
void update();
|
||||
|
||||
// --- Música ---
|
||||
void playMusic(Music* music, int loop = -1);
|
||||
void pauseMusic();
|
||||
void resumeMusic();
|
||||
void stopMusic();
|
||||
void fadeOutMusic(int milliseconds);
|
||||
void crossfadeMusic(Music* music, int crossfade_ms, int loop = -1);
|
||||
[[nodiscard]] auto getMusicState() const -> MusicState;
|
||||
auto setMusicVolume(float volume) -> float;
|
||||
void enableMusic(bool value);
|
||||
// Notifica al motor que un Music s'està destruint: si és el current_music
|
||||
// s'atura abans que els seus recursos (stream/vorbis) deixin de ser vàlids.
|
||||
void onMusicDeleted(const Music* music);
|
||||
|
||||
// --- So ---
|
||||
auto playSound(Sound* sound, int loop = 0, int group = 0) -> int;
|
||||
auto playSoundOnChannel(Sound* sound, int channel, int loop = 0, int group = 0) -> int;
|
||||
void pauseChannel(int channel);
|
||||
void resumeChannel(int channel);
|
||||
void stopChannel(int channel);
|
||||
auto setSoundVolume(float volume, int group = -1) -> float;
|
||||
void enableSound(bool value);
|
||||
// Notifica al motor que un Sound s'està destruint: els canals que el
|
||||
// referenciïn es paren abans d'alliberar el buffer.
|
||||
void onSoundDeleted(const Sound* sound);
|
||||
|
||||
private:
|
||||
void stealCurrentIntoOutgoing(int duration_ms);
|
||||
void updateOutgoingFade();
|
||||
void updateIncomingFade();
|
||||
void updateCurrentMusic();
|
||||
void updateSoundChannels();
|
||||
|
||||
template <typename Fn>
|
||||
void forEachTargetChannel(int channel, Fn&& fn);
|
||||
|
||||
Music* current_music_{nullptr};
|
||||
Channel channels_[MAX_SIMULTANEOUS_CHANNELS]{};
|
||||
SDL_AudioSpec audio_spec_{DEFAULT_SPEC};
|
||||
float music_volume_{1.0F};
|
||||
float sound_volume_[MAX_GROUPS]{};
|
||||
bool music_enabled_{true};
|
||||
bool sound_enabled_{true};
|
||||
SDL_AudioDeviceID sdl_audio_device_{0};
|
||||
OutgoingMusic outgoing_music_;
|
||||
FadeState incoming_fade_;
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming) — convenció projecte: private static amb sufix _
|
||||
static Engine* active_;
|
||||
};
|
||||
|
||||
// --- Lifecycle forwarders ---
|
||||
// Compat amb els callers actuals. Creen/destrueixen l'Engine únic via
|
||||
// make_unique emmagatzemat al propi .cpp. Al commit següent Audio agafarà
|
||||
// ownership directe i aquests forwarders desapareixeran per a la capa de
|
||||
// control; els de música/so romanen només el temps de la migració.
|
||||
void init(int freq, SDL_AudioFormat format, int num_channels);
|
||||
void quit();
|
||||
void update();
|
||||
|
||||
// --- Music API ---
|
||||
[[nodiscard]] auto loadMusic(const Uint8* buffer, Uint32 length) -> Music*;
|
||||
[[nodiscard]] auto loadMusic(const Uint8* buffer, Uint32 length, const char* filename) -> Music*;
|
||||
// --- Music API forwarders (temporal, eliminats al commit 3) ---
|
||||
void playMusic(Music* music, int loop = -1);
|
||||
void pauseMusic();
|
||||
void resumeMusic();
|
||||
@@ -121,19 +195,26 @@ namespace Ja {
|
||||
void fadeOutMusic(int milliseconds);
|
||||
void crossfadeMusic(Music* music, int crossfade_ms, int loop = -1);
|
||||
[[nodiscard]] auto getMusicState() -> MusicState;
|
||||
void deleteMusic(Music* music);
|
||||
auto setMusicVolume(float volume) -> float;
|
||||
void enableMusic(bool value);
|
||||
|
||||
// --- Sound API ---
|
||||
[[nodiscard]] auto loadSound(std::uint8_t* buffer, std::uint32_t size) -> Sound*;
|
||||
// --- Sound API forwarders (temporal, eliminats al commit 3) ---
|
||||
auto playSound(Sound* sound, int loop = 0, int group = 0) -> int;
|
||||
auto playSoundOnChannel(Sound* sound, int channel, int loop = 0, int group = 0) -> int;
|
||||
void deleteSound(Sound* sound);
|
||||
void pauseChannel(int channel);
|
||||
void resumeChannel(int channel);
|
||||
void stopChannel(int channel);
|
||||
auto setSoundVolume(float volume, int group = -1) -> float;
|
||||
void enableSound(bool value);
|
||||
|
||||
// --- Factories i destructors (permanents) ---
|
||||
// No depenen de l'estat del motor: loadMusic/loadSound només construeixen
|
||||
// objectes, deleteMusic/deleteSound consulten Engine::active() per parar
|
||||
// canals abans d'alliberar (si el motor encara viu).
|
||||
[[nodiscard]] auto loadMusic(const Uint8* buffer, Uint32 length) -> Music*;
|
||||
[[nodiscard]] auto loadMusic(const Uint8* buffer, Uint32 length, const char* filename) -> Music*;
|
||||
void deleteMusic(Music* music);
|
||||
[[nodiscard]] auto loadSound(std::uint8_t* buffer, std::uint32_t size) -> Sound*;
|
||||
void deleteSound(Sound* sound);
|
||||
|
||||
} // namespace Ja
|
||||
Reference in new issue
Block a user