From 0ee117135ce3fcac1b5cd93400f5822c0735d665 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 14 May 2026 21:02:43 +0200 Subject: [PATCH] neteja tidy a source/core/system i audio amb fixes d'arrel --- source/core/audio/audio.cpp | 2 +- source/core/audio/audio.hpp | 5 +- source/core/audio/jail_audio.hpp | 147 ++++++++++-------- source/core/system/director.cpp | 41 ++--- source/core/system/director.h | 19 +-- .../external/{stb_vorbis.c => stb_vorbis.h} | 0 source/game/game.h | 32 ++-- source/game/scenes/instructions.h | 6 +- source/game/scenes/intro.h | 2 +- source/game/scenes/logo.h | 2 +- source/game/scenes/title.h | 4 +- 11 files changed, 145 insertions(+), 115 deletions(-) rename source/external/{stb_vorbis.c => stb_vorbis.h} (100%) diff --git a/source/core/audio/audio.cpp b/source/core/audio/audio.cpp index 0384824..aebaaa5 100644 --- a/source/core/audio/audio.cpp +++ b/source/core/audio/audio.cpp @@ -8,7 +8,7 @@ // Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp). // clang-format off #undef STB_VORBIS_HEADER_ONLY -#include "external/stb_vorbis.c" +#include "external/stb_vorbis.h" // stb_vorbis.c filtra les macros L, C i R (i PLAYBACK_*) al TU. Les netegem // perquè xocarien amb noms de paràmetres de plantilla en altres headers. #undef L diff --git a/source/core/audio/audio.hpp b/source/core/audio/audio.hpp index 260b406..dc02f50 100644 --- a/source/core/audio/audio.hpp +++ b/source/core/audio/audio.hpp @@ -1,5 +1,6 @@ #pragma once +#include // Para lround #include // Para int8_t, uint8_t #include // Para string #include // Para move @@ -59,8 +60,8 @@ class Audio { // --- Helpers de conversió per a la capa de presentació --- // UI (menús, notificacions) manega enters 0..100; internament viu float 0..1. - static constexpr auto toPercent(float volume) -> int { - return static_cast((volume * 100.0F) + 0.5F); + static auto toPercent(float volume) -> int { + return static_cast(std::lround(volume * 100.0F)); } static constexpr auto fromPercent(int percent) -> float { return static_cast(percent) / 100.0F; diff --git a/source/core/audio/jail_audio.hpp b/source/core/audio/jail_audio.hpp index bafdf05..fd2ebf2 100644 --- a/source/core/audio/jail_audio.hpp +++ b/source/core/audio/jail_audio.hpp @@ -12,7 +12,7 @@ #include // Para std::vector #define STB_VORBIS_HEADER_ONLY -#include "external/stb_vorbis.c" // Para stb_vorbis_open_memory i streaming +#include "external/stb_vorbis.h" // Para stb_vorbis_open_memory i streaming // Deleter stateless per a buffers reservats amb `SDL_malloc` / `SDL_LoadWAV*`. // Compatible amb `std::unique_ptr` — zero size @@ -26,14 +26,14 @@ struct SDLFreeDeleter { }; // --- Public Enums --- -enum JA_Channel_state { +enum JA_Channel_state : std::uint8_t { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, JA_SOUND_DISABLED, }; -enum JA_Music_state { +enum JA_Music_state : std::uint8_t { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, @@ -42,7 +42,7 @@ enum JA_Music_state { }; // --- Struct Definitions --- -enum { +enum : std::uint8_t { JA_MAX_SIMULTANEOUS_CHANNELS = 20, JA_MAX_GROUPS = 2 }; @@ -57,10 +57,10 @@ struct JA_Sound_t { struct JA_Channel_t { JA_Sound_t* sound{nullptr}; + SDL_AudioStream* stream{nullptr}; int pos{0}; int times{0}; int group{0}; - SDL_AudioStream* stream{nullptr}; JA_Channel_state state{JA_CHANNEL_FREE}; }; @@ -199,63 +199,86 @@ inline void JA_PreFillOutgoing(JA_Music_t* music, int duration_ms) { // --- Core Functions --- +// Fade-out de la música sortint (crossfade o fade-out a silenci). En acabar +// destrueix l'AudioStream sortint. +inline void JA_UpdateOutgoingFade() { + if ((outgoing_music.stream == nullptr) || !outgoing_music.fade.active) { + return; + } + const Uint64 elapsed = SDL_GetTicks() - outgoing_music.fade.start_time; + if (elapsed >= static_cast(outgoing_music.fade.duration_ms)) { + SDL_DestroyAudioStream(outgoing_music.stream); + outgoing_music.stream = nullptr; + outgoing_music.fade.active = false; + return; + } + const float percent = static_cast(elapsed) / static_cast(outgoing_music.fade.duration_ms); + SDL_SetAudioStreamGain(outgoing_music.stream, outgoing_music.fade.initial_volume * (1.0F - percent)); +} + +// Fade-in de la música entrant (segona meitat d'un crossfade). +inline void JA_UpdateIncomingFade() { + if (!incoming_fade.active) { + return; + } + const Uint64 elapsed = SDL_GetTicks() - incoming_fade.start_time; + if (elapsed >= static_cast(incoming_fade.duration_ms)) { + incoming_fade.active = false; + SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume); + return; + } + const float percent = static_cast(elapsed) / static_cast(incoming_fade.duration_ms); + SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * percent); +} + +// Manté l'stream de la música activa alimentat i para la reproducció si +// el vorbis s'ha esgotat i no queden loops. +inline void JA_UpdateCurrentMusic() { + if (!JA_musicEnabled || current_music == nullptr || current_music->state != JA_MUSIC_PLAYING) { + return; + } + JA_UpdateIncomingFade(); + JA_PumpMusic(current_music); + if (current_music->times == 0 && SDL_GetAudioStreamAvailable(current_music->stream) == 0) { + JA_StopMusic(); + } +} + +// Avança l'estat d'un sol canal de so: alimenta més samples mentre quedin +// loops; si ja no queden i l'stream s'ha buidat, atura el canal. +inline void JA_UpdateSoundChannel(int channel_index) { + JA_Channel_t& ch = channels[channel_index]; + if (ch.state != JA_CHANNEL_PLAYING) { + return; + } + if (ch.times == 0) { + if (SDL_GetAudioStreamAvailable(ch.stream) == 0) { + JA_StopChannel(channel_index); + } + return; + } + if (static_cast(SDL_GetAudioStreamAvailable(ch.stream)) < (ch.sound->length / 2)) { + SDL_PutAudioStreamData(ch.stream, ch.sound->buffer.get(), ch.sound->length); + if (ch.times > 0) { + ch.times--; + } + } +} + +// Avança tots els canals de so actius. +inline void JA_UpdateSoundChannels() { + if (!JA_soundEnabled) { + return; + } + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) { + JA_UpdateSoundChannel(i); + } +} + inline void JA_Update() { - // --- Outgoing music fade-out (crossfade o fade-out a silencio) --- - if ((outgoing_music.stream != nullptr) && outgoing_music.fade.active) { - Uint64 now = SDL_GetTicks(); - Uint64 elapsed = now - outgoing_music.fade.start_time; - if (elapsed >= (Uint64)outgoing_music.fade.duration_ms) { - SDL_DestroyAudioStream(outgoing_music.stream); - outgoing_music.stream = nullptr; - outgoing_music.fade.active = false; - } else { - float percent = (float)elapsed / (float)outgoing_music.fade.duration_ms; - SDL_SetAudioStreamGain(outgoing_music.stream, outgoing_music.fade.initial_volume * (1.0F - percent)); - } - } - - // --- Current music --- - if (JA_musicEnabled && (current_music != nullptr) && current_music->state == JA_MUSIC_PLAYING) { - // Fade-in (parte de un crossfade) - if (incoming_fade.active) { - Uint64 now = SDL_GetTicks(); - Uint64 elapsed = now - incoming_fade.start_time; - if (elapsed >= (Uint64)incoming_fade.duration_ms) { - incoming_fade.active = false; - SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume); - } else { - float percent = (float)elapsed / (float)incoming_fade.duration_ms; - SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * percent); - } - } - - // Streaming: rellenem l'stream fins al low-water-mark i parem si el - // vorbis s'ha esgotat i no queden loops. - JA_PumpMusic(current_music); - if (current_music->times == 0 && SDL_GetAudioStreamAvailable(current_music->stream) == 0) { - JA_StopMusic(); - } - } - - // --- Sound channels --- - if (JA_soundEnabled) { - for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) { - if (channels[i].state == JA_CHANNEL_PLAYING) { - if (channels[i].times != 0) { - if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) { - SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer.get(), channels[i].sound->length); - if (channels[i].times > 0) { - channels[i].times--; - } - } - } else { - if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) { - JA_StopChannel(i); - } - } - } - } - } + JA_UpdateOutgoingFade(); + JA_UpdateCurrentMusic(); + JA_UpdateSoundChannels(); } inline void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) { @@ -586,7 +609,7 @@ inline void JA_EnableMusic(const bool value) { inline auto JA_LoadSound(uint8_t* buffer, uint32_t size) -> JA_Sound_t* { auto sound = std::make_unique(); Uint8* raw = nullptr; - if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &raw, &sound->length)) { + if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), true, &sound->spec, &raw, &sound->length)) { std::cout << "Failed to load WAV from memory: " << SDL_GetError() << '\n'; return nullptr; } diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index b34488a..73d394a 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -1,9 +1,10 @@ #include "core/system/director.h" #include -#include // for errno, EEXIST, EACCES, ENAMETOO... -#include // for printf, perror -#include // for strcmp + +#include // for errno, EEXIST, EACCES, ENAMETOO... +#include // for printf, perror +#include // for strcmp #ifndef __EMSCRIPTEN__ #include // for mkdir, stat, S_IRWXU #include // for getuid @@ -40,7 +41,7 @@ // Constructor Director::Director(int argc, const char *argv[]) { - std::cout << "Game start" << std::endl; + std::cout << "Game start" << '\n'; // Inicializa variables section = new section_t(); section->name = SECTION_PROG_LOGO; @@ -97,7 +98,7 @@ Director::Director(int argc, const char *argv[]) { const std::string pack_path = executablePath + "resources.pack"; #endif if (!ResourceHelper::initializeResourceSystem(pack_path, enable_fallback)) { - std::cerr << "Fatal: resource system init failed (missing resources.pack?)" << std::endl; + std::cerr << "Fatal: resource system init failed (missing resources.pack?)" << '\n'; exit(EXIT_FAILURE); } } @@ -197,7 +198,7 @@ Director::~Director() { ResourceHelper::shutdownResourceSystem(); - std::cout << "\nBye!" << std::endl; + std::cout << "\nBye!" << '\n'; } // Inicializa el objeto input @@ -256,14 +257,14 @@ void Director::initJailAudio() { } // Arranca SDL y crea la ventana -bool Director::initSDL() { +auto Director::initSDL() -> bool { // Indicador de éxito bool success = true; // Inicializa SDL if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMEPAD)) { if (Options::settings.console) { - std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl; + std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << '\n'; } success = false; } else { @@ -278,18 +279,18 @@ bool Director::initSDL() { 0); if (window == nullptr) { if (Options::settings.console) { - std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl; + std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << '\n'; } success = false; } else { SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); // Crea un renderizador para la ventana - renderer = SDL_CreateRenderer(window, NULL); + renderer = SDL_CreateRenderer(window, nullptr); if (renderer == nullptr) { if (Options::settings.console) { - std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl; + std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << '\n'; } success = false; } else { @@ -315,17 +316,17 @@ bool Director::initSDL() { } if (Options::settings.console) { - std::cout << std::endl; + std::cout << '\n'; } return success; } // Crea el indice de ficheros -bool Director::setFileList() { +auto Director::setFileList() -> bool { #ifdef MACOS_BUNDLE const std::string prefix = "/../Resources"; #else - const std::string prefix = ""; + const std::string prefix; #endif // Ficheros de configuración @@ -488,7 +489,7 @@ void Director::createSystemFolder(const std::string &folder) { // En Emscripten no necesitamos crear carpetas (MEMFS las crea automáticamente) (void)folder; #else - struct stat st = {0}; + struct stat st = {.st_dev = 0}; if (stat(systemFolder.c_str(), &st) == -1) { errno = 0; #ifdef _WIN32 @@ -537,10 +538,14 @@ void Director::handleSectionTransition() { case SECTION_PROG_GAME: targetSection = ActiveSection::Game; break; + default: + break; } // Si no ha cambiado, no hay nada que hacer - if (targetSection == activeSection) return; + if (targetSection == activeSection) { + return; + } // Destruye la sección anterior logo.reset(); @@ -571,7 +576,7 @@ void Director::handleSectionTransition() { } // Ejecuta un frame del juego -SDL_AppResult Director::iterate() { +auto Director::iterate() -> SDL_AppResult { #ifdef __EMSCRIPTEN__ // En WASM no se puede salir: reinicia al logo if (section->name == SECTION_PROG_QUIT) { @@ -611,7 +616,7 @@ SDL_AppResult Director::iterate() { } // Procesa un evento -SDL_AppResult Director::handleEvent(SDL_Event *event) { +auto Director::handleEvent(SDL_Event *event) -> SDL_AppResult { #ifndef __EMSCRIPTEN__ // Evento de salida de la aplicación if (event->type == SDL_EVENT_QUIT) { diff --git a/source/core/system/director.h b/source/core/system/director.h index 1984d14..bc3f5f3 100644 --- a/source/core/system/director.h +++ b/source/core/system/director.h @@ -2,6 +2,7 @@ #include +#include // for uint8_t #include #include // for string, basic_string class Game; @@ -11,7 +12,7 @@ class Title; struct section_t; // Secciones activas del Director -enum class ActiveSection { None, +enum class ActiveSection : std::uint8_t { None, Logo, Intro, Title, @@ -36,19 +37,19 @@ class Director { std::string systemFolder; // Carpeta del sistema donde guardar datos // Inicializa jail_audio - void initJailAudio(); + static void initJailAudio(); // Arranca SDL y crea la ventana - bool initSDL(); + auto initSDL() -> bool; // Inicializa el objeto input - void initInput(); + static void initInput(); // Crea el indice de ficheros - bool setFileList(); + auto setFileList() -> bool; // Comprueba los parametros del programa - void checkProgramArguments(int argc, const char *argv[]); + static void checkProgramArguments(int argc, const char *argv[]); // Crea la carpeta del sistema donde guardar datos void createSystemFolder(const std::string &folder); @@ -64,11 +65,11 @@ class Director { ~Director(); Director(const Director &) = delete; - Director &operator=(const Director &) = delete; + auto operator=(const Director &) -> Director & = delete; // Ejecuta un frame del juego - SDL_AppResult iterate(); + auto iterate() -> SDL_AppResult; // Procesa un evento - SDL_AppResult handleEvent(SDL_Event *event); + auto handleEvent(SDL_Event *event) -> SDL_AppResult; }; diff --git a/source/external/stb_vorbis.c b/source/external/stb_vorbis.h similarity index 100% rename from source/external/stb_vorbis.c rename to source/external/stb_vorbis.h diff --git a/source/game/game.h b/source/game/game.h index 160ce94..2a101ee 100644 --- a/source/game/game.h +++ b/source/game/game.h @@ -266,16 +266,16 @@ class Game { void loadMedia(); // Carga el fichero de puntos - bool loadScoreFile(); + auto loadScoreFile() -> bool; // Carga el fichero de datos para la demo - bool loadDemoFile(); + auto loadDemoFile() -> bool; // Guarda el fichero de puntos - bool saveScoreFile(); + auto saveScoreFile() -> bool; // Guarda el fichero de datos para la demo - bool saveDemoFile(); + auto saveDemoFile() -> bool; // Inicializa las formaciones enemigas void initEnemyFormations(); @@ -299,7 +299,7 @@ class Game { void updateHiScore(); // Transforma un valor numérico en una cadena de 6 cifras - std::string updateScoreText(Uint32 num); + auto updateScoreText(Uint32 num) -> std::string; // Pinta el marcador en pantalla usando un objeto texto void renderScoreBoard(); @@ -326,7 +326,7 @@ class Game { void renderBalloons(); // Crea un globo nuevo en el vector de globos - Uint8 createBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 stoppedcounter); + auto createBalloon(float x, int y, Uint8 kind, float velx, float speed, Uint16 stoppedcounter) -> Uint8; // Crea una PowerBall void createPowerBall(); @@ -359,7 +359,7 @@ class Game { void freeBalloons(); // Comprueba la colisión entre el jugador y los globos activos - bool checkPlayerBalloonCollision(Player *player); + auto checkPlayerBalloonCollision(Player *player) -> bool; // Comprueba la colisión entre el jugador y los items void checkPlayerItemCollision(Player *player); @@ -386,7 +386,7 @@ class Game { void renderItems(); // Devuelve un item en función del azar - Uint8 dropItem(); + auto dropItem() -> Uint8; // Crea un objeto item void createItem(Uint8 kind, float x, float y); @@ -422,13 +422,13 @@ class Game { void evaluateAndSetMenace(); // Obtiene el valor de la variable - Uint8 getMenace(); + auto getMenace() -> Uint8; // Establece el valor de la variable void setTimeStopped(bool value); // Obtiene el valor de la variable - bool isTimeStopped(); + auto isTimeStopped() -> bool; // Establece el valor de la variable void setTimeStoppedCounter(Uint16 value); @@ -470,7 +470,7 @@ class Game { void updateDeathShake(); // Indica si el efecto de agitación intensa está activo - bool isDeathShaking(); + auto isDeathShaking() -> bool; // Actualiza la secuencia de muerte del jugador void updateDeathSequence(); @@ -494,10 +494,10 @@ class Game { void enterGameOverScreen(); // Indica si se puede crear una powerball - bool canPowerBallBeCreated(); + auto canPowerBallBeCreated() -> bool; // Calcula el poder actual de los globos en pantalla - int calculateScreenPower(); + auto calculateScreenPower() -> int; // Inicializa las variables que contienen puntos de ruta para mover objetos void initPaths(); @@ -509,7 +509,7 @@ class Game { void updateHelper(); // Comprueba si todos los jugadores han muerto - bool allPlayersAreDead(); + auto allPlayersAreDead() -> bool; // Elimina todos los objetos contenidos en vectores void deleteAllVectorObjects(); @@ -525,7 +525,7 @@ class Game { ~Game(); Game(const Game &) = delete; - Game &operator=(const Game &) = delete; + auto operator=(const Game &) -> Game & = delete; // Bucle para el juego void run(); @@ -534,7 +534,7 @@ class Game { void iterate(); // Indica si el juego ha terminado - bool hasFinished() const; + [[nodiscard]] auto hasFinished() const -> bool; // Procesa un evento void handleEvent(const SDL_Event *event); diff --git a/source/game/scenes/instructions.h b/source/game/scenes/instructions.h index 3aa7a37..32f6cde 100644 --- a/source/game/scenes/instructions.h +++ b/source/game/scenes/instructions.h @@ -46,7 +46,7 @@ class Instructions { ~Instructions(); Instructions(const Instructions &) = delete; - Instructions &operator=(const Instructions &) = delete; + auto operator=(const Instructions &) -> Instructions & = delete; // Bucle principal void run(mode_e mode); @@ -64,8 +64,8 @@ class Instructions { void checkEvents(); // Indica si las instrucciones han terminado - bool hasFinished() const; + [[nodiscard]] auto hasFinished() const -> bool; // Indica si se ha solicitado salir de la aplicación - bool isQuitRequested() const; + [[nodiscard]] auto isQuitRequested() const -> bool; }; \ No newline at end of file diff --git a/source/game/scenes/intro.h b/source/game/scenes/intro.h index 6b9489f..f56aa02 100644 --- a/source/game/scenes/intro.h +++ b/source/game/scenes/intro.h @@ -48,7 +48,7 @@ class Intro { ~Intro(); Intro(const Intro &) = delete; - Intro &operator=(const Intro &) = delete; + auto operator=(const Intro &) -> Intro & = delete; // Bucle principal void run(); diff --git a/source/game/scenes/logo.h b/source/game/scenes/logo.h index 97be058..c4149aa 100644 --- a/source/game/scenes/logo.h +++ b/source/game/scenes/logo.h @@ -43,7 +43,7 @@ class Logo { ~Logo(); Logo(const Logo &) = delete; - Logo &operator=(const Logo &) = delete; + auto operator=(const Logo &) -> Logo & = delete; // Bucle principal void run(); diff --git a/source/game/scenes/title.h b/source/game/scenes/title.h index 6a695ac..a9dc38a 100644 --- a/source/game/scenes/title.h +++ b/source/game/scenes/title.h @@ -129,7 +129,7 @@ class Title { void runDemoGame(); // Modifica las opciones para los controles de los jugadores - bool updatePlayerInputs(int numPlayer); + auto updatePlayerInputs(int numPlayer) -> bool; // Crea el mosaico de fondo del titulo void createTiledBackground(); @@ -148,7 +148,7 @@ class Title { ~Title(); Title(const Title &) = delete; - Title &operator=(const Title &) = delete; + auto operator=(const Title &) -> Title & = delete; // Bucle para el titulo del juego void run();