diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 404e78979..d1a811f7f 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -366,7 +366,7 @@ Singleton { property JsonObject audio: JsonObject { property int volumeStep: 5 property bool volumeOverdrive: false - property int cavaFrameRate: 60 + property int cavaFrameRate: 30 property string visualizerType: "linear" property list mprisBlacklist: [] property string preferredPlayer: "" diff --git a/Services/Media/CavaService.qml b/Services/Media/CavaService.qml index 622181c8a..2619229b0 100644 --- a/Services/Media/CavaService.qml +++ b/Services/Media/CavaService.qml @@ -20,6 +20,11 @@ Singleton { property var values: Array(barsCount).fill(0) property int barsCount: 48 + + // Idle detection to reduce GPU usage when there's no audio + property bool isIdle: true + property int idleFrameCount: 0 + readonly property int idleThreshold: 30 // Frames of silence before considered idle (0.5s at 60fps) property var config: ({ "general": { "bars": barsCount, @@ -74,7 +79,35 @@ Singleton { } stdout: SplitParser { onRead: data => { - root.values = data.slice(0, -1).split(";").map(v => parseInt(v, 10) / 100) + const newValues = data.slice(0, -1).split(";").map(v => parseInt(v, 10) / 100) + + // Check if all values are effectively zero (< 0.01) + const allZero = newValues.every(v => v < 0.01) + + if (allZero) { + root.idleFrameCount++ + if (root.idleFrameCount >= root.idleThreshold) { + // We're idle - stop updating values to save GPU + if (!root.isIdle) { + root.isIdle = true + // Set all values to 0 one final time + root.values = Array(root.barsCount).fill(0) + Logger.d("Cava", "Idle detected - stopped rendering") + } + // Don't update values while idle + return + } + } else { + // Audio detected - resume updates + root.idleFrameCount = 0 + if (root.isIdle) { + root.isIdle = false + Logger.d("Cava", "Audio detected - resumed rendering") + } + } + + // Update values (only when not idle) + root.values = newValues } } stderr: StdioCollector { diff --git a/Widgets/AudioSpectrum/NLinearSpectrum.qml b/Widgets/AudioSpectrum/NLinearSpectrum.qml index 97231894e..0224fd9e8 100644 --- a/Widgets/AudioSpectrum/NLinearSpectrum.qml +++ b/Widgets/AudioSpectrum/NLinearSpectrum.qml @@ -35,10 +35,14 @@ Item { border.width: root.strokeWidth antialiasing: true + // Only update when value actually changes - reduces GPU load width: vertical ? root.width * amp : root.barSlotSize * 0.5 height: vertical ? root.barSlotSize * 0.5 : root.height * amp x: vertical ? root.width - width : index * root.barSlotSize + (root.barSlotSize * 0.25) y: vertical ? index * root.barSlotSize + (root.barSlotSize * 0.25) : root.height - height + + // Disable updates when invisible to save GPU + visible: root.visible } } } diff --git a/Widgets/AudioSpectrum/NMirroredSpectrum.qml b/Widgets/AudioSpectrum/NMirroredSpectrum.qml index ae2f87db4..2d6b05ee7 100644 --- a/Widgets/AudioSpectrum/NMirroredSpectrum.qml +++ b/Widgets/AudioSpectrum/NMirroredSpectrum.qml @@ -44,6 +44,9 @@ Item { height: vertical ? root.barSlotSize * 0.8 : barSize x: vertical ? root.centerX - (barSize / 2) : index * root.barSlotSize + (root.barSlotSize * 0.25) y: vertical ? index * root.barSlotSize + (root.barSlotSize * 0.25) : root.centerY - (barSize / 2) + + // Disable updates when invisible to save GPU + visible: root.visible } } } diff --git a/Widgets/AudioSpectrum/NWaveSpectrum.qml b/Widgets/AudioSpectrum/NWaveSpectrum.qml index f45339082..9211fdd1b 100644 --- a/Widgets/AudioSpectrum/NWaveSpectrum.qml +++ b/Widgets/AudioSpectrum/NWaveSpectrum.qml @@ -13,15 +13,35 @@ Item { property bool showMinimumSignal: false property real minimumSignalValue: 0.05 // Default to 5% of height - // Redraw when necessary - onWidthChanged: canvas.requestPaint() - onHeightChanged: canvas.requestPaint() - onValuesChanged: canvas.requestPaint() - onFillColorChanged: canvas.requestPaint() - onStrokeColorChanged: canvas.requestPaint() - onShowMinimumSignalChanged: canvas.requestPaint() - onMinimumSignalValueChanged: canvas.requestPaint() - onVerticalChanged: canvas.requestPaint() + // Rendering active state - only redraw when visible and values are changing + property bool renderingActive: visible && values && values.length > 0 + + // Redraw when necessary - only if rendering is active + onWidthChanged: if (renderingActive) + canvas.requestPaint() + onHeightChanged: if (renderingActive) + canvas.requestPaint() + onValuesChanged: if (renderingActive) + canvas.requestPaint() + onFillColorChanged: if (renderingActive) + canvas.requestPaint() + onStrokeColorChanged: if (renderingActive) + canvas.requestPaint() + onShowMinimumSignalChanged: if (renderingActive) + canvas.requestPaint() + onMinimumSignalValueChanged: if (renderingActive) + canvas.requestPaint() + onVerticalChanged: if (renderingActive) + canvas.requestPaint() + + // Clear canvas when not rendering + onRenderingActiveChanged: { + if (!renderingActive) { + var ctx = canvas.getContext("2d") + ctx.reset() + canvas.requestPaint() + } + } Canvas { id: canvas