From d07a4106391cef87dca94d62a85ddff9c000e731 Mon Sep 17 00:00:00 2001 From: Yuri Pourre Date: Tue, 10 Feb 2026 01:56:22 -0800 Subject: [PATCH] First step to migrate accessibility features --- Source/effects.cpp | 12 +++++++++--- Source/engine/sound.cpp | 14 ++++++++++++-- Source/engine/sound.h | 4 +++- Source/engine/sound_stubs.cpp | 3 ++- Source/monster.cpp | 2 +- Source/options.cpp | 2 ++ Source/options.h | 2 ++ Source/sound_effect_enums.h | 12 +++++++++++- Source/utils/screen_reader.cpp | 7 ++++--- Source/utils/screen_reader.hpp | 4 ++-- assets/txtdata/sound/effects.tsv | 10 ++++++++++ mods/Hellfire/txtdata/sound/effects.tsv | 10 ++++++++++ 12 files changed, 68 insertions(+), 14 deletions(-) diff --git a/Source/effects.cpp b/Source/effects.cpp index fd7aec011..eb4841a39 100644 --- a/Source/effects.cpp +++ b/Source/effects.cpp @@ -19,6 +19,7 @@ #include "engine/sound_defs.hpp" #include "engine/sound_position.hpp" #include "game_mode.hpp" +#include "options.h" #include "player.h" #include "utils/is_of.hpp" @@ -92,8 +93,13 @@ void PlaySfxPriv(TSFX *pSFX, bool loc, Point position) if (pSFX->pSnd == nullptr) pSFX->pSnd = sound_file_load(pSFX->pszName.c_str()); - if (pSFX->pSnd != nullptr && pSFX->pSnd->DSB.IsLoaded()) - snd_play_snd(pSFX->pSnd.get(), lVolume, lPan); + if (pSFX->pSnd == nullptr || !pSFX->pSnd->DSB.IsLoaded()) + return; + + const auto id = static_cast(pSFX - sgSFX.data()); + const bool useCuesVolume = (id >= SfxID::AccessibilityWeapon && id <= SfxID::AccessibilityInteract); + const int userVolume = useCuesVolume ? *GetOptions().Audio.audioCuesVolume : *GetOptions().Audio.soundVolume; + snd_play_snd(pSFX->pSnd.get(), lVolume, lPan, userVolume); } SfxID RndSFX(SfxID psfx) @@ -310,7 +316,7 @@ void effects_play_sound(SfxID id) TSFX &sfx = sgSFX[static_cast(id)]; if (sfx.pSnd != nullptr && !sfx.pSnd->isPlaying()) { - snd_play_snd(sfx.pSnd.get(), 0, 0); + snd_play_snd(sfx.pSnd.get(), 0, 0, *GetOptions().Audio.soundVolume); } } diff --git a/Source/engine/sound.cpp b/Source/engine/sound.cpp index d5dcd20ed..49fb0ba7c 100644 --- a/Source/engine/sound.cpp +++ b/Source/engine/sound.cpp @@ -197,7 +197,7 @@ void ClearDuplicateSounds() duplicateSounds.clear(); } -void snd_play_snd(TSnd *pSnd, int lVolume, int lPan) +void snd_play_snd(TSnd *pSnd, int lVolume, int lPan, int userVolume) { if (pSnd == nullptr || !gbSoundOn) { return; @@ -215,7 +215,7 @@ void snd_play_snd(TSnd *pSnd, int lVolume, int lPan) return; } - sound->PlayWithVolumeAndPan(lVolume, *GetOptions().Audio.soundVolume, lPan); + sound->PlayWithVolumeAndPan(lVolume, userVolume, lPan); pSnd->start_tc = tc; } @@ -389,6 +389,16 @@ int sound_get_or_set_sound_volume(int volume) return *GetOptions().Audio.soundVolume; } +int SoundGetOrSetAudioCuesVolume(int volume) +{ + if (volume == 1) + return *GetOptions().Audio.audioCuesVolume; + + GetOptions().Audio.audioCuesVolume.SetValue(volume); + + return *GetOptions().Audio.audioCuesVolume; +} + void music_mute() { if (music.IsLoaded()) diff --git a/Source/engine/sound.h b/Source/engine/sound.h index 0737f78f0..b5a670bb5 100644 --- a/Source/engine/sound.h +++ b/Source/engine/sound.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -64,7 +65,7 @@ extern SDL_AudioDeviceID CurrentAudioDeviceId; extern _music_id sgnMusicTrack; void ClearDuplicateSounds(); -void snd_play_snd(TSnd *pSnd, int lVolume, int lPan); +void snd_play_snd(TSnd *pSnd, int lVolume, int lPan, int userVolume); std::unique_ptr sound_file_load(const char *path, bool stream = false); tl::expected, std::string> SoundFileLoadWithStatus(const char *path, bool stream = false); void snd_init(); @@ -75,6 +76,7 @@ void music_start(_music_id nTrack); void sound_disable_music(bool disable); int sound_get_or_set_music_volume(int volume); int sound_get_or_set_sound_volume(int volume); +int SoundGetOrSetAudioCuesVolume(int volume); void music_mute(); void music_unmute(); diff --git a/Source/engine/sound_stubs.cpp b/Source/engine/sound_stubs.cpp index 45f062b13..3fcefd04c 100644 --- a/Source/engine/sound_stubs.cpp +++ b/Source/engine/sound_stubs.cpp @@ -9,7 +9,7 @@ bool gbSoundOn; _music_id sgnMusicTrack = NUM_MUSIC; void ClearDuplicateSounds() { } -void snd_play_snd(TSnd *pSnd, int lVolume, int lPan) { } +void snd_play_snd(TSnd *pSnd, int lVolume, int lPan, int userVolume) { } std::unique_ptr sound_file_load(const char *path, bool stream) { return nullptr; } tl::expected, std::string> SoundFileLoadWithStatus(const char *path, bool stream) { return nullptr; } TSnd::~TSnd() { } @@ -20,6 +20,7 @@ void music_start(_music_id nTrack) { } void sound_disable_music(bool disable) { } int sound_get_or_set_music_volume(int volume) { return 0; } int sound_get_or_set_sound_volume(int volume) { return 0; } +int SoundGetOrSetAudioCuesVolume(int volume) { return 0; } void music_mute() { } void music_unmute() { } _music_id GetLevelMusic(dungeon_type dungeonType) { return TMUSIC_TOWN; } diff --git a/Source/monster.cpp b/Source/monster.cpp index f85334499..9052f3efb 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -4531,7 +4531,7 @@ void PlayEffect(Monster &monster, MonsterSound mode) if (!CalculateSoundPosition(monster.position.tile, &lVolume, &lPan)) return; - snd_play_snd(snd, lVolume, lPan); + snd_play_snd(snd, lVolume, lPan, *GetOptions().Audio.soundVolume); } void MissToMonst(Missile &missile, Point position) diff --git a/Source/options.cpp b/Source/options.cpp index 65ae9a892..52e2730ed 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -511,6 +511,7 @@ std::vector HellfireOptions::GetEntries() AudioOptions::AudioOptions() : OptionCategoryBase("Audio", N_("Audio"), N_("Audio Settings")) , soundVolume("Sound Volume", OptionEntryFlags::Invisible, "Sound Volume", "Movie and SFX volume.", VOLUME_MAX) + , audioCuesVolume("Audio Cues Volume", OptionEntryFlags::Invisible, "Audio Cues Volume", "Navigation audio cues volume.", VOLUME_MAX) , musicVolume("Music Volume", OptionEntryFlags::Invisible, "Music Volume", "Music Volume.", VOLUME_MAX) , walkingSound("Walking Sound", OptionEntryFlags::None, N_("Walking Sound"), N_("Player emits sound when walking."), true) , autoEquipSound("Auto Equip Sound", OptionEntryFlags::None, N_("Auto Equip Sound"), N_("Automatically equipping items on pickup emits the equipment sound."), false) @@ -526,6 +527,7 @@ std::vector AudioOptions::GetEntries() // clang-format off return { &soundVolume, + &audioCuesVolume, &musicVolume, &walkingSound, &autoEquipSound, diff --git a/Source/options.h b/Source/options.h index 7e4da902d..58954f55f 100644 --- a/Source/options.h +++ b/Source/options.h @@ -487,6 +487,8 @@ struct AudioOptions : OptionCategoryBase { /** @brief Movie and SFX volume. */ OptionEntryInt soundVolume; + /** @brief Accessibility / navigation cues volume. */ + OptionEntryInt audioCuesVolume; /** @brief Music volume. */ OptionEntryInt musicVolume; /** @brief Player emits sound when walking. */ diff --git a/Source/sound_effect_enums.h b/Source/sound_effect_enums.h index 9c8822de0..2ab777f0d 100644 --- a/Source/sound_effect_enums.h +++ b/Source/sound_effect_enums.h @@ -1023,8 +1023,18 @@ enum class SfxID : int16_t { NarratorHF4, CryptDoorOpen, CryptDoorClose, + AccessibilityWeapon, + AccessibilityArmor, + AccessibilityGold, + AccessibilityPotion, + AccessibilityScroll, + AccessibilityChest, + AccessibilityDoor, + AccessibilityStairs, + AccessibilityMonster, + AccessibilityInteract, - LAST = CryptDoorClose, + LAST = AccessibilityInteract, None = -1, }; diff --git a/Source/utils/screen_reader.cpp b/Source/utils/screen_reader.cpp index 43e3ecdf9..2c75491cf 100644 --- a/Source/utils/screen_reader.cpp +++ b/Source/utils/screen_reader.cpp @@ -34,18 +34,19 @@ void ShutDownScreenReader() #endif } -void SpeakText(std::string_view text) +void SpeakText(std::string_view text, bool force) { static std::string SpokenText; - if (SpokenText == text) + if (!force && SpokenText == text) return; SpokenText = text; #ifdef _WIN32 const auto textUtf16 = ToWideChar(SpokenText); - Tolk_Output(&textUtf16[0], true); + if (textUtf16 != nullptr) + Tolk_Output(textUtf16.get(), true); #else spd_say(Speechd, SPD_TEXT, SpokenText.c_str()); #endif diff --git a/Source/utils/screen_reader.hpp b/Source/utils/screen_reader.hpp index 64a029c1c..5be8f97aa 100644 --- a/Source/utils/screen_reader.hpp +++ b/Source/utils/screen_reader.hpp @@ -7,7 +7,7 @@ namespace devilution { #ifdef SCREEN_READER_INTEGRATION void InitializeScreenReader(); void ShutDownScreenReader(); -void SpeakText(std::string_view text); +void SpeakText(std::string_view text, bool force = false); #else constexpr void InitializeScreenReader() { @@ -17,7 +17,7 @@ constexpr void ShutDownScreenReader() { } -constexpr void SpeakText(std::string_view text) +constexpr void SpeakText(std::string_view text, bool force = false) { } #endif diff --git a/assets/txtdata/sound/effects.tsv b/assets/txtdata/sound/effects.tsv index 8feceaad9..ea2bdbb77 100644 --- a/assets/txtdata/sound/effects.tsv +++ b/assets/txtdata/sound/effects.tsv @@ -803,3 +803,13 @@ Warlord Stream sfx\monsters\warlrd01.wav Zhar1 Stream sfx\monsters\zhar01.wav Zhar2 Stream sfx\monsters\zhar02.wav DiabloDeath Stream sfx\monsters\diablod.wav +AccessibilityWeapon Misc sfx\accessibility\weapon.mp3 +AccessibilityArmor Misc sfx\accessibility\armor.mp3 +AccessibilityGold Misc sfx\accessibility\gold.mp3 +AccessibilityPotion Misc sfx\accessibility\potion.mp3 +AccessibilityScroll Misc sfx\accessibility\scroll.mp3 +AccessibilityChest Misc sfx\accessibility\chest.mp3 +AccessibilityDoor Misc sfx\accessibility\door.mp3 +AccessibilityStairs Misc sfx\accessibility\stairs.mp3 +AccessibilityMonster Misc sfx\accessibility\monster.mp3 +AccessibilityInteract Misc sfx\accessibility\interact.mp3 \ No newline at end of file diff --git a/mods/Hellfire/txtdata/sound/effects.tsv b/mods/Hellfire/txtdata/sound/effects.tsv index 6faaf25ac..9eff1ecfa 100644 --- a/mods/Hellfire/txtdata/sound/effects.tsv +++ b/mods/Hellfire/txtdata/sound/effects.tsv @@ -906,3 +906,13 @@ NarratorHF9 Stream sfx\hellfire\naratr9.wav NarratorHF4 Stream sfx\hellfire\naratr4.wav CryptDoorOpen Misc sfx\items\cropen.wav CryptDoorClose Misc sfx\items\crclos.wav +AccessibilityWeapon Misc sfx\accessibility\weapon.mp3 +AccessibilityArmor Misc sfx\accessibility\armor.mp3 +AccessibilityGold Misc sfx\accessibility\gold.mp3 +AccessibilityPotion Misc sfx\accessibility\potion.mp3 +AccessibilityScroll Misc sfx\accessibility\scroll.mp3 +AccessibilityChest Misc sfx\accessibility\chest.mp3 +AccessibilityDoor Misc sfx\accessibility\door.mp3 +AccessibilityStairs Misc sfx\accessibility\stairs.mp3 +AccessibilityMonster Misc sfx\accessibility\monster.mp3 +AccessibilityInteract Misc sfx\accessibility\interact.mp3 \ No newline at end of file