Files
linux-dotfiles/quickshell/ui/NowPlaying.qml

145 lines
5.1 KiB
QML

import QtQuick
import QtQuick.Layouts
import "../constants"
import "../services"
Row {
id: root
property string artist: ""
property string title: ""
property string artUrl: ""
property int maxTextLength: 50
property int numBands: 32
// if the artist - title text is too long, truncate it
readonly property string displayText: {
const fullText = artist !== "" ? (artist + " - " + title) : title;
return fullText.length > maxTextLength ? fullText.substring(0, maxTextLength - 3) + "..." : fullText;
}
visible: title !== ""
spacing: 6
//TODO add service that reads the data from cava
// the fifo buffer is in /tmp/cava.fifo. Example data is:
// 5;3;3;3;2;1;1;3;6;18;42;16;6;6;1;1;2;6;2;3;6;6;5;11;11;12;13;66;4;4;24;2;2;24;4;4;66;13;12;11;11;5;6;6;3;2;3;2;1;1;6;6;16;42;19;5;2;1;1;2;3;4;3;7;
// where each number represents the amplitude of a frequency band.
// this can be used to create a simple visualizer
// also add cava to autostart
Image {
width: 18
height: 18
source: root.artUrl
fillMode: Image.PreserveAspectCrop
anchors.verticalCenter: parent.verticalCenter
}
Text {
id: titleText
text: root.displayText
color: Colors.md3.primary
font.family: Constants.fontFamily
font.pixelSize: Constants.fontSize
anchors.verticalCenter: parent.verticalCenter
// z: 1
}
Item {
id: titleArea
anchors.bottom: parent.bottom
width: titleText.implicitWidth
height: Math.max(titleText.implicitHeight, visualizer.height)
// Canvas {
// id: visualizer
// anchors.fill: parent
// property var cavaData: []
// property var smoothedData: []
// property real inputMax: 20
// property real attackAlpha: 1.0
// property real releaseAlpha: 0.9
// property real maxStepPerFrame: 50
// property bool needsInit: true
// Component.onCompleted: {
// for (let i = 0; i < root.numBands; i++) {
// cavaData[i] = 0;
// smoothedData[i] = 0;
// }
// needsInit = false;
// }
// function updateData(csvLine) {
// // Fast in-place parse: split once, parse directly into pre-allocated array
// let parts = csvLine.split(";");
// let idx = 0;
// for (let j = 0; j < parts.length && idx < root.numBands; j++) {
// let val = Number(parts[j]);
// if (!isNaN(val))
// cavaData[idx++] = Math.min(100, Math.max(0, val));
// }
// while (idx < root.numBands)
// cavaData[idx++] = 0;
// if (needsInit) {
// for (let i = 0; i < root.numBands; i++)
// smoothedData[i] = cavaData[i];
// needsInit = false;
// } else {
// // Two-speed smoothing in place with early exit if no significant change
// let changed = false;
// for (let i = 0; i < root.numBands; i++) {
// let prev = smoothedData[i];
// let next = cavaData[i];
// let alpha = next >= prev ? attackAlpha : releaseAlpha;
// let blended = prev + (next - prev) * alpha;
// let delta = blended - prev;
// if (Math.abs(delta) > 0.1)
// changed = true;
// if (delta > maxStepPerFrame)
// blended = prev + maxStepPerFrame;
// else if (delta < -maxStepPerFrame)
// blended = prev - maxStepPerFrame;
// smoothedData[i] = blended;
// }
// if (!changed)
// return;
// }
// visualizer.requestPaint();
// }
// onPaint: {
// var ctx = getContext("2d");
// ctx.clearRect(0, 0, width, height);
// if (smoothedData.length === 0) {
// return;
// }
// var barWidth = width / root.numBands;
// ctx.fillStyle = Colors.md3.on_primary;
// for (let i = 0; i < root.numBands; i++) {
// let normalized = (smoothedData[i] || 0) / inputMax;
// if (normalized > 1)
// normalized = 1;
// let barHeight = normalized * height;
// ctx.fillRect(Math.floor(i * barWidth), Math.floor(height - barHeight), Math.ceil(barWidth), Math.floor(barHeight));
// }
// }
// }
}
// CavaService {
// id: cavaService
// onCavaDataChanged: data => {
// visualizer.updateData(data);
// }
// }
}