import QtQuick import Quickshell import Quickshell.Services.Pipewire import qs.config import qs.widgets SettingRow { id: root property var node: null label: "Balance" detail: "Adjust the left and right channels" controlWidth: 270 visible: root.available divider: false PwObjectTracker { objects: root.node ? [root.node] : [] } function channelIndex(channel): int { if (!root.node?.audio) return -1; const channels = root.node.audio.channels; for (let index = 0; index < channels.length; index++) { if (channels[index] === channel) return index; } return -1; } readonly property int leftIndex: root.channelIndex(PwAudioChannel.FrontLeft) readonly property int rightIndex: root.channelIndex(PwAudioChannel.FrontRight) readonly property bool available: !!root.node?.audio && root.leftIndex >= 0 && root.rightIndex >= 0 && root.node.audio.volumes.length > Math.max(root.leftIndex, root.rightIndex) readonly property real position: { if (!root.available) return 0.5; const left = root.node.audio.volumes[root.leftIndex]; const right = root.node.audio.volumes[root.rightIndex]; const level = Math.max(left, right); if (level <= 0.001) return 0.5; return right >= left ? 0.5 + (1 - left / level) * 0.5 : 0.5 - (1 - right / level) * 0.5; } function setBalance(value: real): void { if (!root.available) return; const next = Array.from(root.node.audio.volumes); const level = Math.max(next[root.leftIndex], next[root.rightIndex], 0.001); if (value < 0.5) { next[root.leftIndex] = level; next[root.rightIndex] = level * value * 2; } else { next[root.leftIndex] = level * (1 - value) * 2; next[root.rightIndex] = level; } root.node.audio.volumes = next; } ValueSlider { anchors.fill: parent value: root.position icon: "audio-speakers-symbolic" onMoved: value => root.setBalance(value) } }