-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCavaProcess.qml
More file actions
123 lines (105 loc) · 3.16 KB
/
CavaProcess.qml
File metadata and controls
123 lines (105 loc) · 3.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import QtQuick
import Quickshell
import Quickshell.Io
QtObject {
id: root
property list<real> values: Array(32).fill(0.0)
property int refCount: 0
property bool cavaAvailable: false
property bool isIdle: true
property int lowerCutoffFreq: 50
property int higherCutoffFreq: 12000
readonly property int effectiveLowCutoffFreq: Math.max(1, Math.min(lowerCutoffFreq, higherCutoffFreq - 1))
readonly property int effectiveHighCutoffFreq: Math.min(20000, Math.max(higherCutoffFreq, effectiveLowCutoffFreq + 1))
// Idle detection: consider idle when all values near zero for idleTimeout ms
property int idleTimeout: 2000
property real _idleThreshold: 0.01
property var _idleTimer: Timer {
interval: root.idleTimeout
onTriggered: root.isIdle = true
}
property var _restartTimer: Timer {
interval: 30
repeat: false
onTriggered: {
if (root.cavaAvailable && root.refCount > 0)
root._cavaProcess.running = true;
}
}
property var _cavaCheck: Process {
command: ["which", "cava"]
running: false
onExited: exitCode => {
root.cavaAvailable = exitCode === 0;
}
}
property var _cavaProcess: Process {
running: root.cavaAvailable && root.refCount > 0
command: ["sh", "-c", `cat <<'CAVACONF' | cava -p /dev/stdin
[general]
framerate=60
bars=32
autosens=0
sensitivity=80
# previous values: lower_cutoff_freq=50, higher_cutoff_freq=12000
lower_cutoff_freq=${root.effectiveLowCutoffFreq}
higher_cutoff_freq=${root.effectiveHighCutoffFreq}
[output]
method=raw
raw_target=/dev/stdout
data_format=ascii
channels=mono
mono_option=average
[smoothing]
noise_reduction=35
integral=90
gravity=95
ignore=2
monstercat=1.5
CAVACONF`]
onRunningChanged: {
if (!running) {
root.values = Array(32).fill(0.0);
root.isIdle = true;
}
}
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
if (root.refCount <= 0 || data.length === 0)
return;
const parts = data.split(";");
if (parts.length < 32)
return;
const newValues = [];
let anyActive = false;
for (let i = 0; i < 32; i++) {
const v = parseInt(parts[i], 10) / 100.0;
newValues.push(v);
if (v > root._idleThreshold)
anyActive = true;
}
root.values = newValues;
if (anyActive) {
root.isIdle = false;
root._idleTimer.restart();
}
}
}
}
Component.onCompleted: {
_cavaCheck.running = true;
}
onLowerCutoffFreqChanged: {
if (_cavaProcess.running) {
_cavaProcess.running = false;
_restartTimer.restart();
}
}
onHigherCutoffFreqChanged: {
if (_cavaProcess.running) {
_cavaProcess.running = false;
_restartTimer.restart();
}
}
}