From 0dbf38f50602d0dffb126f0c796dda0af51abd9f Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 18 Apr 2026 11:43:45 +0200 Subject: [PATCH] normalitzat Audio --- CMakeLists.txt | 1 + source/core/audio/audio.cpp | 113 +++--- source/core/audio/audio.hpp | 41 +- source/core/audio/audio_adapter.cpp | 13 + source/core/audio/audio_adapter.hpp | 17 + source/core/audio/jail_audio.hpp | 360 ++++++++++++------ .../external/{stb_vorbis.h => stb_vorbis.c} | 117 +++--- 7 files changed, 435 insertions(+), 227 deletions(-) create mode 100644 source/core/audio/audio_adapter.cpp create mode 100644 source/core/audio/audio_adapter.hpp rename source/external/{stb_vorbis.h => stb_vorbis.c} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5966f0d..7e07487 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ configure_file(${CMAKE_SOURCE_DIR}/source/version.h.in ${CMAKE_BINARY_DIR}/versi set(APP_SOURCES # Core - Audio source/core/audio/audio.cpp + source/core/audio/audio_adapter.cpp # Core - Input source/core/input/global_inputs.cpp diff --git a/source/core/audio/audio.cpp b/source/core/audio/audio.cpp index da8e20b..633a767 100644 --- a/source/core/audio/audio.cpp +++ b/source/core/audio/audio.cpp @@ -1,19 +1,27 @@ -#include "audio.hpp" +#include "core/audio/audio.hpp" -#include // Para SDL_LogInfo, SDL_LogCategory, SDL_G... +#include // Para SDL_GetError, SDL_Init #include // Para clamp #include // Para std::cout -// Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp) +// 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.h" +#include "external/stb_vorbis.c" +// 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 +#undef C +#undef R +#undef PLAYBACK_MONO +#undef PLAYBACK_LEFT +#undef PLAYBACK_RIGHT // clang-format on -#include "core/audio/jail_audio.hpp" // Para JA_FadeOutMusic, JA_Init, JA_PauseM... -#include "core/resources/resource_cache.hpp" // Para Resource -#include "game/options.hpp" // Para AudioOptions, audio, MusicOptions +#include "core/audio/audio_adapter.hpp" // Para AudioResource::getMusic/getSound +#include "core/audio/jail_audio.hpp" // Para JA_* +#include "game/options.hpp" // Para Options::audio // Singleton Audio* Audio::instance = nullptr; @@ -22,7 +30,10 @@ Audio* Audio::instance = nullptr; void Audio::init() { Audio::instance = new Audio(); } // Libera la instancia -void Audio::destroy() { delete Audio::instance; } +void Audio::destroy() { + delete Audio::instance; + Audio::instance = nullptr; +} // Obtiene la instancia auto Audio::get() -> Audio* { return Audio::instance; } @@ -38,10 +49,15 @@ Audio::~Audio() { // Método principal void Audio::update() { JA_Update(); + + // Sincronizar estado: detectar cuando la música se para (ej. fade-out completado) + if (instance && instance->music_.state == MusicState::PLAYING && JA_GetMusicState() != JA_MUSIC_PLAYING) { + instance->music_.state = MusicState::STOPPED; + } } -// Reproduce la música -void Audio::playMusic(const std::string& name, const int loop) { // NOLINT(readability-convert-member-functions-to-static) +// Reproduce la música por nombre (con crossfade opcional) +void Audio::playMusic(const std::string& name, const int loop, const int crossfade_ms) { bool new_loop = (loop != 0); // Si ya está sonando exactamente la misma pista y mismo modo loop, no hacemos nada @@ -49,29 +65,45 @@ void Audio::playMusic(const std::string& name, const int loop) { // NOLINT(read return; } - // Intentar obtener recurso; si falla, no tocar estado - auto* resource = Resource::Cache::get()->getMusic(name); - if (resource == nullptr) { - // manejo de error opcional - return; + if (!music_enabled_) return; + + auto* resource = AudioResource::getMusic(name); + if (resource == nullptr) return; + + if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { + JA_CrossfadeMusic(resource, crossfade_ms, loop); + } else { + if (music_.state == MusicState::PLAYING) { + JA_StopMusic(); + } + JA_PlayMusic(resource, loop); } - // Si hay algo reproduciéndose, detenerlo primero (si el backend lo requiere) - if (music_.state == MusicState::PLAYING) { - JA_StopMusic(); // sustituir por la función de stop real del API si tiene otro nombre - } - - // Llamada al motor para reproducir la nueva pista - JA_PlayMusic(resource, loop); - - // Actualizar estado y metadatos después de iniciar con éxito music_.name = name; music_.loop = new_loop; music_.state = MusicState::PLAYING; } +// Reproduce la música por puntero (con crossfade opcional) +void Audio::playMusic(JA_Music_t* music, const int loop, const int crossfade_ms) { + if (!music_enabled_ || music == nullptr) return; + + if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { + JA_CrossfadeMusic(music, crossfade_ms, loop); + } else { + if (music_.state == MusicState::PLAYING) { + JA_StopMusic(); + } + JA_PlayMusic(music, loop); + } + + music_.name.clear(); // nom desconegut quan es passa per punter + music_.loop = (loop != 0); + music_.state = MusicState::PLAYING; +} + // Pausa la música -void Audio::pauseMusic() { // NOLINT(readability-convert-member-functions-to-static) +void Audio::pauseMusic() { if (music_enabled_ && music_.state == MusicState::PLAYING) { JA_PauseMusic(); music_.state = MusicState::PAUSED; @@ -79,7 +111,7 @@ void Audio::pauseMusic() { // NOLINT(readability-convert-member-functions-to-st } // Continua la música pausada -void Audio::resumeMusic() { // NOLINT(readability-convert-member-functions-to-static) +void Audio::resumeMusic() { if (music_enabled_ && music_.state == MusicState::PAUSED) { JA_ResumeMusic(); music_.state = MusicState::PLAYING; @@ -87,7 +119,7 @@ void Audio::resumeMusic() { // NOLINT(readability-convert-member-functions-to-s } // Detiene la música -void Audio::stopMusic() { // NOLINT(readability-make-member-function-const) +void Audio::stopMusic() { if (music_enabled_) { JA_StopMusic(); music_.state = MusicState::STOPPED; @@ -97,13 +129,13 @@ void Audio::stopMusic() { // NOLINT(readability-make-member-function-const) // Reproduce un sonido por nombre void Audio::playSound(const std::string& name, Group group) const { if (sound_enabled_) { - JA_PlaySound(Resource::Cache::get()->getSound(name), 0, static_cast(group)); + JA_PlaySound(AudioResource::getSound(name), 0, static_cast(group)); } } // Reproduce un sonido por puntero directo void Audio::playSound(JA_Sound_t* sound, Group group) const { - if (sound_enabled_) { + if (sound_enabled_ && sound != nullptr) { JA_PlaySound(sound, 0, static_cast(group)); } } @@ -138,7 +170,7 @@ auto Audio::getRealMusicState() -> MusicState { } } -// Establece el volumen de los sonidos +// Establece el volumen de los sonidos (float 0.0..1.0) void Audio::setSoundVolume(float sound_volume, Group group) const { if (sound_enabled_) { sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME); @@ -147,7 +179,7 @@ void Audio::setSoundVolume(float sound_volume, Group group) const { } } -// Establece el volumen de la música +// Establece el volumen de la música (float 0.0..1.0) void Audio::setMusicVolume(float music_volume) const { if (music_enabled_) { music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME); @@ -172,24 +204,9 @@ void Audio::enable(bool value) { // Inicializa SDL Audio void Audio::initSDLAudio() { if (!SDL_Init(SDL_INIT_AUDIO)) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError()); + std::cout << "SDL_AUDIO could not initialize! SDL Error: " << SDL_GetError() << '\n'; } else { JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2); enable(Options::audio.enabled); - - // Aplicar estado de música y sonido guardado en las opciones. - // enable() ya aplica los volúmenes, pero no toca music_enabled_/sound_enabled_. - // Si alguno está desactivado, hay que forzar el volumen a 0 en el backend. - if (!Options::audio.music.enabled) { - setMusicVolume(0.0F); // music_enabled_=true aún → llega a JA - enableMusic(false); - } - if (!Options::audio.sound.enabled) { - setSoundVolume(0.0F); // sound_enabled_=true aún → llega a JA - enableSound(false); - } - - std::cout << "\n** AUDIO SYSTEM **\n"; - std::cout << "Audio system initialized successfully\n"; } -} \ No newline at end of file +} diff --git a/source/core/audio/audio.hpp b/source/core/audio/audio.hpp index e29463f..9c5ea6f 100644 --- a/source/core/audio/audio.hpp +++ b/source/core/audio/audio.hpp @@ -1,28 +1,35 @@ #pragma once +#include // Para int8_t, uint8_t #include // Para string #include // Para move // --- Clase Audio: gestor de audio (singleton) --- +// Implementació canònica, byte-idèntica entre projectes. +// Els volums es manegen internament com a float 0.0–1.0; la capa de +// presentació (menús, notificacions) usa les helpers toPercent/fromPercent +// per mostrar 0–100 a l'usuari. class Audio { public: // --- Enums --- - enum class Group : int { + enum class Group : std::int8_t { ALL = -1, // Todos los grupos GAME = 0, // Sonidos del juego INTERFACE = 1 // Sonidos de la interfaz }; - enum class MusicState { + enum class MusicState : std::uint8_t { PLAYING, // Reproduciendo música PAUSED, // Música pausada STOPPED, // Música detenida }; // --- Constantes --- - static constexpr float MAX_VOLUME = 1.0F; // Volumen máximo - static constexpr float MIN_VOLUME = 0.0F; // Volumen mínimo - static constexpr int FREQUENCY = 48000; // Frecuencia de audio + static constexpr float MAX_VOLUME = 1.0F; // Volumen máximo (float 0..1) + static constexpr float MIN_VOLUME = 0.0F; // Volumen mínimo (float 0..1) + static constexpr float VOLUME_STEP = 0.05F; // Pas estàndard per a UI (5%) + static constexpr int FREQUENCY = 48000; // Frecuencia de audio + static constexpr int DEFAULT_CROSSFADE_MS = 1500; // Duració del crossfade per defecte (ms) // --- Singleton --- static void init(); // Inicializa el objeto Audio @@ -34,21 +41,31 @@ class Audio { static void update(); // Actualización del sistema de audio // --- Control de música --- - void playMusic(const std::string& name, int loop = -1); // Reproducir música en bucle - void pauseMusic(); // Pausar reproducción de música - void resumeMusic(); // Continua la música pausada - void stopMusic(); // Detener completamente la música - void fadeOutMusic(int milliseconds) const; // Fundido de salida de la música + void playMusic(const std::string& name, int loop = -1, int crossfade_ms = 0); // Reproducir música por nombre (con crossfade opcional) + void playMusic(struct JA_Music_t* music, int loop = -1, int crossfade_ms = 0); // Reproducir música por puntero (con crossfade opcional) + void pauseMusic(); // Pausar reproducción de música + void resumeMusic(); // Continua la música pausada + void stopMusic(); // Detener completamente la música + void fadeOutMusic(int milliseconds) const; // Fundido de salida de la música // --- Control de sonidos --- void playSound(const std::string& name, Group group = Group::GAME) const; // Reproducir sonido puntual por nombre void playSound(struct JA_Sound_t* sound, Group group = Group::GAME) const; // Reproducir sonido puntual por puntero void stopAllSounds() const; // Detener todos los sonidos - // --- Control de volumen --- + // --- Control de volumen (API interna: float 0.0..1.0) --- void setSoundVolume(float volume, Group group = Group::ALL) const; // Ajustar volumen de efectos void setMusicVolume(float volume) const; // Ajustar volumen de música + // --- 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 constexpr auto fromPercent(int percent) -> float { + return static_cast(percent) / 100.0F; + } + // --- Configuración general --- void enable(bool value); // Establecer estado general void toggleEnabled() { enabled_ = !enabled_; } // Alternar estado general @@ -94,4 +111,4 @@ class Audio { bool enabled_{true}; // Estado general del audio bool sound_enabled_{true}; // Estado de los efectos de sonido bool music_enabled_{true}; // Estado de la música -}; \ No newline at end of file +}; diff --git a/source/core/audio/audio_adapter.cpp b/source/core/audio/audio_adapter.cpp new file mode 100644 index 0000000..9221e5a --- /dev/null +++ b/source/core/audio/audio_adapter.cpp @@ -0,0 +1,13 @@ +#include "core/audio/audio_adapter.hpp" + +#include "core/resources/resource_cache.hpp" + +namespace AudioResource { + JA_Music_t* getMusic(const std::string& name) { + return Resource::Cache::get()->getMusic(name); + } + + JA_Sound_t* getSound(const std::string& name) { + return Resource::Cache::get()->getSound(name); + } +} // namespace AudioResource diff --git a/source/core/audio/audio_adapter.hpp b/source/core/audio/audio_adapter.hpp new file mode 100644 index 0000000..a5eb16e --- /dev/null +++ b/source/core/audio/audio_adapter.hpp @@ -0,0 +1,17 @@ +#pragma once + +// --- Audio Resource Adapter --- +// Aquest fitxer exposa una interfície comuna a Audio per obtenir JA_Music_t* / +// JA_Sound_t* per nom. Cada projecte la implementa en audio_adapter.cpp +// delegant al seu singleton de recursos (Resource::get(), Resource::Cache::get(), +// etc.). Això permet que audio.hpp/audio.cpp siguin idèntics entre projectes. + +#include // Para string + +struct JA_Music_t; +struct JA_Sound_t; + +namespace AudioResource { + JA_Music_t* getMusic(const std::string& name); + JA_Sound_t* getSound(const std::string& name); +} // namespace AudioResource diff --git a/source/core/audio/jail_audio.hpp b/source/core/audio/jail_audio.hpp index 44e5bb7..63308e0 100644 --- a/source/core/audio/jail_audio.hpp +++ b/source/core/audio/jail_audio.hpp @@ -3,24 +3,41 @@ // --- Includes --- #include #include // Para uint32_t, uint8_t -#include // Para NULL, fseek, printf, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET +#include // Para NULL, fseek, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET #include // Para free, malloc -#include // Para strcpy, strlen + +#include // Para std::cout +#include // Para std::unique_ptr +#include // Para std::string +#include // Para std::vector #define STB_VORBIS_HEADER_ONLY -#include "external/stb_vorbis.h" // Para stb_vorbis_decode_memory +#include "external/stb_vorbis.c" // 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 +// overhead gràcies a EBO, igual que un unique_ptr amb default_delete. +struct SDLFreeDeleter { + void operator()(Uint8* p) const noexcept { + if (p) SDL_free(p); + } +}; // --- Public Enums --- -enum JA_Channel_state { JA_CHANNEL_INVALID, +enum JA_Channel_state { + JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, - JA_SOUND_DISABLED }; -enum JA_Music_state { JA_MUSIC_INVALID, + JA_SOUND_DISABLED, +}; +enum JA_Music_state { + JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, JA_MUSIC_STOPPED, - JA_MUSIC_DISABLED }; + JA_MUSIC_DISABLED, +}; // --- Struct Definitions --- #define JA_MAX_SIMULTANEOUS_CHANNELS 20 @@ -29,7 +46,9 @@ enum JA_Music_state { JA_MUSIC_INVALID, struct JA_Sound_t { SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000}; Uint32 length{0}; - Uint8* buffer{NULL}; + // Buffer descomprimit (PCM) propietat del sound. Reservat per SDL_LoadWAV + // via SDL_malloc; el deleter `SDLFreeDeleter` allibera amb SDL_free. + std::unique_ptr buffer; }; struct JA_Channel_t { @@ -44,21 +63,22 @@ struct JA_Channel_t { struct JA_Music_t { SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000}; - // OGG comprimit en memòria. Propietat nostra; es copia des del fitxer una - // sola vegada en JA_LoadMusic i es descomprimix en chunks per streaming. - Uint8* ogg_data{nullptr}; - Uint32 ogg_length{0}; - stb_vorbis* vorbis{nullptr}; // Handle del decoder, viu tot el cicle del JA_Music_t + // OGG comprimit en memòria. Propietat nostra; es copia des del buffer + // d'entrada una sola vegada en JA_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 JA_Music_t - char* filename{nullptr}; + std::string filename; - int times{0}; // Loops restants (-1 = infinit, 0 = un sol play) + int times{0}; // loops restants (-1 = infinit, 0 = un sol play) SDL_AudioStream* stream{nullptr}; JA_Music_state state{JA_MUSIC_INVALID}; }; -// --- Internal Global State --- -// Marcado 'inline' (C++17) para asegurar una única instancia. +// --- Internal Global State (inline, C++17) --- inline JA_Music_t* current_music{nullptr}; inline JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS]; @@ -70,15 +90,27 @@ inline bool JA_musicEnabled{true}; inline bool JA_soundEnabled{true}; inline SDL_AudioDeviceID sdlAudioDevice{0}; -inline bool fading{false}; -inline int fade_start_time{0}; -inline int fade_duration{0}; -inline float fade_initial_volume{0.0f}; // Corregido de 'int' a 'float' +// --- Crossfade / Fade State --- +struct JA_FadeState { + bool active{false}; + Uint64 start_time{0}; + int duration_ms{0}; + float initial_volume{0.0f}; +}; + +struct JA_OutgoingMusic { + SDL_AudioStream* stream{nullptr}; + JA_FadeState fade; +}; + +inline JA_OutgoingMusic outgoing_music; +inline JA_FadeState incoming_fade; // --- Forward Declarations --- inline void JA_StopMusic(); inline void JA_StopChannel(const int channel); inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop = 0, const int group = 0); +inline void JA_CrossfadeMusic(JA_Music_t* music, int crossfade_ms, int loop = -1); // --- Music streaming internals --- // Bytes-per-sample per canal (sempre s16) @@ -96,15 +128,15 @@ inline int JA_FeedMusicChunk(JA_Music_t* music) { if (!music || !music->vorbis || !music->stream) return 0; short chunk[JA_MUSIC_CHUNK_SHORTS]; - const int channels = music->spec.channels; + const int num_channels = music->spec.channels; const int samples_per_channel = stb_vorbis_get_samples_short_interleaved( music->vorbis, - channels, + num_channels, chunk, JA_MUSIC_CHUNK_SHORTS); if (samples_per_channel <= 0) return 0; - const int bytes = samples_per_channel * channels * JA_MUSIC_BYTES_PER_SAMPLE; + const int bytes = samples_per_channel * num_channels * JA_MUSIC_BYTES_PER_SAMPLE; SDL_PutAudioStreamData(music->stream, chunk, bytes); return samples_per_channel; } @@ -131,20 +163,51 @@ inline void JA_PumpMusic(JA_Music_t* music) { } } +// Pre-carrega `duration_ms` de so dins l'stream actual abans que l'stream +// siga robat per outgoing_music (crossfade o fade-out). Imprescindible amb +// streaming: l'stream robat no es pot re-alimentar perquè perd la referència +// al seu vorbis decoder. No aplica loop — si el vorbis s'esgota abans, parem. +inline void JA_PreFillOutgoing(JA_Music_t* music, int duration_ms) { + if (!music || !music->vorbis || !music->stream) return; + + const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE; + const int needed_bytes = static_cast((static_cast(duration_ms) * bytes_per_second) / 1000); + + while (SDL_GetAudioStreamAvailable(music->stream) < needed_bytes) { + const int decoded = JA_FeedMusicChunk(music); + if (decoded <= 0) break; // EOF: deixem drenar el que hi haja + } +} + // --- Core Functions --- inline void JA_Update() { + // --- Outgoing music fade-out (crossfade o fade-out a silencio) --- + if (outgoing_music.stream && 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 && current_music->state == JA_MUSIC_PLAYING) { - if (fading) { - int time = SDL_GetTicks(); - if (time > (fade_start_time + fade_duration)) { - fading = false; - JA_StopMusic(); - return; + // 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 { - const int time_passed = time - fade_start_time; - const float percent = (float)time_passed / (float)fade_duration; - SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * (1.0 - percent)); + float percent = (float)elapsed / (float)incoming_fade.duration_ms; + SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * percent); } } @@ -156,12 +219,13 @@ inline void JA_Update() { } } + // --- 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, channels[i].sound->length); + SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer.get(), channels[i].sound->length); if (channels[i].times > 0) channels[i].times--; } } else { @@ -172,20 +236,20 @@ inline void JA_Update() { } inline void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) { -#ifdef _DEBUG - SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); -#endif - JA_audioSpec = {format, num_channels, freq}; - if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); // Corregido: !sdlAudioDevice -> sdlAudioDevice + if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec); - if (sdlAudioDevice == 0) SDL_Log("Failed to initialize SDL audio!"); + if (sdlAudioDevice == 0) std::cout << "Failed to initialize SDL audio!" << '\n'; for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE; for (int i = 0; i < JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f; } inline void JA_Quit() { - if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); // Corregido: !sdlAudioDevice -> sdlAudioDevice + if (outgoing_music.stream) { + SDL_DestroyAudioStream(outgoing_music.stream); + outgoing_music.stream = nullptr; + } + if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); sdlAudioDevice = 0; } @@ -194,26 +258,25 @@ inline void JA_Quit() { inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) { if (!buffer || length == 0) return nullptr; - // Còpia del OGG comprimit: stb_vorbis llig de forma persistent aquesta - // memòria mentre el handle estiga viu, així que hem de posseir-la nosaltres. - Uint8* ogg_copy = static_cast(SDL_malloc(length)); - if (!ogg_copy) return nullptr; - SDL_memcpy(ogg_copy, buffer, length); + // Allocem el JA_Music_t primer per aprofitar el seu `std::vector` + // com a propietari del OGG comprimit. stb_vorbis guarda un punter + // persistent al buffer; com que ací no el resize'jem, el .data() és + // estable durant tot el cicle de vida del music. + auto* music = new JA_Music_t(); + music->ogg_data.assign(buffer, buffer + length); int error = 0; - stb_vorbis* vorbis = stb_vorbis_open_memory(ogg_copy, static_cast(length), &error, nullptr); - if (!vorbis) { - SDL_free(ogg_copy); - SDL_Log("JA_LoadMusic: stb_vorbis_open_memory failed (error %d)", error); + music->vorbis = stb_vorbis_open_memory(music->ogg_data.data(), + static_cast(length), + &error, + nullptr); + if (!music->vorbis) { + std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << error << ")" << '\n'; + delete music; return nullptr; } - auto* music = new JA_Music_t(); - music->ogg_data = ogg_copy; - music->ogg_length = length; - music->vorbis = vorbis; - - const stb_vorbis_info info = stb_vorbis_get_info(vorbis); + const stb_vorbis_info info = stb_vorbis_get_info(music->vorbis); music->spec.channels = info.channels; music->spec.freq = static_cast(info.sample_rate); music->spec.format = SDL_AUDIO_S16; @@ -222,31 +285,36 @@ inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) { return music; } +// Overload amb filename — els callers l'usen per poder comparar la música +// en curs amb JA_GetMusicFilename() i no rearrancar-la si ja és la mateixa. +inline JA_Music_t* JA_LoadMusic(Uint8* buffer, Uint32 length, const char* filename) { + JA_Music_t* music = JA_LoadMusic(static_cast(buffer), length); + if (music && filename) music->filename = filename; + return music; +} + inline JA_Music_t* JA_LoadMusic(const char* filename) { - // [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid. + // Carreguem primer el arxiu en memòria i després el descomprimim. FILE* f = fopen(filename, "rb"); - if (!f) return NULL; // Añadida comprobación de apertura + if (!f) return nullptr; fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); auto* buffer = static_cast(malloc(fsize + 1)); - if (!buffer) { // Añadida comprobación de malloc + if (!buffer) { fclose(f); - return NULL; + return nullptr; } if (fread(buffer, fsize, 1, f) != 1) { fclose(f); free(buffer); - return NULL; + return nullptr; } fclose(f); - JA_Music_t* music = JA_LoadMusic(buffer, fsize); - if (music) { // Comprobar que JA_LoadMusic tuvo éxito - music->filename = static_cast(malloc(strlen(filename) + 1)); - if (music->filename) { - strcpy(music->filename, filename); - } + JA_Music_t* music = JA_LoadMusic(static_cast(buffer), static_cast(fsize)); + if (music) { + music->filename = filename; } free(buffer); @@ -269,7 +337,7 @@ inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) { current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec); if (!current_music->stream) { - SDL_Log("Failed to create audio stream!"); + std::cout << "Failed to create audio stream!" << '\n'; current_music->state = JA_MUSIC_STOPPED; return; } @@ -278,18 +346,20 @@ inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) { // Pre-cargem el buffer abans de bindejar per evitar un underrun inicial. JA_PumpMusic(current_music); - if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) printf("[ERROR] SDL_BindAudioStream failed!\n"); + if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) { + std::cout << "[ERROR] SDL_BindAudioStream failed!" << '\n'; + } } -inline char* JA_GetMusicFilename(const JA_Music_t* music = nullptr) { +inline const char* JA_GetMusicFilename(const JA_Music_t* music = nullptr) { if (!music) music = current_music; - if (!music) return nullptr; // Añadida comprobación - return music->filename; + if (!music || music->filename.empty()) return nullptr; + return music->filename.c_str(); } inline void JA_PauseMusic() { if (!JA_musicEnabled) return; - if (!current_music || current_music->state != JA_MUSIC_PLAYING) return; // Comprobación mejorada + if (!current_music || current_music->state != JA_MUSIC_PLAYING) return; current_music->state = JA_MUSIC_PAUSED; SDL_UnbindAudioStream(current_music->stream); @@ -297,13 +367,21 @@ inline void JA_PauseMusic() { inline void JA_ResumeMusic() { if (!JA_musicEnabled) return; - if (!current_music || current_music->state != JA_MUSIC_PAUSED) return; // Comprobación mejorada + if (!current_music || current_music->state != JA_MUSIC_PAUSED) return; current_music->state = JA_MUSIC_PLAYING; SDL_BindAudioStream(sdlAudioDevice, current_music->stream); } inline void JA_StopMusic() { + // Limpiar outgoing crossfade si existe + if (outgoing_music.stream) { + SDL_DestroyAudioStream(outgoing_music.stream); + outgoing_music.stream = nullptr; + outgoing_music.fade.active = false; + } + incoming_fade.active = false; + if (!current_music || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) return; current_music->state = JA_MUSIC_STOPPED; @@ -316,17 +394,73 @@ inline void JA_StopMusic() { if (current_music->vorbis) { stb_vorbis_seek_start(current_music->vorbis); } - // No liberem filename aquí; es fa en JA_DeleteMusic. } inline void JA_FadeOutMusic(const int milliseconds) { if (!JA_musicEnabled) return; - if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) return; + if (!current_music || current_music->state != JA_MUSIC_PLAYING) return; - fading = true; - fade_start_time = SDL_GetTicks(); - fade_duration = milliseconds; - fade_initial_volume = JA_musicVolume; + // Destruir outgoing anterior si existe + if (outgoing_music.stream) { + SDL_DestroyAudioStream(outgoing_music.stream); + outgoing_music.stream = nullptr; + } + + // Pre-omplim l'stream amb `milliseconds` de so: un cop robat, ja no + // tindrà accés al vorbis decoder i només podrà drenar el que tinga. + JA_PreFillOutgoing(current_music, milliseconds); + + // Robar el stream del current_music al outgoing + outgoing_music.stream = current_music->stream; + outgoing_music.fade = {true, SDL_GetTicks(), milliseconds, JA_musicVolume}; + + // Dejar current_music sin stream (ya lo tiene outgoing) + current_music->stream = nullptr; + current_music->state = JA_MUSIC_STOPPED; + if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis); + incoming_fade.active = false; +} + +inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const int loop) { + if (!JA_musicEnabled || !music || !music->vorbis) return; + + // Destruir outgoing anterior si existe (crossfade durante crossfade) + if (outgoing_music.stream) { + SDL_DestroyAudioStream(outgoing_music.stream); + outgoing_music.stream = nullptr; + outgoing_music.fade.active = false; + } + + // Robar el stream de la musica actual al outgoing para el fade-out. + // Pre-omplim amb `crossfade_ms` de so perquè no es quede en silenci + // abans d'acabar el fade (l'stream robat ja no pot alimentar-se). + if (current_music && current_music->state == JA_MUSIC_PLAYING && current_music->stream) { + JA_PreFillOutgoing(current_music, crossfade_ms); + outgoing_music.stream = current_music->stream; + outgoing_music.fade = {true, SDL_GetTicks(), crossfade_ms, JA_musicVolume}; + current_music->stream = nullptr; + current_music->state = JA_MUSIC_STOPPED; + if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis); + } + + // Iniciar la nueva pista con gain=0 (el fade-in la sube gradualmente) + current_music = music; + current_music->state = JA_MUSIC_PLAYING; + current_music->times = loop; + + stb_vorbis_seek_start(current_music->vorbis); + current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec); + if (!current_music->stream) { + std::cout << "Failed to create audio stream for crossfade!" << '\n'; + current_music->state = JA_MUSIC_STOPPED; + return; + } + SDL_SetAudioStreamGain(current_music->stream, 0.0f); + JA_PumpMusic(current_music); // pre-carrega abans de bindejar + SDL_BindAudioStream(sdlAudioDevice, current_music->stream); + + // Configurar fade-in + incoming_fade = {true, SDL_GetTicks(), crossfade_ms, 0.0f}; } inline JA_Music_state JA_GetMusicState() { @@ -344,8 +478,8 @@ inline void JA_DeleteMusic(JA_Music_t* music) { } if (music->stream) SDL_DestroyAudioStream(music->stream); if (music->vorbis) stb_vorbis_close(music->vorbis); - SDL_free(music->ogg_data); - free(music->filename); // filename es libera aquí + // ogg_data (std::vector) i filename (std::string) s'alliberen sols + // al destructor de JA_Music_t. delete music; } @@ -358,49 +492,40 @@ inline float JA_SetMusicVolume(float volume) { } inline void JA_SetMusicPosition(float /*value*/) { - // No implementat amb el backend de streaming. Mai va arribar a usar-se - // en el codi existent, així que es manté com a stub. + // No implementat amb el backend de streaming. } inline float JA_GetMusicPosition() { - // Veure nota a JA_SetMusicPosition. return 0.0f; } inline void JA_EnableMusic(const bool value) { if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING)) JA_StopMusic(); - JA_musicEnabled = value; } // --- Sound Functions --- -inline JA_Sound_t* JA_NewSound(Uint8* buffer, Uint32 length) { - JA_Sound_t* sound = new JA_Sound_t(); - sound->buffer = buffer; - sound->length = length; - // Nota: spec se queda con los valores por defecto. - return sound; -} - inline JA_Sound_t* JA_LoadSound(uint8_t* buffer, uint32_t size) { - JA_Sound_t* sound = new JA_Sound_t(); - if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &sound->buffer, &sound->length)) { - SDL_Log("Failed to load WAV from memory: %s", SDL_GetError()); - delete sound; + auto sound = std::make_unique(); + Uint8* raw = nullptr; + if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &raw, &sound->length)) { + std::cout << "Failed to load WAV from memory: " << SDL_GetError() << '\n'; return nullptr; } - return sound; + sound->buffer.reset(raw); // adopta el SDL_malloc'd buffer + return sound.release(); } inline JA_Sound_t* JA_LoadSound(const char* filename) { - JA_Sound_t* sound = new JA_Sound_t(); - if (!SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length)) { - SDL_Log("Failed to load WAV file: %s", SDL_GetError()); - delete sound; + auto sound = std::make_unique(); + Uint8* raw = nullptr; + if (!SDL_LoadWAV(filename, &sound->spec, &raw, &sound->length)) { + std::cout << "Failed to load WAV file: " << SDL_GetError() << '\n'; return nullptr; } - return sound; + sound->buffer.reset(raw); // adopta el SDL_malloc'd buffer + return sound.release(); } inline int JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group = 0) { @@ -420,22 +545,22 @@ inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int if (!JA_soundEnabled || !sound) return -1; if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1; - JA_StopChannel(channel); // Detiene y limpia el canal si estaba en uso + JA_StopChannel(channel); channels[channel].sound = sound; channels[channel].times = loop; channels[channel].pos = 0; - channels[channel].group = group; // Asignar grupo + channels[channel].group = group; channels[channel].state = JA_CHANNEL_PLAYING; channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec); if (!channels[channel].stream) { - SDL_Log("Failed to create audio stream for sound!"); + std::cout << "Failed to create audio stream for sound!" << '\n'; channels[channel].state = JA_CHANNEL_FREE; return -1; } - SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length); + SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer.get(), channels[channel].sound->length); SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]); SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream); @@ -447,7 +572,7 @@ inline void JA_DeleteSound(JA_Sound_t* sound) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].sound == sound) JA_StopChannel(i); } - SDL_free(sound->buffer); + // buffer es destrueix automàticament via RAII (SDLFreeDeleter). delete sound; } @@ -493,7 +618,7 @@ inline void JA_StopChannel(const int channel) { channels[i].stream = nullptr; channels[i].state = JA_CHANNEL_FREE; channels[i].pos = 0; - channels[i].sound = NULL; + channels[i].sound = nullptr; } } } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { @@ -502,7 +627,7 @@ inline void JA_StopChannel(const int channel) { channels[channel].stream = nullptr; channels[channel].state = JA_CHANNEL_FREE; channels[channel].pos = 0; - channels[channel].sound = NULL; + channels[channel].sound = nullptr; } } } @@ -514,8 +639,7 @@ inline JA_Channel_state JA_GetChannelState(const int channel) { return channels[channel].state; } -inline float JA_SetSoundVolume(float volume, const int group = -1) // -1 para todos los grupos -{ +inline float JA_SetSoundVolume(float volume, const int group = -1) { const float v = SDL_clamp(volume, 0.0f, 1.0f); if (group == -1) { @@ -525,10 +649,10 @@ inline float JA_SetSoundVolume(float volume, const int group = -1) // -1 para t } else if (group >= 0 && group < JA_MAX_GROUPS) { JA_soundVolume[group] = v; } else { - return v; // Grupo inválido + return v; } - // Aplicar volumen a canales activos + // Aplicar volum als canals actius. for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) { if (group == -1 || channels[i].group == group) { @@ -543,13 +667,13 @@ inline float JA_SetSoundVolume(float volume, const int group = -1) // -1 para t inline void JA_EnableSound(const bool value) { if (!value) { - JA_StopChannel(-1); // Detener todos los canales + JA_StopChannel(-1); } JA_soundEnabled = value; } inline float JA_SetVolume(float volume) { float v = JA_SetMusicVolume(volume); - JA_SetSoundVolume(v, -1); // Aplicar a todos los grupos de sonido + JA_SetSoundVolume(v, -1); return v; -} \ No newline at end of file +} diff --git a/source/external/stb_vorbis.h b/source/external/stb_vorbis.c similarity index 98% rename from source/external/stb_vorbis.h rename to source/external/stb_vorbis.c index 49df433..7e5daa3 100644 --- a/source/external/stb_vorbis.h +++ b/source/external/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.20 - public domain +// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -29,12 +29,15 @@ // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// github:manxorist saga musix github:infatum +// github:manxorist Saga Musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller // github:audinowho Dougall Johnson David Reid // github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier // // Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments // 1.20 - 2020-07-11 - several small fixes // 1.19 - 2020-02-05 - warnings // 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. @@ -220,6 +223,12 @@ extern int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with @@ -579,7 +588,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -646,6 +655,12 @@ typedef signed int int32; typedef float codetype; +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + // @NOTE // // Some arrays below are tagged "//varies", which means it's actually @@ -1046,7 +1061,7 @@ static float float32_unpack(uint32 x) uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); + return (float) ldexp((float)res, (int)exp-788); } @@ -1077,6 +1092,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves @@ -1090,6 +1106,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -1099,12 +1116,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); @@ -2577,34 +2592,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, while (z > base) { float k00,k11; + float l00,l11; - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; iter_54(z); iter_54(z-8); @@ -3069,6 +3083,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) @@ -3171,6 +3186,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // WINDOWING + STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; @@ -3368,7 +3384,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3642,9 +3658,11 @@ static int start_decoder(vorb *f) f->vendor[len] = (char)'\0'; //user comments f->comment_list_length = get32_packet(f); - if (f->comment_list_length > 0) { - f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); - if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); } for(i=0; i < f->comment_list_length; ++i) { @@ -3867,8 +3885,7 @@ static int start_decoder(vorb *f) unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; + float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; @@ -3951,7 +3968,7 @@ static int start_decoder(vorb *f) if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f,8)-1; + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } @@ -4509,6 +4526,7 @@ stb_vorbis *stb_vorbis_open_pushdata( *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); @@ -4566,7 +4584,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; @@ -4970,7 +4988,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // set. whoops! break; } - previous_safe = last_page_loc+1; + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } @@ -5081,7 +5099,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; @@ -5156,11 +5177,11 @@ static void copy_samples(short *dest, float *src, int len) static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { @@ -5177,16 +5198,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in output[o+i] = v; } } + #undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); @@ -5216,6 +5238,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d output[o2+i] = v; } } + #undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) @@ -5288,8 +5311,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short float **outputs; int len = num_shorts / channels; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; @@ -5308,8 +5329,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in { float **outputs; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n;