Загрузка данных


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
                }
            }
        }
    }
}