import QtQuick
import Quickshell.Io
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginComponent {
id: root
// ЗАМЕНИ micSource на свой Audio/Source из pw-dump.
property string micSource: "alsa_input.pci-0000_00_1f.3.analog-stereo"
// Можно оставить auto. Если не реагирует, поставь node.name твоего Audio/Sink + ".monitor".
property string speakerSource: "auto"
property int barsCount: 18
horizontalBarPill: Component {
Row {
spacing: Theme.spacingM
MiniWave {
label: "MIC"
sourceName: root.micSource
barsCount: root.barsCount
}
MiniWave {
label: "OUT"
sourceName: root.speakerSource
barsCount: root.barsCount
}
}
}
verticalBarPill: Component {
Column {
spacing: Theme.spacingS
MiniWave {
label: "M"
sourceName: root.micSource
barsCount: 10
}
MiniWave {
label: "O"
sourceName: root.speakerSource
barsCount: 10
}
}
}
component MiniWave: Row {
id: wave
property string label: ""
property string sourceName: "auto"
property int barsCount: 18
property var values: Array(barsCount).fill(0)
spacing: 3
height: 18
function cavaCommand() {
return `cat <<'CAVACONF' | cava -p /dev/stdin
[general]
framerate=30
bars=${wave.barsCount}
autosens=1
sensitivity=100
lower_cutoff_freq=50
higher_cutoff_freq=12000
[input]
method=pipewire
source=${wave.sourceName}
active=1
virtual=1
[output]
method=raw
raw_target=/dev/stdout
data_format=ascii
ascii_max_range=100
channels=mono
mono_option=average
[smoothing]
noise_reduction=55
integral=80
gravity=90
monstercat=1
CAVACONF`
}
StyledText {
text: wave.label
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
Row {
spacing: 1
height: 16
anchors.verticalCenter: parent.verticalCenter
Repeater {
model: wave.barsCount
Item {
width: 3
height: 16
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: Math.max(2, parent.height * (wave.values[index] || 0))
radius: 2
color: Theme.primary
}
}
}
}
Process {
id: cavaProcess
running: true
command: ["sh", "-c", wave.cavaCommand()]
stdout: SplitParser {
splitMarker: "\n"
onRead: data => {
const parts = data.trim().split(";")
if (parts.length < wave.barsCount)
return
const next = []
for (let i = 0; i < wave.barsCount; i++) {
const n = parseInt(parts[i], 10)
next.push(Number.isFinite(n) ? Math.max(0, Math.min(1, n / 100.0)) : 0)
}
wave.values = next
}
}
}
}
}