diff --git a/source/core/audio/audio.cpp b/source/core/audio/audio.cpp index d5fbd5b..7e5e1a6 100644 --- a/source/core/audio/audio.cpp +++ b/source/core/audio/audio.cpp @@ -51,8 +51,6 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa return; } - if (!music_enabled_) { return; } - auto* resource = AudioResource::getMusic(name); if (resource == nullptr) { return; } @@ -62,7 +60,7 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa // Reprodueix la música per punter (amb crossfade opcional) void Audio::playMusic(Ja::Music* music, const int loop, const int crossfade_ms) { - if (!music_enabled_ || music == nullptr) { return; } + if (music == nullptr) { return; } playMusicInternal(music, loop, crossfade_ms); // Si el Ja::Music es va crear con filename (loadMusic con 3 arguments), el @@ -72,9 +70,12 @@ void Audio::playMusic(Ja::Music* music, const int loop, const int crossfade_ms) } // Camí comú dels dos overloads: fa el dispatch crossfade vs stop+play i -// actualitza el loop cachejat. Els callers s'encarreguen del gating -// (music_enabled_, nullptr, same-track early return) y del nom. L'estat el -// manté Ja (Ja::playMusic posa PLAYING al Ja::Music* corresponent). +// actualitza el loop cachejat. Els callers s'encarreguen del same-track early +// return i del nom. El gate de música deshabilitada NO atura la reproducció: +// effectiveVolume porta el volum efectiu a 0 i la pista continua sonant +// silenciada, per garantir que reactivar la música la torne a sentir sense +// haver de reiniciar la pista. L'estat el manté Ja (Ja::playMusic posa +// PLAYING al Ja::Music* corresponent). void Audio::playMusicInternal(Ja::Music* music, const int loop, const int crossfade_ms) { const bool CURRENTLY_PLAYING = (getMusicState() == MusicState::PLAYING); if (crossfade_ms > 0 && CURRENTLY_PLAYING) { @@ -91,41 +92,35 @@ void Audio::playMusicInternal(Ja::Music* music, const int loop, const int crossf // Pausa la música (l'estat el transiciona Engine::pauseMusic) void Audio::pauseMusic() { - if (music_enabled_ && getMusicState() == MusicState::PLAYING) { + if (getMusicState() == MusicState::PLAYING) { engine_->pauseMusic(); } } // Continua la música pausada (l'estat el transiciona Engine::resumeMusic) void Audio::resumeMusic() { - if (music_enabled_ && getMusicState() == MusicState::PAUSED) { + if (getMusicState() == MusicState::PAUSED) { engine_->resumeMusic(); } } // Atura la música (l'estat el transiciona Engine::stopMusic) void Audio::stopMusic() { - if (music_enabled_) { - engine_->stopMusic(); - } + engine_->stopMusic(); } void Audio::setMusicSpeed(float ratio) { - if (music_enabled_) { - engine_->setMusicSpeed(ratio); - } + engine_->setMusicSpeed(ratio); } // Reprodueix un so per nom void Audio::playSound(const std::string& name, Group group) { - if (sound_enabled_) { - engine_->playSound(AudioResource::getSound(name), 0, static_cast(group)); - } + engine_->playSound(AudioResource::getSound(name), 0, static_cast(group)); } // Reprodueix un so per punter directe void Audio::playSound(Ja::Sound* sound, Group group) { - if (sound_enabled_ && sound != nullptr) { + if (sound != nullptr) { engine_->playSound(sound, 0, static_cast(group)); } } @@ -136,7 +131,6 @@ void Audio::playSound(Ja::Sound* sound, Group group) { // Si l'engine torna -1 (sense canal lliure) o el so no existeix, no fem // la crida al ratio — sin efectes col·laterals. void Audio::playSound(const std::string& name, Group group, float speed) { - if (!sound_enabled_) { return; } auto* sound = AudioResource::getSound(name); if (sound == nullptr) { return; } const int CH = engine_->playSound(sound, 0, static_cast(group)); @@ -149,7 +143,6 @@ void Audio::playSound(const std::string& name, Group group, float speed) { // existeix o l'engine retorna -1 (sin de canals d'efecte plé), cau a playSound // sec — l'usuari sent el so aún que la cua no s'apliqui. void Audio::playSoundWithEcho(const std::string& name, const std::string& preset_name, Group group) { - if (!sound_enabled_) { return; } auto* sound = AudioResource::getSound(name); if (sound == nullptr) { return; } @@ -168,7 +161,6 @@ void Audio::playSoundWithEcho(const std::string& name, const std::string& preset // Reprodueix un so processat per un reverb definit a sounds.yaml. Mateix // fallback que playSoundWithEcho. void Audio::playSoundWithReverb(const std::string& name, const std::string& preset_name, Group group) { - if (!sound_enabled_) { return; } auto* sound = AudioResource::getSound(name); if (sound == nullptr) { return; } @@ -186,14 +178,12 @@ void Audio::playSoundWithReverb(const std::string& name, const std::string& pres // Atura tots los sons void Audio::stopAllSounds() { - if (sound_enabled_) { - engine_->stopChannel(-1); - } + engine_->stopChannel(-1); } // Fa una fosa de sortida de la música void Audio::fadeOutMusic(int milliseconds) { - if (music_enabled_ && getMusicState() == MusicState::PLAYING) { + if (getMusicState() == MusicState::PLAYING) { engine_->fadeOutMusic(milliseconds); } } @@ -269,12 +259,12 @@ void Audio::applySettings(const Config& config) { enable(config_.enabled); } -// Estableix l'estat general +// Estableix l'estat general. Re-aplica els volums actuals; effectiveVolume +// retalla a 0 quan enabled_ és false, sense perdre els valors guardats. void Audio::enable(bool value) { enabled_ = value; - - setSoundVolume(enabled_ ? config_.sound_volume : MIN_VOLUME); - setMusicVolume(enabled_ ? config_.music_volume : MIN_VOLUME); + setSoundVolume(config_.sound_volume); + setMusicVolume(config_.music_volume); } // Estableix l'estat dels sons i reaplica el volum porque los canals actius diff --git a/source/core/config/engine_config.hpp b/source/core/config/engine_config.hpp index e871703..a9a3f7d 100644 --- a/source/core/config/engine_config.hpp +++ b/source/core/config/engine_config.hpp @@ -56,9 +56,19 @@ namespace Config { std::string gamepad_name; // Empty = auto-assign by index }; + struct AudioConfig { + bool enabled{true}; + float volume{1.0F}; // Master 0..1 + bool music_enabled{true}; + float music_volume{1.0F}; + bool sound_enabled{true}; + float sound_volume{0.25F}; + }; + struct EngineConfig { WindowConfig window{}; RenderingConfig rendering{}; + AudioConfig audio{}; PlayerBindings player1{}; PlayerBindings player2{}; KeyboardBindings keyboard_controls{}; // Defaults globals per Input diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 0422a14..f85aa3c 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -11,7 +11,6 @@ #include "core/audio/audio.hpp" #include "core/audio/audio_adapter.hpp" -#include "core/defaults/audio.hpp" #include "core/defaults/window.hpp" #include "core/input/input.hpp" #include "core/input/mouse.hpp" @@ -138,12 +137,12 @@ Director::Director(int argc, char* argv[]) } const Audio::Config AUDIO_CONFIG{ - .enabled = Defaults::Audio::ENABLED, - .volume = Defaults::Audio::VOLUME, - .music_enabled = Defaults::Audio::MUSIC_ENABLED, - .music_volume = Defaults::Audio::MUSIC_VOLUME, - .sound_enabled = Defaults::Audio::SOUND_ENABLED, - .sound_volume = Defaults::Audio::SOUND_VOLUME, + .enabled = cfg_->audio.enabled, + .volume = cfg_->audio.volume, + .music_enabled = cfg_->audio.music_enabled, + .music_volume = cfg_->audio.music_volume, + .sound_enabled = cfg_->audio.sound_enabled, + .sound_volume = cfg_->audio.sound_volume, }; Audio::init(AUDIO_CONFIG); Audio::get()->applySettings(AUDIO_CONFIG); diff --git a/source/core/system/service_menu.cpp b/source/core/system/service_menu.cpp index 67b026f..6960e29 100644 --- a/source/core/system/service_menu.cpp +++ b/source/core/system/service_menu.cpp @@ -299,6 +299,8 @@ namespace System { .on_change = [](int) { if (auto* a = Audio::get(); a != nullptr) { a->toggleEnabled(); + ConfigYaml::engine_config.audio.enabled = a->isEnabled(); + ConfigYaml::saveToFile(); } }, }, // VOLUM GENERAL (master) @@ -315,6 +317,8 @@ namespace System { .on_change = [step_volume](int dir) { if (auto* a = Audio::get(); a != nullptr) { a->setMasterVolume(step_volume(a->getMasterVolume(), dir)); + ConfigYaml::engine_config.audio.volume = a->getMasterVolume(); + ConfigYaml::saveToFile(); } }, }, // MUSICA ON/OFF @@ -330,6 +334,8 @@ namespace System { .on_change = [](int) { if (auto* a = Audio::get(); a != nullptr) { a->toggleMusic(); + ConfigYaml::engine_config.audio.music_enabled = a->isMusicEnabled(); + ConfigYaml::saveToFile(); } }, }, // VOLUM MUSICA @@ -346,6 +352,8 @@ namespace System { .on_change = [step_volume](int dir) { if (auto* a = Audio::get(); a != nullptr) { a->setMusicVolume(step_volume(a->getMusicVolume(), dir)); + ConfigYaml::engine_config.audio.music_volume = a->getMusicVolume(); + ConfigYaml::saveToFile(); } }, }, // SONS ON/OFF @@ -361,6 +369,8 @@ namespace System { .on_change = [](int) { if (auto* a = Audio::get(); a != nullptr) { a->toggleSound(); + ConfigYaml::engine_config.audio.sound_enabled = a->isSoundEnabled(); + ConfigYaml::saveToFile(); } }, }, // VOLUM SONS @@ -377,6 +387,8 @@ namespace System { .on_change = [step_volume](int dir) { if (auto* a = Audio::get(); a != nullptr) { a->setSoundVolume(step_volume(a->getSoundVolume(), dir)); + ConfigYaml::engine_config.audio.sound_volume = a->getSoundVolume(); + ConfigYaml::saveToFile(); } }, }, }; diff --git a/source/game/config_yaml.cpp b/source/game/config_yaml.cpp index 1527912..4c1d9ce 100644 --- a/source/game/config_yaml.cpp +++ b/source/game/config_yaml.cpp @@ -5,6 +5,7 @@ #include #include +#include "core/defaults/audio.hpp" #include "core/defaults/rendering.hpp" #include "core/defaults/window.hpp" #include "external/fkyaml_node.hpp" @@ -17,6 +18,7 @@ namespace ConfigYaml { // Permeten escriure window.width en lloc d'engine_config.window.width. Config::WindowConfig& window = engine_config.window; Config::RenderingConfig& rendering = engine_config.rendering; + Config::AudioConfig& audio = engine_config.audio; Config::PlayerBindings& player1 = engine_config.player1; Config::PlayerBindings& player2 = engine_config.player2; bool& console = engine_config.console; @@ -209,6 +211,14 @@ namespace ConfigYaml { rendering.render_width = Defaults::Rendering::RENDER_WIDTH_DEFAULT; rendering.render_height = Defaults::Rendering::RENDER_HEIGHT_DEFAULT; + // Audio + audio.enabled = Defaults::Audio::ENABLED; + audio.volume = Defaults::Audio::VOLUME; + audio.music_enabled = Defaults::Audio::MUSIC_ENABLED; + audio.music_volume = Defaults::Audio::MUSIC_VOLUME; + audio.sound_enabled = Defaults::Audio::SOUND_ENABLED; + audio.sound_volume = Defaults::Audio::SOUND_VOLUME; + // Idioma locale = "ca"; @@ -307,6 +317,22 @@ namespace ConfigYaml { } } + static void loadAudioConfigFromYaml(const fkyaml::node& yaml) { + if (!yaml.contains("audio")) { + return; + } + const auto& aud = yaml["audio"]; + + auto in_unit_range = [](float v) { return v >= 0.0F && v <= 1.0F; }; + + readField(aud, "enabled", audio.enabled, Defaults::Audio::ENABLED); + readField(aud, "volume", audio.volume, Defaults::Audio::VOLUME, in_unit_range); + readField(aud, "music_enabled", audio.music_enabled, Defaults::Audio::MUSIC_ENABLED); + readField(aud, "music_volume", audio.music_volume, Defaults::Audio::MUSIC_VOLUME, in_unit_range); + readField(aud, "sound_enabled", audio.sound_enabled, Defaults::Audio::SOUND_ENABLED); + readField(aud, "sound_volume", audio.sound_volume, Defaults::Audio::SOUND_VOLUME, in_unit_range); + } + // Carregar controls del player 1 desde YAML static void loadPlayer1ControlsFromYaml(const fkyaml::node& yaml) { if (!yaml.contains("player1")) { @@ -447,6 +473,7 @@ namespace ConfigYaml { // Carregar seccions loadWindowConfigFromYaml(yaml); loadRenderingConfigFromYaml(yaml); + loadAudioConfigFromYaml(yaml); loadPlayer1ControlsFromYaml(yaml); loadPlayer2ControlsFromYaml(yaml); @@ -479,6 +506,17 @@ namespace ConfigYaml { } } + static void saveAudioConfigToYaml(std::ofstream& file) { + file << "# AUDIO\n"; + file << "audio:\n"; + file << " enabled: " << (audio.enabled ? "true" : "false") << " # ON/OFF general\n"; + file << " volume: " << audio.volume << " # Master 0.0-1.0\n"; + file << " music_enabled: " << (audio.music_enabled ? "true" : "false") << "\n"; + file << " music_volume: " << audio.music_volume << " # 0.0-1.0\n"; + file << " sound_enabled: " << (audio.sound_enabled ? "true" : "false") << "\n"; + file << " sound_volume: " << audio.sound_volume << " # 0.0-1.0\n\n"; + } + // Guardar controls del player 1 a YAML static void savePlayer1ControlsToYaml(std::ofstream& file) { file << "# CONTROLS JUGADOR 1\n"; @@ -549,6 +587,8 @@ namespace ConfigYaml { file << "# IDIOMA\n"; file << "locale: " << locale << " # ca | en\n\n"; + saveAudioConfigToYaml(file); + // Guardar controls de jugadors savePlayer1ControlsToYaml(file); savePlayer2ControlsToYaml(file);