1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 05:55:37 -05:00

make cava a singleton

This commit is contained in:
bbedward
2025-08-02 12:13:05 -04:00
parent 2f97c39647
commit be301f829f
2 changed files with 68 additions and 53 deletions

View File

@@ -1,6 +1,4 @@
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Services.Mpris
import qs.Common
import qs.Services
@@ -8,69 +6,33 @@ import qs.Services
Item {
id: root
property var audioLevels: [0, 0, 0, 0, 0, 0]
readonly property MprisPlayer activePlayer: MprisController.activePlayer
readonly property bool hasActiveMedia: activePlayer !== null
property bool cavaAvailable: false
readonly property bool isPlaying: hasActiveMedia && activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing
width: 20
height: Theme.iconSize
Process {
id: cavaCheck
command: ["which", "cava"]
running: true
onExited: (exitCode) => {
root.cavaAvailable = exitCode === 0;
if (root.cavaAvailable)
cavaProcess.running = Qt.binding(() => {
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
});
else
fallbackTimer.running = Qt.binding(() => {
return root.hasActiveMedia && root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing;
});
}
}
Process {
id: cavaProcess
running: false
command: ["sh", "-c", `printf '[general]\nmode=normal\nframerate=25\nautosens=0\nsensitivity=30\nbars=6\nlower_cutoff_freq=50\nhigher_cutoff_freq=12000\n[output]\nmethod=raw\nraw_target=/dev/stdout\ndata_format=ascii\nchannels=mono\nmono_option=average\n[smoothing]\nnoise_reduction=35\nintegral=90\ngravity=95\nignore=2\nmonstercat=1.5' | cava -p /dev/stdin`]
onRunningChanged: {
if (!running)
root.audioLevels = [0, 0, 0, 0, 0, 0];
}
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
if (data.trim()) {
let points = data.split(";").map((p) => {
return parseFloat(p.trim());
}).filter((p) => {
return !isNaN(p);
});
if (points.length >= 6)
root.audioLevels = [points[0], points[1], points[2], points[3], points[4], points[5]];
}
}
}
Ref {
service: CavaService
}
Timer {
id: fallbackTimer
running: false
running: !CavaService.cavaAvailable && isPlaying
interval: 256
repeat: true
onTriggered: {
root.audioLevels = [Math.random() * 40 + 10, Math.random() * 60 + 20, Math.random() * 50 + 15, Math.random() * 35 + 20, Math.random() * 45 + 15, Math.random() * 55 + 25];
// Generate fake audio levels when cava is unavailable
CavaService.values = [
Math.random() * 40 + 10,
Math.random() * 60 + 20,
Math.random() * 50 + 15,
Math.random() * 35 + 20,
Math.random() * 45 + 15,
Math.random() * 55 + 25
];
}
}
@@ -84,8 +46,8 @@ Item {
Rectangle {
width: 2
height: {
if (root.activePlayer && root.activePlayer.playbackState === MprisPlaybackState.Playing && root.audioLevels.length > index) {
const rawLevel = root.audioLevels[index] || 0;
if (root.isPlaying && CavaService.values.length > index) {
const rawLevel = CavaService.values[index] || 0;
const scaledLevel = Math.sqrt(Math.min(Math.max(rawLevel, 0), 100) / 100) * 100;
const maxHeight = Theme.iconSize - 2;
const minHeight = 3;

53
Services/CavaService.qml Normal file
View File

@@ -0,0 +1,53 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
property list<int> values: Array(6)
property int refCount: 0
property bool cavaAvailable: false
Process {
id: cavaCheck
command: ["which", "cava"]
running: true
onExited: (exitCode) => {
root.cavaAvailable = exitCode === 0;
}
}
Process {
id: cavaProcess
running: root.cavaAvailable && root.refCount > 0
command: ["sh", "-c", `printf '[general]\\nmode=normal\\nframerate=25\\nautosens=0\\nsensitivity=30\\nbars=6\\nlower_cutoff_freq=50\\nhigher_cutoff_freq=12000\\n[output]\\nmethod=raw\\nraw_target=/dev/stdout\\ndata_format=ascii\\nchannels=mono\\nmono_option=average\\n[smoothing]\\nnoise_reduction=35\\nintegral=90\\ngravity=95\\nignore=2\\nmonstercat=1.5' | cava -p /dev/stdin`]
onRunningChanged: {
if (!running) {
root.values = Array(6).fill(0);
}
}
stdout: SplitParser {
splitMarker: "\n"
onRead: (data) => {
if (root.refCount > 0 && data.trim()) {
let points = data.split(";").map((p) => {
return parseInt(p.trim(), 10);
}).filter((p) => {
return !isNaN(p);
});
if (points.length >= 6) {
root.values = points.slice(0, 6);
}
}
}
}
}
}