diff --git a/Assets/Sounds/volume-change.wav b/Assets/Sounds/volume-change.wav new file mode 100644 index 000000000..24bcc4460 Binary files /dev/null and b/Assets/Sounds/volume-change.wav differ diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 3ec109b0d..317ec4ee3 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -679,7 +679,9 @@ "volumes-step-size-description": "Adjust the step size for volume changes (scroll wheel, keyboard shortcuts).", "volumes-step-size-label": "Volume step size", "volumes-volume-overdrive-description": "Allow raising volume above 100%. May not be supported by all hardware.", - "volumes-volume-overdrive-label": "Allow volume overdrive" + "volumes-volume-overdrive-label": "Allow volume overdrive", + "volumes-volume-feedback-label": "Play volume feedback sound", + "volumes-volume-feedback-description": "Play a feedback sound when adjusting volume" }, "bar": { "appearance-background-opacity-description": "Set the background opacity specifically for the bar.", diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 348b6ac9a..858e99882 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -679,7 +679,9 @@ "volumes-step-size-description": "调整音量变化的步长(滚轮、键盘快捷键)。", "volumes-step-size-label": "音量步长", "volumes-volume-overdrive-description": "允许将音量提高到100%以上。可能不被所有硬件支持。", - "volumes-volume-overdrive-label": "允许音量过载" + "volumes-volume-overdrive-label": "允许音量过载", + "volumes-volume-feedback-label": "播放音量反馈", + "volumes-volume-feedback-description": "调整音量时播放反馈音效" }, "bar": { "appearance-background-opacity-description": "为状态栏设置背景不透明度。", diff --git a/CREDITS.md b/CREDITS.md index a626fdd22..50c7302a7 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -33,6 +33,7 @@ Noctalia Shell is made possible by the incredible work of many open-source proje ## Audio Assets - **[Universfield on Pixabay](https://pixabay.com/users/universfield-28281460/)** - Notification sound effect - **[DrNI on Freesound](https://freesound.org/people/DrNI/sounds/34562/)** - Timer's alarm sound effect +- **[Lucas McCallister on Freesound](http://www.freesound.org/samplesViewSingle.php?id=67091)** - Volume change feedback sound effect ## Special Thanks diff --git a/Commons/Settings.qml b/Commons/Settings.qml index f583a3d8d..2f5bfcc41 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -586,6 +586,7 @@ Singleton { property string visualizerType: "linear" property list mprisBlacklist: [] property string preferredPlayer: "" + property bool volumeFeedback: false } // brightness diff --git a/Modules/Panels/Settings/Tabs/Audio/VolumesSubTab.qml b/Modules/Panels/Settings/Tabs/Audio/VolumesSubTab.qml index bc86441af..f4fa88824 100644 --- a/Modules/Panels/Settings/Tabs/Audio/VolumesSubTab.qml +++ b/Modules/Panels/Settings/Tabs/Audio/VolumesSubTab.qml @@ -61,6 +61,21 @@ ColumnLayout { } } + // Volume Feedback sound Toggle + ColumnLayout { + spacing: Style.marginS + Layout.fillWidth: true + + NToggle { + label: I18n.tr("panels.audio.volumes-volume-feedback-label") + description: I18n.tr("panels.audio.volumes-volume-feedback-description") + checked: Settings.data.audio.volumeFeedback + defaultValue: Settings.getDefaultValue("audio.volumeFeedback") + onToggled: checked => Settings.data.audio.volumeFeedback = checked + } + } + + // Mute Toggle ColumnLayout { spacing: Style.marginS diff --git a/Services/Media/AudioService.qml b/Services/Media/AudioService.qml index aa3949eed..2befa950d 100644 --- a/Services/Media/AudioService.qml +++ b/Services/Media/AudioService.qml @@ -4,10 +4,15 @@ import QtQuick import Quickshell import Quickshell.Services.Pipewire import qs.Commons +import qs.Services.System Singleton { id: root + // Rate limiting for volume feedback (minimum 100ms between sounds) + property var lastVolumeFeedbackTime: 0 + readonly property int minVolumeFeedbackInterval: 100 + // Devices readonly property PwNode sink: Pipewire.ready ? Pipewire.defaultAudioSink : null readonly property PwNode source: validatedSource @@ -230,6 +235,8 @@ Singleton { sink.audio.muted = false; sink.audio.volume = clampedVolume; + playVolumeFeedback(clampedVolume); + // Clear flag after a short delay to allow external changes to be detected Qt.callLater(() => { isSettingOutputVolume = false; @@ -305,6 +312,29 @@ Singleton { }); } + function playVolumeFeedback(currentVolume: real) { + if (!SoundService.multimediaAvailable) { + return; + } + + if (!Settings.data.audio.volumeFeedback) { + return; + } + + const now = Date.now(); + if (now - lastVolumeFeedbackTime < minVolumeFeedbackInterval) { + return; + } + lastVolumeFeedbackTime = now; + + const feedbackVolume = currentVolume; + SoundService.playSound("volume-change.wav", { + volume: feedbackVolume, + fallback: false, + repeat: false + }); + } + function setInputMuted(muted: bool) { if (!Pipewire.ready || !source?.audio) { Logger.w("AudioService", "No source available or Pipewire not ready");