Subsistema d'àudio: port de ../aee/core/audio refactoritzat amb namespace Ja, desacoblat d'Options via Audio::Config i extensió de Resource::Cache per preloading de música i sons; pyramid_1.ogg sona a GameScene (v0.15.0)
This commit is contained in:
1 parent
6f3fafbfff
commit
e2b8d0e459
16 files changed
+6796
-5
No files matched your search
@@ -0,0 +1,213 @@
|
||||
#include "core/audio/audio.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_GetError, SDL_Init
|
||||
|
||||
#include <algorithm> // Para clamp
|
||||
#include <iostream> // Para std::cout
|
||||
|
||||
// Implementació de stb_vorbis (ha d'anar ABANS d'incloure jail_audio.hpp).
|
||||
// clang-format off
|
||||
#undef STB_VORBIS_HEADER_ONLY
|
||||
// NOLINTNEXTLINE(bugprone-suspicious-include)
|
||||
#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/audio_adapter.hpp" // Para AudioResource::getMusic/getSound
|
||||
#include "core/audio/jail_audio.hpp" // Para Ja::* (engine jailgames)
|
||||
|
||||
// Singleton
|
||||
std::unique_ptr<Audio> Audio::instance;
|
||||
|
||||
// Inicialitza la instància única del singleton amb la configuració rebuda
|
||||
void Audio::init(const Config& config) { Audio::instance = std::unique_ptr<Audio>(new Audio(config)); }
|
||||
|
||||
// Libera la instancia
|
||||
void Audio::destroy() { Audio::instance.reset(); }
|
||||
|
||||
// Obtiene la instancia
|
||||
auto Audio::get() -> Audio* { return Audio::instance.get(); }
|
||||
|
||||
// Constructor
|
||||
Audio::Audio(const Config& config)
|
||||
: config_(config) { initSDLAudio(); }
|
||||
|
||||
// Destructor
|
||||
Audio::~Audio() {
|
||||
Ja::quit();
|
||||
}
|
||||
|
||||
// Método principal
|
||||
void Audio::update() {
|
||||
Ja::update();
|
||||
|
||||
// Sincronitzar estat: detectar quan la música s'ha parat (p. ex. fade-out completat)
|
||||
if (instance && instance->music_.state == MusicState::PLAYING && Ja::getMusicState() != Ja::MusicState::PLAYING) {
|
||||
instance->music_.state = MusicState::STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
// Reproduce la música por nombre (con crossfade opcional)
|
||||
void Audio::playMusic(const std::string& name, const int loop, const int crossfade_ms) {
|
||||
const bool NEW_LOOP = (loop != 0);
|
||||
|
||||
// Si ja sona exactament la mateixa pista i mateix mode loop, no fem res
|
||||
if (music_.state == MusicState::PLAYING && music_.name == name && music_.loop == NEW_LOOP) {
|
||||
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);
|
||||
}
|
||||
|
||||
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* 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() {
|
||||
if (music_enabled_ && music_.state == MusicState::PLAYING) {
|
||||
Ja::pauseMusic();
|
||||
music_.state = MusicState::PAUSED;
|
||||
}
|
||||
}
|
||||
|
||||
// Continua la música pausada
|
||||
void Audio::resumeMusic() {
|
||||
if (music_enabled_ && music_.state == MusicState::PAUSED) {
|
||||
Ja::resumeMusic();
|
||||
music_.state = MusicState::PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
// Detiene la música
|
||||
void Audio::stopMusic() {
|
||||
if (music_enabled_) {
|
||||
Ja::stopMusic();
|
||||
music_.state = MusicState::STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
// Reproduce un sonido por nombre
|
||||
void Audio::playSound(const std::string& name, Group group) const {
|
||||
if (sound_enabled_) {
|
||||
Ja::playSound(AudioResource::getSound(name), 0, static_cast<int>(group));
|
||||
}
|
||||
}
|
||||
|
||||
// Reproduce un sonido por puntero directo
|
||||
void Audio::playSound(Ja::Sound* sound, Group group) const {
|
||||
if (sound_enabled_ && sound != nullptr) {
|
||||
Ja::playSound(sound, 0, static_cast<int>(group));
|
||||
}
|
||||
}
|
||||
|
||||
// Detiene todos los sonidos
|
||||
void Audio::stopAllSounds() const {
|
||||
if (sound_enabled_) {
|
||||
Ja::stopChannel(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Realiza un fundido de salida de la música
|
||||
void Audio::fadeOutMusic(int milliseconds) const {
|
||||
if (music_enabled_ && getRealMusicState() == MusicState::PLAYING) {
|
||||
Ja::fadeOutMusic(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
// Consulta directament l'estat real de la música a jail_audio
|
||||
auto Audio::getRealMusicState() -> MusicState {
|
||||
const Ja::MusicState JA_STATE = Ja::getMusicState();
|
||||
switch (JA_STATE) {
|
||||
case Ja::MusicState::PLAYING:
|
||||
return MusicState::PLAYING;
|
||||
case Ja::MusicState::PAUSED:
|
||||
return MusicState::PAUSED;
|
||||
case Ja::MusicState::STOPPED:
|
||||
case Ja::MusicState::INVALID:
|
||||
case Ja::MusicState::DISABLED:
|
||||
default:
|
||||
return MusicState::STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
// Estableix el volum dels sons (float 0.0..1.0)
|
||||
void Audio::setSoundVolume(float sound_volume, Group group) const {
|
||||
sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME);
|
||||
const bool ACTIVE = enabled_ && sound_enabled_;
|
||||
const float CONVERTED_VOLUME = ACTIVE ? sound_volume * config_.volume : 0.0F;
|
||||
Ja::setSoundVolume(CONVERTED_VOLUME, static_cast<int>(group));
|
||||
}
|
||||
|
||||
// Estableix el volum de la música (float 0.0..1.0)
|
||||
void Audio::setMusicVolume(float music_volume) const {
|
||||
music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME);
|
||||
const bool ACTIVE = enabled_ && music_enabled_;
|
||||
const float CONVERTED_VOLUME = ACTIVE ? music_volume * config_.volume : 0.0F;
|
||||
Ja::setMusicVolume(CONVERTED_VOLUME);
|
||||
}
|
||||
|
||||
// Aplica una nova configuració (substitueix la cache i reaplica enables/volums)
|
||||
void Audio::applySettings(const Config& config) {
|
||||
config_ = config;
|
||||
sound_enabled_ = config_.sound_enabled;
|
||||
music_enabled_ = config_.music_enabled;
|
||||
enable(config_.enabled);
|
||||
}
|
||||
|
||||
// Estableix l'estat general
|
||||
void Audio::enable(bool value) {
|
||||
enabled_ = value;
|
||||
|
||||
setSoundVolume(enabled_ ? config_.sound_volume : MIN_VOLUME);
|
||||
setMusicVolume(enabled_ ? config_.music_volume : MIN_VOLUME);
|
||||
}
|
||||
|
||||
// Inicialitza SDL Audio
|
||||
void Audio::initSDLAudio() {
|
||||
if (!SDL_Init(SDL_INIT_AUDIO)) {
|
||||
std::cout << "SDL_AUDIO could not initialize! SDL Error: " << SDL_GetError() << '\n';
|
||||
} else {
|
||||
Ja::init(FREQUENCY, SDL_AUDIO_S16LE, 2);
|
||||
sound_enabled_ = config_.sound_enabled;
|
||||
music_enabled_ = config_.music_enabled;
|
||||
enable(config_.enabled);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user