From 56d7d4af52d77b2f25da0bbaafb23736d4154f3d Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 24 May 2026 11:49:14 +0200 Subject: [PATCH] feat(service-menu): pobla AUDIO amb toggles i sliders de volum --- data/locale/ca.yaml | 7 ++ data/locale/en.yaml | 7 ++ source/core/audio/audio.cpp | 21 +++++- source/core/audio/audio.hpp | 8 ++ source/core/system/service_menu.cpp | 109 +++++++++++++++++++++++++++- source/core/system/service_menu.hpp | 1 + 6 files changed, 148 insertions(+), 5 deletions(-) diff --git a/data/locale/ca.yaml b/data/locale/ca.yaml index 8f124df..565aeab 100644 --- a/data/locale/ca.yaml +++ b/data/locale/ca.yaml @@ -61,6 +61,13 @@ service_menu: video_vsync: "VSYNC" video_aa: "ANTIALIAS" video_postfx: "POSTPROCESSAT" + # Items del submenu AUDIO + audio_master: "AUDIO" + audio_master_volume: "VOLUM GENERAL" + audio_music: "MUSICA" + audio_music_volume: "VOLUM MUSICA" + audio_sound: "EFECTES" + audio_sound_volume: "VOLUM EFECTES" # Valors comuns value_on: "ACTIU" value_off: "INACTIU" diff --git a/data/locale/en.yaml b/data/locale/en.yaml index 45efd8c..d107c96 100644 --- a/data/locale/en.yaml +++ b/data/locale/en.yaml @@ -60,6 +60,13 @@ service_menu: video_vsync: "VSYNC" video_aa: "ANTIALIAS" video_postfx: "POSTPROCESS" + # Items of AUDIO submenu + audio_master: "AUDIO" + audio_master_volume: "MASTER VOLUME" + audio_music: "MUSIC" + audio_music_volume: "MUSIC VOLUME" + audio_sound: "SOUNDS" + audio_sound_volume: "SOUND VOLUME" # Common values value_on: "ON" value_off: "OFF" diff --git a/source/core/audio/audio.cpp b/source/core/audio/audio.cpp index 8bb79d0..d5fbd5b 100644 --- a/source/core/audio/audio.cpp +++ b/source/core/audio/audio.cpp @@ -238,14 +238,27 @@ auto Audio::effectiveVolume(float volume, bool channel_enabled) const -> float { return (enabled_ && channel_enabled) ? volume * config_.volume : 0.0F; } -// Estableix el volum dels sons (float 0.0..1.0) +// Estableix el volum dels sons (float 0.0..1.0). Actualitza el valor cachejat +// a config_ perquè els getters i les re-aplicacions internes (enableSound, +// setMasterVolume) puguin tornar al volum que l'usuari va triar. void Audio::setSoundVolume(float sound_volume, Group group) { - engine_->setSoundVolume(effectiveVolume(sound_volume, sound_enabled_), static_cast(group)); + config_.sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME); + engine_->setSoundVolume(effectiveVolume(config_.sound_volume, sound_enabled_), static_cast(group)); } -// Estableix el volum de la música (float 0.0..1.0) +// Estableix el volum de la música (float 0.0..1.0). Cf. setSoundVolume. void Audio::setMusicVolume(float music_volume) { - engine_->setMusicVolume(effectiveVolume(music_volume, music_enabled_)); + config_.music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME); + engine_->setMusicVolume(effectiveVolume(config_.music_volume, music_enabled_)); +} + +// Estableix el volum master (multiplicador aplicat a sound + music). Re-aplica +// els canals perquè el canvi tingui efecte immediat sense esperar al següent +// setSoundVolume/setMusicVolume explícit. +void Audio::setMasterVolume(float master_volume) { + config_.volume = std::clamp(master_volume, MIN_VOLUME, MAX_VOLUME); + setSoundVolume(config_.sound_volume); + setMusicVolume(config_.music_volume); } // Aplica una nueva configuración (substitueix la config cachejada i reaplica enables/volums) diff --git a/source/core/audio/audio.hpp b/source/core/audio/audio.hpp index e9f522f..e7fed27 100644 --- a/source/core/audio/audio.hpp +++ b/source/core/audio/audio.hpp @@ -101,6 +101,14 @@ class Audio { // --- Control de volum (API interna: float 0.0..1.0) --- void setSoundVolume(float volume, Group group = Group::ALL); // Ajusta el volum dels efectes void setMusicVolume(float volume); // Ajusta el volum de la música + void setMasterVolume(float volume); // Ajusta el master (re-aplica sound + music) + + // Getters dels volums actuals (lectura de la config_ cachejada). Reflexen + // el valor que l'usuari ha triat l'última vegada, independent del gating + // d'enabled/channel. + [[nodiscard]] auto getMasterVolume() const -> float { return config_.volume; } + [[nodiscard]] auto getSoundVolume() const -> float { return config_.sound_volume; } + [[nodiscard]] auto getMusicVolume() const -> float { return config_.music_volume; } // --- Helpers de conversió para la capa de presentació --- // UI (menús, notificacions) manega enters 0..100; internament viu float 0..1. diff --git a/source/core/system/service_menu.cpp b/source/core/system/service_menu.cpp index 198133b..a2d6819 100644 --- a/source/core/system/service_menu.cpp +++ b/source/core/system/service_menu.cpp @@ -11,6 +11,7 @@ #include "core/audio/audio.hpp" #include "core/config/engine_config.hpp" +#include "core/defaults/audio.hpp" #include "core/defaults/service_menu.hpp" #include "core/locale/locale.hpp" #include "core/rendering/sdl_manager.hpp" @@ -116,7 +117,7 @@ namespace System { root.title_key = "service_menu.title"; root.items = { makeSubmenu("service_menu.video", [this] { pushPage(buildVideoPage()); }), - makeSubmenu("service_menu.audio", [this] { pushSubmenuPlaceholder("service_menu.audio"); }), + makeSubmenu("service_menu.audio", [this] { pushPage(buildAudioPage()); }), makeSubmenu("service_menu.options", [this] { pushSubmenuPlaceholder("service_menu.options"); }), makeSubmenu("service_menu.system", [this] { pushSubmenuPlaceholder("service_menu.system"); }), }; @@ -197,6 +198,112 @@ namespace System { return page; } + auto ServiceMenu::buildAudioPage() -> Page { + auto on_off_text = [](bool v) -> std::string { + return Locale::get().text(v ? "service_menu.value_on" : "service_menu.value_off"); + }; + + // Aplica un step de volum (±VOLUME_STEP) a un valor 0..1 i retorna el + // resultat clampat. El motor s'encarrega d'aplicar-lo amb el getter. + auto step_volume = [](float current, int dir) -> float { + const float STEP = Defaults::Audio::VOLUME_STEP; + return std::clamp(current + (static_cast(dir) * STEP), 0.0F, 1.0F); + }; + + Page page; + page.title_key = "service_menu.audio"; + page.items = { + // AUDIO (master ON/OFF) + Item{ + .kind = Kind::TOGGLE, + .label_key = "service_menu.audio_master", + .selectable = true, + .on_activate = {}, + .get_value_text = [on_off_text] { + const Audio* a = Audio::get(); + return on_off_text(a != nullptr && a->isEnabled()); }, + .on_change = [](int) { + if (auto* a = Audio::get(); a != nullptr) { + a->toggleEnabled(); + } }, + }, + // VOLUM GENERAL (master) + Item{ + .kind = Kind::INT_RANGE, + .label_key = "service_menu.audio_master_volume", + .selectable = true, + .on_activate = {}, + .get_value_text = [] { + const Audio* a = Audio::get(); + const float V = (a != nullptr) ? a->getMasterVolume() : 0.0F; + return std::to_string(Audio::toPercent(V)); }, + .on_change = [step_volume](int dir) { + if (auto* a = Audio::get(); a != nullptr) { + a->setMasterVolume(step_volume(a->getMasterVolume(), dir)); + } }, + }, + // MUSICA ON/OFF + Item{ + .kind = Kind::TOGGLE, + .label_key = "service_menu.audio_music", + .selectable = true, + .on_activate = {}, + .get_value_text = [on_off_text] { + const Audio* a = Audio::get(); + return on_off_text(a != nullptr && a->isMusicEnabled()); }, + .on_change = [](int) { + if (auto* a = Audio::get(); a != nullptr) { + a->toggleMusic(); + } }, + }, + // VOLUM MUSICA + Item{ + .kind = Kind::INT_RANGE, + .label_key = "service_menu.audio_music_volume", + .selectable = true, + .on_activate = {}, + .get_value_text = [] { + const Audio* a = Audio::get(); + const float V = (a != nullptr) ? a->getMusicVolume() : 0.0F; + return std::to_string(Audio::toPercent(V)); }, + .on_change = [step_volume](int dir) { + if (auto* a = Audio::get(); a != nullptr) { + a->setMusicVolume(step_volume(a->getMusicVolume(), dir)); + } }, + }, + // SONS ON/OFF + Item{ + .kind = Kind::TOGGLE, + .label_key = "service_menu.audio_sound", + .selectable = true, + .on_activate = {}, + .get_value_text = [on_off_text] { + const Audio* a = Audio::get(); + return on_off_text(a != nullptr && a->isSoundEnabled()); }, + .on_change = [](int) { + if (auto* a = Audio::get(); a != nullptr) { + a->toggleSound(); + } }, + }, + // VOLUM SONS + Item{ + .kind = Kind::INT_RANGE, + .label_key = "service_menu.audio_sound_volume", + .selectable = true, + .on_activate = {}, + .get_value_text = [] { + const Audio* a = Audio::get(); + const float V = (a != nullptr) ? a->getSoundVolume() : 0.0F; + return std::to_string(Audio::toPercent(V)); }, + .on_change = [step_volume](int dir) { + if (auto* a = Audio::get(); a != nullptr) { + a->setSoundVolume(step_volume(a->getSoundVolume(), dir)); + } }, + }, + }; + return page; + } + void ServiceMenu::pushPage(Page page) { stack_.push_back(std::move(page)); // El cursor salta a una pagina nova: enganxem el highlight per a diff --git a/source/core/system/service_menu.hpp b/source/core/system/service_menu.hpp index cd9e0f7..24210cc 100644 --- a/source/core/system/service_menu.hpp +++ b/source/core/system/service_menu.hpp @@ -88,6 +88,7 @@ namespace System { void buildRootPage(); void pushSubmenuPlaceholder(const std::string& title_key); [[nodiscard]] auto buildVideoPage() const -> Page; + [[nodiscard]] static auto buildAudioPage() -> Page; void pushPage(Page page); void popPage(); void moveCursor(int direction);