feat: add cava audio visualizer component

This commit is contained in:
tux
2026-08-23 12:06:05 +05:30
parent a1533e73c7
commit ae6628f549
3 changed files with 92 additions and 1 deletions

53
Bar.qml
View File

@@ -217,6 +217,57 @@ PanelWindow {
color: Appearance.colors.inActive color: Appearance.colors.inActive
} }
} }
Button {
id: cava
padding: Appearance.padding
readonly property int barCount: 14
readonly property real maxValue: 100
readonly property bool shouldVisualize: barCount > 0 && Cava.values.some(v => v >= 0.001)
implicitWidth: 96
implicitHeight: 32
background: Rectangle {
anchors.fill: parent
radius: Appearance.radius
color: Appearance.colors.inActive
}
visible: shouldVisualize
Row {
id: barRow
anchors.fill: parent
anchors.margins: 8
spacing: 3
Repeater {
model: cava.barCount
Rectangle {
required property int index
readonly property real value: Cava.values[index] ?? 0
width: (barRow.width - barRow.spacing * (cava.barCount - 1)) / cava.barCount
height: Math.max(2, (value / cava.maxValue) * barRow.height)
anchors.bottom: parent.bottom
radius: width / 2
color: Appearance.colors.accent
antialiasing: true
Behavior on height {
NumberAnimation {
duration: Appearance.duration
}
}
}
}
}
}
} }
} }
@@ -369,7 +420,7 @@ PanelWindow {
y: topBar.implicitHeight y: topBar.implicitHeight
Repeater { Repeater {
model: [0, 1, 2, 3] model: 4
Corner { Corner {
required property int modelData required property int modelData

26
Cava.qml Normal file
View File

@@ -0,0 +1,26 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: root
readonly property string configPath: {
const url = Quickshell.shellPath("assets/cava.conf").toString();
return url.startsWith("file://") ? decodeURIComponent(url.substring(7)) : url;
}
property var values: []
Process {
running: true
command: ["cava", "-p", root.configPath]
stdout: SplitParser {
onRead: line => {
root.values = line.split(";").map(Number).filter(v => !Number.isNaN(v));
}
}
}
}

14
assets/cava.conf Normal file
View File

@@ -0,0 +1,14 @@
[general]
bars = 12
framerate = 60
[input]
method = pulse
[output]
method = raw
raw_target = /dev/stdout
data_format = ascii
ascii_max_range = 100
bar_delimiter = 59
frame_delimiter = 10