1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-15 09:52:50 -05:00

audio: un-dumbify audio service

This commit is contained in:
bbedward
2025-07-17 23:20:36 -04:00
parent 930572e1cb
commit 42927eeb97
5 changed files with 115 additions and 217 deletions

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import qs.Common
import qs.Services
@@ -10,14 +11,12 @@ Item {
id: audioTab
property int audioSubTab: 0 // 0: Output, 1: Input
readonly property real volumeLevel: AudioService.volumeLevel
readonly property real micLevel: AudioService.micLevel
readonly property bool volumeMuted: AudioService.sinkMuted
readonly property bool micMuted: AudioService.sourceMuted
readonly property string currentAudioSink: AudioService.currentAudioSink
readonly property string currentAudioSource: AudioService.currentAudioSource
readonly property var audioSinks: AudioService.audioSinks
readonly property var audioSources: AudioService.audioSources
readonly property real volumeLevel: (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.volume * 100) || 0
readonly property real micLevel: (AudioService.source && AudioService.source.audio && AudioService.source.audio.volume * 100) || 0
readonly property bool volumeMuted: (AudioService.sink && AudioService.sink.audio && AudioService.sink.audio.muted) || false
readonly property bool micMuted: (AudioService.source && AudioService.source.audio && AudioService.source.audio.muted) || false
readonly property string currentSinkDisplayName: AudioService.sink ? AudioService.displayName(AudioService.sink) : ""
readonly property string currentSourceDisplayName: AudioService.source ? AudioService.displayName(AudioService.source) : ""
Column {
anchors.fill: parent
@@ -115,7 +114,11 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: AudioService.toggleMute()
onClicked: {
if (AudioService.sink && AudioService.sink.audio)
AudioService.sink.audio.muted = !AudioService.sink.audio.muted;
}
}
}
@@ -191,7 +194,10 @@ Item {
isDragging = true;
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
if (AudioService.sink && AudioService.sink.audio) {
AudioService.sink.audio.muted = false;
AudioService.sink.audio.volume = newVolume / 100;
}
}
onReleased: {
isDragging = false;
@@ -200,13 +206,19 @@ Item {
if (pressed && isDragging) {
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
if (AudioService.sink && AudioService.sink.audio) {
AudioService.sink.audio.muted = false;
AudioService.sink.audio.volume = newVolume / 100;
}
}
}
onClicked: (mouse) => {
let ratio = Math.max(0, Math.min(1, mouse.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
if (AudioService.sink && AudioService.sink.audio) {
AudioService.sink.audio.muted = false;
AudioService.sink.audio.volume = newVolume / 100;
}
}
}
@@ -223,7 +235,10 @@ Item {
let globalPos = mapToItem(volumeSliderTrack, mouse.x, mouse.y);
let ratio = Math.max(0, Math.min(1, globalPos.x / volumeSliderTrack.width));
let newVolume = Math.round(ratio * 100);
AudioService.setVolume(newVolume);
if (AudioService.sink && AudioService.sink.audio) {
AudioService.sink.audio.muted = false;
AudioService.sink.audio.volume = newVolume / 100;
}
}
}
onReleased: {
@@ -265,7 +280,7 @@ Item {
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
border.width: 1
visible: audioTab.currentAudioSink !== ""
visible: AudioService.sink !== null
Row {
anchors.left: parent.left
@@ -281,7 +296,7 @@ Item {
}
Text {
text: "Current: " + (AudioService.currentSinkDisplayName || "None")
text: "Current: " + (audioTab.currentSinkDisplayName || "None")
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
@@ -293,14 +308,25 @@ Item {
// Real audio devices
Repeater {
model: audioTab.audioSinks
model: {
if (!Pipewire.ready || !Pipewire.nodes || !Pipewire.nodes.values) return []
let sinks = []
for (let i = 0; i < Pipewire.nodes.values.length; i++) {
let node = Pipewire.nodes.values[i]
if (!node || node.isStream) continue
if ((node.type & PwNodeType.AudioSink) === PwNodeType.AudioSink) {
sinks.push(node)
}
}
return sinks
}
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: deviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData.active ? Theme.primary : "transparent"
color: deviceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData === AudioService.sink ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData === AudioService.sink ? Theme.primary : "transparent"
border.width: 1
Row {
@@ -322,7 +348,7 @@ Item {
}
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: modelData.active ? Theme.primary : Theme.surfaceText
color: modelData === AudioService.sink ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
@@ -331,18 +357,18 @@ Item {
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.displayName
text: AudioService.displayName(modelData)
font.pixelSize: Theme.fontSizeMedium
color: modelData.active ? Theme.primary : Theme.surfaceText
font.weight: modelData.active ? Font.Medium : Font.Normal
color: modelData === AudioService.sink ? Theme.primary : Theme.surfaceText
font.weight: modelData === AudioService.sink ? Font.Medium : Font.Normal
}
Text {
text: {
if (modelData.subtitle && modelData.subtitle !== "")
return modelData.subtitle + (modelData.active ? " • Selected" : "");
if (AudioService.subtitle(modelData.name) && AudioService.subtitle(modelData.name) !== "")
return AudioService.subtitle(modelData.name) + (modelData === AudioService.sink ? " • Selected" : "");
else
return modelData.active ? "Selected" : "";
return modelData === AudioService.sink ? "Selected" : "";
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
@@ -360,7 +386,9 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
AudioService.setAudioSink(modelData.name);
if (modelData)
Pipewire.preferredDefaultAudioSink = modelData;
}
}
@@ -412,7 +440,11 @@ Item {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: AudioService.toggleMicMute()
onClicked: {
if (AudioService.source && AudioService.source.audio)
AudioService.source.audio.muted = !AudioService.source.audio.muted;
}
}
}
@@ -488,7 +520,10 @@ Item {
isDragging = true;
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
if (AudioService.source && AudioService.source.audio) {
AudioService.source.audio.muted = false;
AudioService.source.audio.volume = newMicLevel / 100;
}
}
onReleased: {
isDragging = false;
@@ -497,13 +532,19 @@ Item {
if (pressed && isDragging) {
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
if (AudioService.source && AudioService.source.audio) {
AudioService.source.audio.muted = false;
AudioService.source.audio.volume = newMicLevel / 100;
}
}
}
onClicked: (mouse) => {
let ratio = Math.max(0, Math.min(1, mouse.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
if (AudioService.source && AudioService.source.audio) {
AudioService.source.audio.muted = false;
AudioService.source.audio.volume = newMicLevel / 100;
}
}
}
@@ -520,7 +561,10 @@ Item {
let globalPos = mapToItem(micSliderTrack, mouse.x, mouse.y);
let ratio = Math.max(0, Math.min(1, globalPos.x / micSliderTrack.width));
let newMicLevel = Math.round(ratio * 100);
AudioService.setMicLevel(newMicLevel);
if (AudioService.source && AudioService.source.audio) {
AudioService.source.audio.muted = false;
AudioService.source.audio.volume = newMicLevel / 100;
}
}
}
onReleased: {
@@ -562,7 +606,7 @@ Item {
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
border.width: 1
visible: audioTab.currentAudioSource !== ""
visible: AudioService.source !== null
Row {
anchors.left: parent.left
@@ -578,7 +622,7 @@ Item {
}
Text {
text: "Current: " + (AudioService.currentSourceDisplayName || "None")
text: "Current: " + (audioTab.currentSourceDisplayName || "None")
font.pixelSize: Theme.fontSizeMedium
color: Theme.primary
font.weight: Font.Medium
@@ -590,14 +634,25 @@ Item {
// Real audio input devices
Repeater {
model: audioTab.audioSources
model: {
if (!Pipewire.ready || !Pipewire.nodes || !Pipewire.nodes.values) return []
let sources = []
for (let i = 0; i < Pipewire.nodes.values.length; i++) {
let node = Pipewire.nodes.values[i]
if (!node || node.isStream) continue
if ((node.type & PwNodeType.AudioSource) === PwNodeType.AudioSource && !node.name.includes(".monitor")) {
sources.push(node)
}
}
return sources
}
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: sourceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData.active ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData.active ? Theme.primary : "transparent"
color: sourceArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : (modelData === AudioService.source ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08))
border.color: modelData === AudioService.source ? Theme.primary : "transparent"
border.width: 1
Row {
@@ -617,7 +672,7 @@ Item {
}
font.family: Theme.iconFont
font.pixelSize: Theme.iconSize
color: modelData.active ? Theme.primary : Theme.surfaceText
color: modelData === AudioService.source ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
@@ -626,18 +681,18 @@ Item {
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.displayName
text: AudioService.displayName(modelData)
font.pixelSize: Theme.fontSizeMedium
color: modelData.active ? Theme.primary : Theme.surfaceText
font.weight: modelData.active ? Font.Medium : Font.Normal
color: modelData === AudioService.source ? Theme.primary : Theme.surfaceText
font.weight: modelData === AudioService.source ? Font.Medium : Font.Normal
}
Text {
text: {
if (modelData.subtitle && modelData.subtitle !== "")
return modelData.subtitle + (modelData.active ? " • Selected" : "");
if (AudioService.subtitle(modelData.name) && AudioService.subtitle(modelData.name) !== "")
return AudioService.subtitle(modelData.name) + (modelData === AudioService.source ? " • Selected" : "");
else
return modelData.active ? "Selected" : "";
return modelData === AudioService.source ? "Selected" : "";
}
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
@@ -655,7 +710,9 @@ Item {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
AudioService.setAudioSource(modelData.name);
if (modelData)
Pipewire.preferredDefaultAudioSource = modelData;
}
}