From ae6628f54997911abfed40d3d1a2c0616d9576dd Mon Sep 17 00:00:00 2001 From: tux Date: Sun, 23 Aug 2026 12:06:05 +0530 Subject: [PATCH] feat: add cava audio visualizer component --- Bar.qml | 53 +++++++++++++++++++++++++++++++++++++++++++++++- Cava.qml | 26 ++++++++++++++++++++++++ assets/cava.conf | 14 +++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 Cava.qml create mode 100644 assets/cava.conf diff --git a/Bar.qml b/Bar.qml index ff3cd4b..762ca55 100644 --- a/Bar.qml +++ b/Bar.qml @@ -217,6 +217,57 @@ PanelWindow { 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 Repeater { - model: [0, 1, 2, 3] + model: 4 Corner { required property int modelData diff --git a/Cava.qml b/Cava.qml new file mode 100644 index 0000000..e71ff9b --- /dev/null +++ b/Cava.qml @@ -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)); + } + } + } +} diff --git a/assets/cava.conf b/assets/cava.conf new file mode 100644 index 0000000..fb75362 --- /dev/null +++ b/assets/cava.conf @@ -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