#pragma once // --- Includes --- #include #include #include #include #include // Forward-declaració del decoder de vorbis. La implementació viu a // jail_audio.cpp (únic TU que compila external/stb_vorbis.c). Qualsevol caller // només necessita `stb_vorbis*` per punter — mai per valor — així que el // forward decl n'hi ha prou i evita arrossegar el .c a tots els TU. // NOLINTNEXTLINE(readability-identifier-naming) — nom imposat per l'API de stb_vorbis struct stb_vorbis; // Deleter stateless per a buffers reservats amb `SDL_malloc` / `SDL_LoadWAV*`. // Compatible amb `std::unique_ptr` — zero size overhead // gràcies a EBO, igual que un unique_ptr amb default_delete. struct SdlFreeDeleter { void operator()(Uint8* p) const noexcept { if (p != nullptr) { SDL_free(p); } } }; // 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. namespace Ja { // --- Public Enums --- enum class ChannelState : std::uint8_t { INVALID, FREE, PLAYING, PAUSED, SOUND_DISABLED, }; enum class MusicState : std::uint8_t { INVALID, PLAYING, PAUSED, STOPPED, DISABLED, }; // --- Constants --- 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. inline constexpr SDL_AudioSpec DEFAULT_SPEC{SDL_AUDIO_S16, 2, 48000}; // --- Struct Definitions --- struct Sound { SDL_AudioSpec spec{DEFAULT_SPEC}; Uint32 length{0}; // Buffer descomprimit (PCM) propietat del sound. Reservat per SDL_LoadWAV // via SDL_malloc; el deleter `SdlFreeDeleter` allibera amb SDL_free. std::unique_ptr buffer; }; // L'ordre (punters primer, ints després, enum de 8 bits al final) minimitza // el padding a 64-bit (evita avisos de clang-analyzer-optin.performance.Padding). struct Channel { Sound* sound{nullptr}; SDL_AudioStream* stream{nullptr}; int pos{0}; int times{0}; int group{0}; ChannelState state{ChannelState::FREE}; }; struct Music { SDL_AudioSpec spec{DEFAULT_SPEC}; // OGG comprimit en memòria. Propietat nostra; es copia des del buffer // d'entrada una sola vegada en loadMusic i es descomprimix en chunks // per streaming. Com que stb_vorbis guarda un punter persistent al // `.data()` d'aquest vector, no el podem resize'jar un cop establert // (una reallocation invalidaria el punter que el decoder conserva). std::vector ogg_data; stb_vorbis* vorbis{nullptr}; // handle del decoder, viu tot el cicle del Music std::string filename; int times{0}; // loops restants (-1 = infinit, 0 = un sol play) SDL_AudioStream* stream{nullptr}; MusicState state{MusicState::INVALID}; }; struct FadeState { bool active{false}; Uint64 start_time{0}; int duration_ms{0}; float initial_volume{0.0F}; }; struct OutgoingMusic { SDL_AudioStream* stream{nullptr}; FadeState fade; }; // --- Lifecycle --- 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*; 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() -> 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*; 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); } // namespace Ja