From 156146fd9a050f8af92c7dcd63ebb1502c4d63a9 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 28 Aug 2025 17:48:02 +0200 Subject: [PATCH] Add audio IPC options AudioService: add a few functions to AudioService IPCManager: Add 4 Audio IPC calls README: Add information about new IPC calls --- Modules/IPC/IPCManager.qml | 18 ++++++++++ Modules/SettingsPanel/Tabs/AudioTab.qml | 48 +++++++++++++++++++++++++ README.md | 4 +++ Services/AudioService.qml | 42 ++++++++++++++++++++++ 4 files changed, 112 insertions(+) diff --git a/Modules/IPC/IPCManager.qml b/Modules/IPC/IPCManager.qml index 78ed64b..771aea4 100644 --- a/Modules/IPC/IPCManager.qml +++ b/Modules/IPC/IPCManager.qml @@ -99,6 +99,24 @@ Item { } } + IpcHandler { + target: "volume" + function increase() { + AudioService.increaseVolume() + } + function decrease() { + AudioService.decreaseVolume() + } + function muteOutput() { + AudioService.setMuted(!AudioService.muted) + } + function muteInput() { + if (AudioService.source?.ready && AudioService.source?.audio) { + AudioService.source.audio.muted = !AudioService.source.audio.muted + } + } + } + IpcHandler { target: "powerPanel" function toggle() { diff --git a/Modules/SettingsPanel/Tabs/AudioTab.qml b/Modules/SettingsPanel/Tabs/AudioTab.qml index 1a6f4b9..a81c2c8 100644 --- a/Modules/SettingsPanel/Tabs/AudioTab.qml +++ b/Modules/SettingsPanel/Tabs/AudioTab.qml @@ -81,6 +81,54 @@ ColumnLayout { } } + // Input Volume + ColumnLayout { + spacing: Style.marginS * scaling + Layout.fillWidth: true + Layout.topMargin: Style.marginM * scaling + + NLabel { + label: "Input Volume" + description: "Microphone input volume level." + } + + RowLayout { + NSlider { + Layout.fillWidth: true + from: 0 + to: 1.0 + value: AudioService.inputVolume + stepSize: 0.01 + onMoved: { + AudioService.setInputVolume(value) + } + } + + NText { + text: Math.floor(AudioService.inputVolume * 100) + "%" + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: Style.marginS * scaling + color: Color.mOnSurface + } + } + } + + // Input Mute Toggle + ColumnLayout { + spacing: Style.marginS * scaling + Layout.fillWidth: true + Layout.topMargin: Style.marginM * scaling + + NToggle { + label: "Mute Audio Input" + description: "Mute or unmute the default audio input (microphone)." + checked: AudioService.inputMuted + onToggled: checked => { + AudioService.setInputMuted(checked) + } + } + } + // Volume Step Size ColumnLayout { spacing: Style.marginS * scaling diff --git a/README.md b/README.md index 518cc87..12ffa43 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,10 @@ The following commands apply to the Nix flake and also the AUR package installat | Open Calculator | `noctalia-shell ipc call launcher calculator` | | Increase Brightness | `noctalia-shell ipc call brightness increase` | | Decrease Brightness | `noctalia-shell ipc call brightness decrease` | +| Increase Output Volume | `noctalia-shell ipc call volume increase` | +| Decrease Output Volume | `noctalia-shell ipc call volume decrease` | +| Toggle Mute Audio Output | `noctalia-shell ipc call volume muteOutput` | +| Toggle Mute Audio Input | `noctalia-shell ipc call volume muteInput` | | Toggle Power Panel | `noctalia-shell ipc call powerPanel toggle` | | Toggle Idle Inhibitor | `noctalia-shell ipc call idleInhibitor toggle` | | Toggle Settings Window | `noctalia-shell ipc call settings toggle` | diff --git a/Services/AudioService.qml b/Services/AudioService.qml index 0dd6fc9..c6ec05c 100644 --- a/Services/AudioService.qml +++ b/Services/AudioService.qml @@ -35,6 +35,13 @@ Singleton { readonly property alias muted: root._muted property bool _muted: !!sink?.audio?.muted + // Input volume [0..1] is readonly from outside + readonly property alias inputVolume: root._inputVolume + property real _inputVolume: source?.audio?.volume ?? 0 + + readonly property alias inputMuted: root._inputMuted + property bool _inputMuted: !!source?.audio?.muted + readonly property real stepVolume: Settings.data.audio.volumeStep / 100.0 PwObjectTracker { @@ -58,6 +65,23 @@ Singleton { } } + Connections { + target: source?.audio ? source?.audio : null + + function onVolumeChanged() { + var vol = (source?.audio.volume ?? 0) + if (isNaN(vol)) { + vol = 0 + } + root._inputVolume = vol + } + + function onMutedChanged() { + root._inputMuted = (source?.audio.muted ?? true) + Logger.log("AudioService", "OnInputMuteChanged:", root._inputMuted) + } + } + function increaseVolume() { setVolume(volume + stepVolume) } @@ -85,6 +109,24 @@ Singleton { } } + function setInputVolume(newVolume: real) { + if (source?.ready && source?.audio) { + // Clamp it accordingly + source.audio.muted = false + source.audio.volume = Math.max(0, Math.min(1, newVolume)) + } else { + Logger.warn("AudioService", "No source available") + } + } + + function setInputMuted(muted: bool) { + if (source?.ready && source?.audio) { + source.audio.muted = muted + } else { + Logger.warn("AudioService", "No source available") + } + } + function setAudioSink(newSink: PwNode): void { Pipewire.preferredDefaultAudioSink = newSink }