170 lines
5.8 KiB
QML
170 lines
5.8 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
|
|
import "../constants"
|
|
import "../services"
|
|
|
|
Row {
|
|
id: root
|
|
|
|
property string artist: ""
|
|
property string title: ""
|
|
property string artUrl: ""
|
|
property bool isPlaying: false
|
|
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;
|
|
}
|
|
|
|
property string dancingLeft: "└(ಠ_ಠ )┐"
|
|
property string dancingRight: "┌( ಠ_ಠ)┘"
|
|
property string notDancing: "(ಠ ∩ ಠ)"
|
|
|
|
visible: title !== ""
|
|
spacing: 6
|
|
|
|
Text {
|
|
id: littleFella
|
|
text: root.dancingLeft
|
|
color: Colors.md3.primary
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
//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
|
|
}
|
|
|
|
Timer {
|
|
interval: 400
|
|
running: true
|
|
repeat: true
|
|
onTriggered: {
|
|
if (root.isPlaying) {
|
|
littleFella.text = littleFella.text === root.dancingLeft ? root.dancingRight : root.dancingLeft;
|
|
} else {
|
|
littleFella.text = root.notDancing;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
// }
|
|
// }
|
|
}
|